text:
/********************************************************************************
 *   File Name:
 *       bp_spi.cpp
 *
 *   Description:
 *       Implements the SPI interface to the Bus Pirate hardware
 *
 *   2019 | Brandon Braun | brandonbraun653@gmail.com
 ********************************************************************************/

/* Driver Includes */
#include "bp_spi.hpp"

/* C++ Includes */
#include <array>
#include <map>
#include <string>
#include <algorithm>

/* Boost Includes */
#include <boost/bimap.hpp>
#include <boost/assign.hpp>

/* Library Includes */
#include <spdlog/spdlog.h>

using namespace Chimera;
using namespace Chimera::GPIO;
using namespace Chimera::SPI;

namespace HWInterface
{
  namespace BusPirate
  {
    static constexpr uint8_t CMD_ENTER_RAW_SPI = 0x01;

    /*------------------------------------------------
    Chip Select Options
    ------------------------------------------------*/
    static constexpr uint8_t CMD_SET_CS = 0x02;
    static constexpr uint8_t MSK_SET_CS = 0x01;
    static constexpr uint8_t SET_CS     = ( 1u << 0 );

    /*------------------------------------------------
    Board Configuration Options
    ------------------------------------------------*/
    static constexpr uint8_t CMD_CFG_PERIPH     = 0x40;
    static constexpr uint8_t MSK_CFG_PERIPH     = 0x0F;
    static constexpr uint8_t CFG_PERIPH_POWER   = ( 1u << 3 );
    static constexpr uint8_t CFG_PERIPH_PULLUP  = ( 1u << 2 );
    static constexpr uint8_t CFG_PERIPH_AUX_PIN = ( 1u << 1 );
    static constexpr uint8_t CFG_PERIPH_CS_PIN  = ( 1u << 0 );

    /*------------------------------------------------
    SPI Configuration Options
    ------------------------------------------------*/
    static constexpr uint8_t CMD_CFG_SPI = 0x80;
    static constexpr uint8_t MSK_CFG_SPI = 0x0F;

    static constexpr uint8_t CFG_SPI_PIN_3V3 = ( 1u << 3 );
    static constexpr uint8_t CFG_SPI_PIN_HIZ = ~CFG_SPI_PIN_3V3;

    static constexpr uint8_t CFG_SPI_CPOL_1 = ( 1u << 2 );
    static constexpr uint8_t CFG_SPI_CPOL_0 = ~CFG_SPI_CPOL_1;

    static constexpr uint8_t CFG_SPI_CPHA_ACT_TO_IDLE = ( 1u << 1 );
    static constexpr uint8_t CFG_SPI_CPHA_IDLE_TO_ACT = ~CFG_SPI_CPHA_ACT_TO_IDLE;

    /*------------------------------------------------
    SPI Speed Configuration & Lookup Options
    ------------------------------------------------*/
    static constexpr uint8_t CMD_CFG_SPEED = 0x60;
    static constexpr uint8_t MSK_CFG_SPEED = 0x07;

    static constexpr std::array<uint32_t, 9> sortedSPISpeeds = { SPEED_30kHz, SPEED_125kHz, SPEED_250kHz,
                                                                 SPEED_1MHz,  SPEED_2MHz,   SPEED_2_6MHz,
                                                                 SPEED_4MHz,  SPEED_8MHz,   SPEED_NOT_SUPPORTED };

    // clang-format off
    using SpeedBimap_t = boost::bimap<uint32_t, uint8_t>;
    static const SpeedBimap_t mapSpeedtoBits = boost::assign::list_of<SpeedBimap_t::relation>
      ( SPEED_30kHz,  0x00 )
      ( SPEED_125kHz, 0x01 )
      ( SPEED_250kHz, 0x02 )
      ( SPEED_1MHz,   0x03 )
      ( SPEED_2MHz,   0x04 )
      ( SPEED_2_6MHz, 0x05 )
      ( SPEED_4MHz,   0x06 )
      ( SPEED_8MHz,   0x07 )
      ( SPEED_NOT_SUPPORTED, 0x00 );
    // clang-format on

    /*------------------------------------------------
    SPI Write/Read Commands
    ------------------------------------------------*/
    static constexpr uint8_t CMD_BULK_SPI_TXFR       = 0x10;
    static constexpr uint8_t MSK_BULK_SPI_TXFR_BYTES = 0x0F;
    static constexpr uint8_t CMD_TX_THEN_RX_MAN_CS   = 0x05;
    static constexpr uint8_t CMD_TX_THEN_RX_AUTO_CS  = 0x04;

    BinarySPI::BinarySPI( Device &device ) : busPirate( device )
    {
      busPirate.open();
      systemInitialized = false;
      csMode            = Chimera::SPI::ChipSelectMode::AUTO_AFTER_TRANSFER;

      /*------------------------------------------------
      Initialize the virtual registers
      ------------------------------------------------*/
      reg_CS        = 0;
      reg_SPICfg    = 0;
      reg_PeriphCfg = 0;
      reg_SPISpeed  = 0;
    }

    Chimera::Status_t BinarySPI::init( const Chimera::SPI::Setup &setupStruct ) noexcept
    {
      Chimera::Status_t result = SPI::Status::NOT_INITIALIZED;

      if ( busPirate.bbInit() && busPirate.bbEnterSPI() )
      {
        /*------------------------------------------------
        Power on the ouptut and wait a bit to let whatever is connected stabilize
        ------------------------------------------------*/
        result = cfgPowerSupplies( true );
        Chimera::delayMilliseconds( 100 );

        /*------------------------------------------------
        Enable all the pins as outputs
        ------------------------------------------------*/
        result |= cfgSPIPinOut( true );

        /*------------------------------------------------
        Disable the pullups to false, otherwise signals will propagate from MOSI
        into MISO when there is no load, falsely indicating a response. This software
        was developed on v3.6 hardware and the schematics seem to indicate that
        enabling the pullups (IC3) requires an external voltage source (VEXTERN).
        Without this, it will tie together the SPI signals via 10k resistors and
        therefore generate a signal on MISO with nothing connected.
        ------------------------------------------------*/
        result |= cfgPullups( false );

        /*------------------------------------------------
        The chip select line follows whatever configuration is set by cfgSPIPinOut.
        ------------------------------------------------*/
        result |= cfgChipSelect( true );
        result |= setChipSelect( State::HI );

        switch ( setupStruct.clockMode )
        {
          case ClockMode::MODE0:
            result |= cfgSPIClkIdle( false );
            result |= cfgSPIClkEdge( true );
            break;

          case ClockMode::MODE1:
            result |= cfgSPIClkIdle( false );
            result |= cfgSPIClkEdge( false );
            break;

          case ClockMode::MODE2:
            result |= cfgSPIClkIdle( true );
            result |= cfgSPIClkEdge( true );
            break;

          case ClockMode::MODE3:
            result |= cfgSPIClkIdle( true );
            result |= cfgSPIClkEdge( false );
            break;

          default:
            break;
        }

        /*------------------------------------------------
        Attempts to get the closest supported match to what the user inputted.
        ------------------------------------------------*/
        auto tmp = setClockFrequency( setupStruct.clockFrequency, 0 );
        if ( ( tmp == SPI::Status::CLOCK_SET_EQ ) || ( tmp == SPI::Status::CLOCK_SET_LT )
             || ( tmp == SPI::Status::CLOCK_SET_GT ) )
        {
          result |= SPI::Status::OK;
        }
        else
        {
          result |= SPI::Status::FAIL;
          spdlog::error( "Failed SPI initialization" );
        }

        if ( result == SPI::Status::OK )
        {
          systemInitialized = true;
        }
      }

      return result;
    }

    Chimera::Status_t BinarySPI::deInit() noexcept
    {
      Chimera::Status_t result = SPI::Status::FAIL;

      busPirate.close();
      result            = SPI::Status::OK;
      systemInitialized = false;

      return result;
    }

    Chimera::Status_t BinarySPI::setChipSelect( const Chimera::GPIO::State &value ) noexcept
    {
      Chimera::Status_t result = SPI::Status::FAILED_CHIP_SELECT_WRITE;

      uint8_t bitVals = reg_CS;
      if ( static_cast<bool>( value ) )
      {
        bitVals |= SET_CS;
      }
      else
      {
        bitVals &= ~SET_CS;
      }

      uint8_t command = CMD_SET_CS | ( bitVals & MSK_SET_CS );

      /*------------------------------------------------
      Send and verify
      ------------------------------------------------*/
      std::vector<uint8_t> cmd = { command };
      auto rx                  = busPirate.sendResponsiveCommand( cmd, static_cast<uint32_t>( cmd.size() ) );

      if ( rx.size() && rx[ 0 ] == BitBangCommands::success )
      {
        reg_CS = bitVals;
        result = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::setChipSelectControlMode( const Chimera::SPI::ChipSelectMode &mode ) noexcept
    {
      csMode = mode;
      return SPI::Status::OK;
    }

    Chimera::Status_t BinarySPI::writeBytes( const uint8_t *const txBuffer, const size_t length,
                                             const uint32_t timeoutMS ) noexcept
    {
      if ( !txBuffer || !length )
      {
        return SPI::Status::INVAL_FUNC_PARAM;
      }

      TXRXPacket_t transfer;

      transfer.command       = CMD_BULK_SPI_TXFR;
      transfer.numWriteBytes = static_cast<uint16_t>( length );
      transfer.numReadBytes  = static_cast<uint16_t>( length );
      transfer.writeData     = std::vector<uint8_t>( txBuffer, txBuffer + length );

      return bulkTransfer( transfer );
    }

    Chimera::Status_t BinarySPI::readBytes( uint8_t *const rxBuffer, const size_t length, const uint32_t timeoutMS ) noexcept
    {
      if ( !rxBuffer || !length )
      {
        return SPI::Status::INVAL_FUNC_PARAM;
      }

      Chimera::Status_t result = SPI::Status::FAIL;
      TXRXPacket_t transfer;

      transfer.command       = CMD_BULK_SPI_TXFR;
      transfer.numWriteBytes = static_cast<uint16_t>( length );
      transfer.numReadBytes  = static_cast<uint16_t>( length );
      transfer.writeData     = std::vector<uint8_t>( length );

      transfer.writeData.assign( length, 0 );

      if ( bulkTransfer( transfer ) == SPI::Status::OK )
      {
        auto rxLen = std::min( length, transfer.readData.size() );
        memcpy( rxBuffer, transfer.readData.data(), rxLen );
        result = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::readWriteBytes( const uint8_t *const txBuffer, uint8_t *const rxBuffer, const size_t length,
                                                 const uint32_t timeoutMS ) noexcept
    {
      if ( !txBuffer || !rxBuffer || !length )
      {
        return SPI::Status::INVAL_FUNC_PARAM;
      }

      Chimera::Status_t result = SPI::Status::FAIL;
      TXRXPacket_t transfer;

      transfer.command       = CMD_BULK_SPI_TXFR;
      transfer.numWriteBytes = static_cast<uint16_t>( length );
      transfer.numReadBytes  = static_cast<uint16_t>( length );
      transfer.writeData     = std::vector<uint8_t>( txBuffer, txBuffer + length );

      if ( bulkTransfer( transfer ) == SPI::Status::OK )
      {
        auto rxLen = std::min( length, transfer.readData.size() );
        memcpy( rxBuffer, transfer.readData.data(), rxLen );
        result = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::setPeripheralMode( const Chimera::SPI::SubPeripheral periph,
                                                    const Chimera::SPI::SubPeripheralMode mode ) noexcept
    {
      return SPI::Status::NOT_SUPPORTED;
    }

    Chimera::Status_t BinarySPI::setClockFrequency( const uint32_t freq, const uint32_t tolerance ) noexcept
    {
      Chimera::Status_t result = SPI::Status::FAIL;

      /*------------------------------------------------
      Find the lowest error clock
      ------------------------------------------------*/
      std::array<float, sortedSPISpeeds.size()> absError;
      absError.fill( std::numeric_limits<float>::max() );

      for ( size_t x = 0; x < sortedSPISpeeds.size(); x++ )
      {
        absError[ x ] = fabs( static_cast<float>( sortedSPISpeeds[ x ] ) - static_cast<float>( freq ) );
      }

      auto pos = std::distance( absError.begin(), std::min_element( absError.begin(), absError.end() ));
      auto iter = sortedSPISpeeds.begin() + pos;

      /* Reset to the lowest speed if the requested value is not found */
      if ( iter == sortedSPISpeeds.end() )
      {
        iter = sortedSPISpeeds.begin();
      }

      /*------------------------------------------------
      Build the command
      ------------------------------------------------*/
      auto bitVals = mapSpeedtoBits.left.find( *iter )->second;
      uint8_t command = CMD_CFG_SPEED | ( bitVals & MSK_CFG_SPEED );

      /*------------------------------------------------
      Send and verify
      ------------------------------------------------*/
      std::vector<uint8_t> cmd = { command };
      auto rx                  = busPirate.sendResponsiveCommand( cmd, static_cast<uint32_t>( cmd.size() ) );

      if ( rx.size() && rx[ 0 ] == BitBangCommands::success )
      {
        reg_SPISpeed = bitVals;

        uint32_t actualClock = mapSpeedtoBits.right.find( reg_SPISpeed )->second;

        if (actualClock == freq )
        {
          result = SPI::Status::CLOCK_SET_EQ;
        }
        else if ( actualClock < freq )
        {
          result = SPI::Status::CLOCK_SET_LT;
        }
        else
        {
          result = SPI::Status::CLOCK_SET_GT;
        }
      }

      return result;
    }

    Chimera::Status_t BinarySPI::getClockFrequency( uint32_t &freq ) noexcept
    {
      Chimera::Status_t result = SPI::Status::OK;

      freq = mapSpeedtoBits.right.find( reg_SPISpeed )->second;

      return result;
    }

    Chimera::Status_t BinarySPI::cfgPowerSupplies( const bool state )
    {
      Chimera::Status_t result = SPI::Status::FAIL;

      /*------------------------------------------------
      Build the command
      ------------------------------------------------*/
      uint8_t bitVals = reg_PeriphCfg;
      if ( state )
      {
        bitVals |= CFG_PERIPH_POWER;
      }
      else
      {
        bitVals &= ~CFG_PERIPH_POWER;
      }

      uint8_t command = CMD_CFG_PERIPH | ( bitVals & MSK_CFG_PERIPH );

      /*------------------------------------------------
      Send and verify
      ------------------------------------------------*/
      std::vector<uint8_t> cmd = { command };
      auto rx                  = busPirate.sendResponsiveCommand( cmd, static_cast<uint32_t>( cmd.size() ) );

      if ( rx.size() && rx[ 0 ] == BitBangCommands::success )
      {
        reg_PeriphCfg = bitVals;
        result        = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::cfgPullups( const bool state )
    {
      Chimera::Status_t result = SPI::Status::FAIL;

      /*------------------------------------------------
      Build the command
      ------------------------------------------------*/
      uint8_t bitVals = reg_PeriphCfg;
      if ( state )
      {
        bitVals |= CFG_PERIPH_PULLUP;
      }
      else
      {
        bitVals &= ~CFG_PERIPH_PULLUP;
      }

      uint8_t command = CMD_CFG_PERIPH | ( bitVals & MSK_CFG_PERIPH );

      /*------------------------------------------------
      Send and verify
      ------------------------------------------------*/
      std::vector<uint8_t> cmd = { command };
      auto rx                  = busPirate.sendResponsiveCommand( cmd, static_cast<uint32_t>( cmd.size() ) );

      if ( rx.size() && rx[ 0 ] == BitBangCommands::success )
      {
        reg_PeriphCfg = bitVals;
        result        = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::cfgAuxPin( const bool state )
    {
      Chimera::Status_t result = SPI::Status::FAIL;

      /*------------------------------------------------
      Build the command
      ------------------------------------------------*/
      uint8_t bitVals = reg_PeriphCfg;
      if ( state )
      {
        bitVals |= CFG_PERIPH_AUX_PIN;
      }
      else
      {
        bitVals &= ~CFG_PERIPH_AUX_PIN;
      }

      uint8_t command = CMD_CFG_PERIPH | ( bitVals & MSK_CFG_PERIPH );

      /*------------------------------------------------
      Send and verify
      ------------------------------------------------*/
      std::vector<uint8_t> cmd = { command };
      auto rx                  = busPirate.sendResponsiveCommand( cmd, static_cast<uint32_t>( cmd.size() ) );

      if ( rx.size() && rx[ 0 ] == BitBangCommands::success )
      {
        reg_PeriphCfg = bitVals;
        result        = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::cfgChipSelect( const bool state )
    {
      Chimera::Status_t result = SPI::Status::FAIL;

      /*------------------------------------------------
      Build the command
      ------------------------------------------------*/
      uint8_t bitVals = reg_PeriphCfg;
      if ( state )
      {
        bitVals |= CFG_PERIPH_CS_PIN;
      }
      else
      {
        bitVals &= ~CFG_PERIPH_CS_PIN;
      }

      uint8_t command = CMD_CFG_PERIPH | ( bitVals & MSK_CFG_PERIPH );

      /*------------------------------------------------
      Send and verify
      ------------------------------------------------*/
      std::vector<uint8_t> cmd = { command };
      auto rx                  = busPirate.sendResponsiveCommand( cmd, static_cast<uint32_t>( cmd.size() ) );

      if ( rx.size() && rx[ 0 ] == BitBangCommands::success )
      {
        reg_PeriphCfg = bitVals;
        result        = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::cfgSPIPinOut( const bool state )
    {
      Chimera::Status_t result = SPI::Status::FAIL;

      /*------------------------------------------------
      Build the command
      ------------------------------------------------*/
      uint8_t bitVals = reg_SPICfg;
      if ( state )
      {
        bitVals |= CFG_SPI_PIN_3V3;
      }
      else
      {
        bitVals &= CFG_SPI_PIN_HIZ;
      }

      uint8_t command = CMD_CFG_SPI | ( bitVals & MSK_CFG_SPI );

      /*------------------------------------------------
      Send and verify
      ------------------------------------------------*/
      std::vector<uint8_t> cmd = { command };
      auto rx                  = busPirate.sendResponsiveCommand( cmd, static_cast<uint32_t>( cmd.size() ) );

      if ( rx.size() && rx[ 0 ] == BitBangCommands::success )
      {
        reg_SPICfg = bitVals;
        result     = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::cfgSPIClkIdle( const bool state )
    {
      Chimera::Status_t result = SPI::Status::FAIL;

      /*------------------------------------------------
      Build the command
      ------------------------------------------------*/
      uint8_t bitVals = reg_SPICfg;
      if ( state )
      {
        bitVals |= CFG_SPI_CPOL_1;
      }
      else
      {
        bitVals &= CFG_SPI_CPOL_0;
      }

      uint8_t command = CMD_CFG_SPI | ( bitVals & MSK_CFG_SPI );

      /*------------------------------------------------
      Send and verify
      ------------------------------------------------*/
      std::vector<uint8_t> cmd = { command };
      auto rx                  = busPirate.sendResponsiveCommand( cmd, static_cast<uint32_t>( cmd.size() ) );

      if ( rx.size() && rx[ 0 ] == BitBangCommands::success )
      {
        reg_SPICfg = bitVals;
        result     = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::cfgSPIClkEdge( const bool direction )
    {
      Chimera::Status_t result = SPI::Status::FAIL;

      /*------------------------------------------------
      Build the command
      ------------------------------------------------*/
      uint8_t bitVals = reg_SPICfg;
      if ( direction )
      {
        bitVals |= CFG_SPI_CPHA_ACT_TO_IDLE;
      }
      else
      {
        bitVals &= CFG_SPI_CPHA_IDLE_TO_ACT;
      }

      uint8_t command = CMD_CFG_SPI | ( bitVals & MSK_CFG_SPI );

      /*------------------------------------------------
      Send and verify
      ------------------------------------------------*/
      std::vector<uint8_t> cmd = { command };
      auto rx                  = busPirate.sendResponsiveCommand( cmd, static_cast<uint32_t>( cmd.size() ) );

      if ( rx.size() && rx[ 0 ] == BitBangCommands::success )
      {
        reg_SPICfg = bitVals;
        result     = SPI::Status::OK;
      }

      return result;
    }

    Chimera::Status_t BinarySPI::reserve( const uint32_t timeout_ms )
    {
      return SPI::Status::NOT_SUPPORTED;
    }

    Chimera::Status_t BinarySPI::release( const uint32_t timeout_ms )
    {
      return SPI::Status::NOT_SUPPORTED;
    }

    Chimera::Status_t BinarySPI::bulkTransfer( TXRXPacket_t &transfer )
    {
      Chimera::Status_t result = SPI::Status::OK;
      static constexpr uint8_t BULK_TRANSFER_MAX_LEN = 16;

      auto bytesWritten = 0;
      auto bytesLeft    = transfer.writeData.size();

      if ( csMode != ChipSelectMode::MANUAL )
      {
        setChipSelect( Chimera::GPIO::State::LOW );
      }

      while ( bytesLeft && ( result == SPI::Status::OK ) )
      {
        /*------------------------------------------------
        Extract as many bytes as possible into the sub-transfer
        ------------------------------------------------*/
        std::vector<uint8_t> subTransfer;
        if ( bytesLeft > BULK_TRANSFER_MAX_LEN )
        {
          /* Transfer the max number of bytes the bus pirate supports at once */
          subTransfer = std::vector<uint8_t>( transfer.writeData.begin() + bytesWritten,
                                              transfer.writeData.begin() + bytesWritten + BULK_TRANSFER_MAX_LEN );
        }
        else
        {
          /* Transfer only the remaining bytes (less than bus pirate max) */
          subTransfer = std::vector<uint8_t>( transfer.writeData.begin() + bytesWritten,
                                              transfer.writeData.begin() + bytesWritten + bytesLeft );
        }

        /*------------------------------------------------
        Let the Bus Pirate know how many bytes we want to transfer.
        0 == 1 byte TX
        ------------------------------------------------*/
        uint8_t command = transfer.command | ( ( subTransfer.size() - 1 ) & MSK_BULK_SPI_TXFR_BYTES );
        auto data       = std::vector<uint8_t>{ command };
        auto out        = busPirate.sendResponsiveCommand( data, static_cast<uint32_t>( data.size() ) );

        /*------------------------------------------------
        If BP responds with a success, it's prepared to do the transfer. Send the data.
        ------------------------------------------------*/
        if ( out.size() && out[ 0 ] == BitBangCommands::success )
        {
          auto output = busPirate.sendResponsiveCommand( subTransfer, static_cast<uint32_t>( subTransfer.size() ) );

          /*------------------------------------------------
          Did we receive anything back?
          ------------------------------------------------*/
          if ( output.size() )
          {
            transfer.readData.insert(transfer.readData.end(), output.begin(), output.end());

            bytesLeft -= output.size();
            bytesWritten += output.size();
          }
          else
          {
            result = SPI::Status::FAILED_READ;
          }

          /*------------------------------------------------
          Handle chip select options if enabled
          ------------------------------------------------*/
          if ( csMode == ChipSelectMode::AUTO_BETWEEN_TRANSFER )
          {
            setChipSelect( Chimera::GPIO::State::HIGH );
            setChipSelect( Chimera::GPIO::State::LOW );
          }
        }
      }

      /*------------------------------------------------
      Any state besides manual must disable the chip select line after the transfer
      ------------------------------------------------*/
      if ( csMode != ChipSelectMode::MANUAL )
      {
        setChipSelect( Chimera::GPIO::State::HIGH );
      }

      return result;
    }

    Chimera::Status_t BinarySPI::writeThenRead( TXRXPacket_t &transfer )
    {
      Chimera::Status_t result = SPI::Status::FAIL;
      std::vector<uint8_t> data;

      /*------------------------------------------------
      Send the command preamble
      ------------------------------------------------*/
      data = std::vector<uint8_t>{ transfer.command, static_cast<uint8_t>( ( transfer.numWriteBytes >> 8 ) & 0xFF ),
                                   static_cast<uint8_t>( ( transfer.numWriteBytes & 0xFF ) ),
                                   static_cast<uint8_t>( ( transfer.numReadBytes >> 8 ) & 0xFF ),
                                   static_cast<uint8_t>( ( transfer.numReadBytes & 0xFF ) ) };
      busPirate.sendCommand( data );

      /*------------------------------------------------
      Send the actual data
      ------------------------------------------------*/
      auto out = busPirate.sendResponsiveCommand( transfer.writeData, transfer.numReadBytes + 1 );

      if ( out.size() && out[ 0 ] == BitBangCommands::success )
      {
        /* Remove the success byte from the returned data (0x01) */
        transfer.readData = std::vector<uint8_t>( out.begin() + 1, out.end() );
      }

      return result;
    }

  }  // namespace BusPirate
}  // namespace HWInterface


text:
//
// Copyright (c) 2019-2021 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include "integration_test_common.hpp"
#include "metadata_validator.hpp"
#include "test_common.hpp"
#include <boost/test/data/monomorphic/collection.hpp>
#include <boost/test/data/test_case.hpp>
#include <map>
#include <bitset>

using namespace boost::mysql::test;
using namespace boost::unit_test;
using boost::mysql::value;
using boost::mysql::field_metadata;
using boost::mysql::field_type;
using boost::mysql::datetime;
using boost::mysql::make_values;

BOOST_AUTO_TEST_SUITE(test_database_types)

/**
 * These tests try to cover a range of the possible types and values MySQL support.
 * Given a table, a single field, and a row_id that will match the "id" field of the table,
 * we validate that we get the expected metadata and the expected value. The cases are
 * defined in SQL in db_setup.sql
 */
struct database_types_sample
{
    std::string table;
    std::string field;
    std::string row_id;
    value expected_value;
    meta_validator mvalid;

    template <class ValueType, class... Args>
    database_types_sample(std::string table, std::string field, std::string row_id,
            ValueType&& expected_value, Args&&... args) :
        table(table),
        field(field),
        row_id(move(row_id)),
        expected_value(std::forward<ValueType>(expected_value)),
        mvalid(move(table), move(field), std::forward<Args>(args)...)
    {
    }
};

std::ostream& operator<<(std::ostream& os, const database_types_sample& input)
{
    return os << input.table << '.' << input.field << '.' << input.row_id;
}

// Fixture
struct database_types_fixture : network_fixture<boost::asio::ip::tcp::socket>
{
    database_types_fixture()
    {
        connect(boost::mysql::ssl_mode::disable);
    }
};

// Helpers
using flagsvec = std::vector<meta_validator::flag_getter>;

const flagsvec no_flags {};
const flagsvec flags_unsigned { &field_metadata::is_unsigned };
const flagsvec flags_zerofill { &field_metadata::is_unsigned, &field_metadata::is_zerofill };

// Int cases
void add_int_samples_helper(
    const char* table_name,
    field_type type,
    std::int64_t signed_min,
    std::int64_t signed_max,
    std::uint64_t unsigned_max,
    std::vector<database_types_sample>& output
)
{
    output.emplace_back(table_name, "field_signed", "regular", std::int64_t(20), type);
    output.emplace_back(table_name, "field_signed", "negative", std::int64_t(-20), type);
    output.emplace_back(table_name, "field_signed", "min", signed_min, type);
    output.emplace_back(table_name, "field_signed", "max", signed_max, type);

    output.emplace_back(table_name, "field_unsigned", "regular", std::uint64_t(20), type, flags_unsigned);
    output.emplace_back(table_name, "field_unsigned", "min", std::uint64_t(0), type, flags_unsigned);
    output.emplace_back(table_name, "field_unsigned", "max", unsigned_max, type, flags_unsigned);

    output.emplace_back(table_name, "field_width", "regular", std::int64_t(20), type);
    output.emplace_back(table_name, "field_width", "negative", std::int64_t(-20), type);

    output.emplace_back(table_name, "field_zerofill", "regular", std::uint64_t(20), type, flags_zerofill);
    output.emplace_back(table_name, "field_zerofill", "min", std::uint64_t(0), type, flags_zerofill);
}

void add_int_samples(std::vector<database_types_sample>& output)
{
    add_int_samples_helper("types_tinyint", field_type::tinyint,
        -0x80, 0x7f, 0xff, output);
    add_int_samples_helper("types_smallint", field_type::smallint,
        -0x8000, 0x7fff, 0xffff, output);
    add_int_samples_helper("types_mediumint", field_type::mediumint,
        -0x800000, 0x7fffff, 0xffffff, output);
    add_int_samples_helper("types_int", field_type::int_,
        -0x80000000LL, 0x7fffffff, 0xffffffff, output);
    add_int_samples_helper("types_bigint", field_type::bigint,
        -0x7fffffffffffffff - 1, 0x7fffffffffffffff, 0xffffffffffffffff, output);
}

// Floating point cases
void add_float_samples(std::vector<database_types_sample>& output)
{
    output.emplace_back("types_float", "field_signed", "zero",
        0.0f, field_type::float_, no_flags, 31);
    output.emplace_back("types_float", "field_signed", "int_positive",
        4.0f, field_type::float_, no_flags, 31);
    output.emplace_back("types_float", "field_signed", "int_negative",
        -4.0f, field_type::float_, no_flags, 31);
    output.emplace_back("types_float", "field_signed", "fractional_positive",
        4.2f, field_type::float_, no_flags, 31);
    output.emplace_back("types_float", "field_signed", "fractional_negative",
        -4.2f, field_type::float_, no_flags, 31);
    output.emplace_back("types_float", "field_signed", "positive_exp_positive_int",
        3e20f, field_type::float_, no_flags, 31);
    output.emplace_back("types_float", "field_signed", "positive_exp_negative_int",
        -3e20f, field_type::float_, no_flags, 31);
    output.emplace_back("types_float", "field_signed", "positive_exp_positive_fractional",
        3.14e20f, field_type::float_, no_flags, 31);
    output.emplace_back("types_float", "field_signed", "positive_exp_negative_fractional",
        -3.14e20f, field_type::float_, no_flags, 31);
    output.emplace_back("types_float", "field_signed", "negative_exp_positive_fractional",
        3.14e-20f, field_type::float_, no_flags, 31);

    output.emplace_back("types_float", "field_unsigned", "zero",
        0.0f, field_type::float_, flags_unsigned, 31);
    output.emplace_back("types_float", "field_unsigned", "fractional_positive",
        4.2f, field_type::float_, flags_unsigned, 31);

    output.emplace_back("types_float", "field_width", "zero",
        0.0f, field_type::float_, no_flags, 10);
    output.emplace_back("types_float", "field_width", "fractional_positive",
        4.2f, field_type::float_, no_flags, 10);
    output.emplace_back("types_float", "field_width", "fractional_negative",
        -4.2f, field_type::float_, no_flags, 10);

    output.emplace_back("types_float", "field_zerofill", "zero",
        0.0f, field_type::float_, flags_zerofill, 31);
    output.emplace_back("types_float", "field_zerofill", "fractional_positive",
        4.2f, field_type::float_, flags_zerofill, 31);
    output.emplace_back("types_float", "field_zerofill", "positive_exp_positive_fractional",
        3.14e20f, field_type::float_, flags_zerofill, 31);
    output.emplace_back("types_float", "field_zerofill", "negative_exp_positive_fractional",
        3.14e-20f, field_type::float_, flags_zerofill, 31);
}

void add_double_samples(std::vector<database_types_sample>& output)
{
    output.emplace_back("types_double", "field_signed", "zero",
        0.0, field_type::double_, no_flags, 31);
    output.emplace_back("types_double", "field_signed", "int_positive",
        4.0, field_type::double_, no_flags, 31);
    output.emplace_back("types_double", "field_signed", "int_negative",
        -4.0, field_type::double_, no_flags, 31);
    output.emplace_back("types_double", "field_signed", "fractional_positive",
        4.2, field_type::double_, no_flags, 31);
    output.emplace_back("types_double", "field_signed", "fractional_negative",
        -4.2, field_type::double_, no_flags, 31);
    output.emplace_back("types_double", "field_signed", "positive_exp_positive_int",
        3e200, field_type::double_, no_flags, 31);
    output.emplace_back("types_double", "field_signed", "positive_exp_negative_int",
        -3e200, field_type::double_, no_flags, 31);
    output.emplace_back("types_double", "field_signed", "positive_exp_positive_fractional",
        3.14e200, field_type::double_, no_flags, 31);
    output.emplace_back("types_double", "field_signed", "positive_exp_negative_fractional",
        -3.14e200, field_type::double_, no_flags, 31);
    output.emplace_back("types_double", "field_signed", "negative_exp_positive_fractional",
        3.14e-200, field_type::double_, no_flags, 31);

    output.emplace_back("types_double", "field_unsigned", "zero",
        0.0, field_type::double_, flags_unsigned, 31);
    output.emplace_back("types_double", "field_unsigned", "fractional_positive",
        4.2, field_type::double_, flags_unsigned, 31);

    output.emplace_back("types_double", "field_width", "zero",
        0.0, field_type::double_, no_flags, 10);
    output.emplace_back("types_double", "field_width", "fractional_positive",
        4.2, field_type::double_, no_flags, 10);
    output.emplace_back("types_double", "field_width", "fractional_negative",
        -4.2, field_type::double_, no_flags, 10);

    output.emplace_back("types_double", "field_zerofill", "zero",
        0.0, field_type::double_, flags_zerofill, 31);
    output.emplace_back("types_double", "field_zerofill", "fractional_positive",
        4.2, field_type::double_, flags_zerofill, 31);
    output.emplace_back("types_double", "field_zerofill", "positive_exp_positive_fractional",
        3.14e200, field_type::double_, flags_zerofill, 31);
    output.emplace_back("types_double", "field_zerofill", "negative_exp_positive_fractional",
        3.14e-200, field_type::double_, flags_zerofill, 31);
}

// Date cases
// MySQL accepts zero and invalid dates. We represent them as NULL
constexpr const char* invalid_date_cases [] = {
    "zero",
    "yzero_mzero_dregular",
    "yzero_mregular_dzero",
    "yzero_invalid_date",
    "yregular_mzero_dzero",
    "yregular_mzero_dregular",
    "yregular_mregular_dzero",
    "yregular_invalid_date",
    "yregular_invalid_date_leapregular",
    "yregular_invalid_date_leap100"
};

void add_date_samples(std::vector<database_types_sample>& output)
{
    constexpr auto type = field_type::date;
    constexpr const char* table = "types_date";
    constexpr const char* field = "field_date";

    // Regular cases
    output.emplace_back(table, field, "regular", makedate(2010, 3, 28), type);
    output.emplace_back(table, field, "leap_regular", makedate(1788, 2, 29), type);
    output.emplace_back(table, field, "leap_400", makedate(2000, 2, 29), type);
    output.emplace_back(table, field, "min", makedate(0, 1, 1), type);
    output.emplace_back(table, field, "max", makedate(9999, 12, 31), type);

    // Invalid DATEs
    for (const char* invalid_case: invalid_date_cases)
    {
        output.emplace_back(table, field, invalid_case, nullptr, type);
    }
}

// Datetime and time cases

// Given a number of microseconds, removes the least significant part according to decimals
int round_micros(int input, int decimals)
{
    assert(decimals >= 0 && decimals <= 6);
    if (decimals == 0) return 0;
    auto modulus = static_cast<int>(std::pow(10, 6 - decimals));
    return (input / modulus) * modulus;
}

std::chrono::microseconds round_micros(std::chrono::microseconds input, int decimals)
{
    return std::chrono::microseconds(round_micros(static_cast<int>(input.count()), decimals));
}

std::pair<std::string, datetime> datetime_from_id(std::bitset<4> id, int decimals)
{
    // id represents which components (h, m, s, u) should the test case have
    constexpr struct
    {
        char letter;
        std::chrono::microseconds offset;
    } bit_meaning [] = {
        { 'h', std::chrono::hours(23) }, // bit 0
        { 'm', std::chrono::minutes(1) },
        { 's', std::chrono::seconds(50) },
        { 'u', std::chrono::microseconds(123456) }
    };

    std::string name;
    datetime dt = makedt(2010, 5, 2); // components all tests have

    for (std::size_t i = 0; i < id.size(); ++i)
    {
        if (id[i]) // component present
        {
            char letter = bit_meaning[i].letter;
            auto offset = bit_meaning[i].offset;
            name.push_back(letter); // add to name
            dt += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
        }
    }
    if (name.empty()) name = "date";

    return {name, dt};
}

database_types_sample create_datetime_sample(
    int decimals,
    std::string id,
    value expected,
    field_type type
)
{
    static std::map<field_type, const char*> table_map {
        { field_type::datetime, "types_datetime" },
        { field_type::timestamp, "types_timestamp" },
        { field_type::time, "types_time" }
    };
    // Inconsistencies between Maria and MySQL in the unsigned flag
    // we don't really care here about signedness of timestamps
    return database_types_sample(
        table_map.at(type),
        "field_" + std::to_string(decimals),
        std::move(id),
        expected,
        type,
        no_flags,
        decimals,
        flagsvec{ &field_metadata::is_unsigned }
    );
}

std::pair<std::string, boost::mysql::time> time_from_id(std::bitset<6> id, int decimals)
{
    // id represents which components (h, m, s, u) should the test case have
    constexpr struct
    {
        char letter;
        std::chrono::microseconds offset;
    } bit_meaning [] = {
        { 'n', std::chrono::hours(0) }, // bit 0: negative bit
        { 'd', std::chrono::hours(48) }, // bit 1
        { 'h', std::chrono::hours(23) }, // bit 2
        { 'm', std::chrono::minutes(1) },
        { 's', std::chrono::seconds(50) },
        { 'u', std::chrono::microseconds(123456) }
    };

    std::string name;
    boost::mysql::time t {0};

    for (std::size_t i = 1; i < id.size(); ++i)
    {
        if (id[i]) // component present
        {
            char letter = bit_meaning[i].letter;
            auto offset = bit_meaning[i].offset;
            name.push_back(letter); // add to name
            t += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
        }
    }
    if (name.empty())
        name = "zero";
    if (id[0]) // bit sign
    {
        name = "negative_" + name;
        t = -t;
    }

    return {name, t};
}

// shared between DATETIME and TIMESTAMP
void add_common_datetime_samples(
    field_type type,
    std::vector<database_types_sample>& output
)
{
    for (int decimals = 0; decimals <= 6; ++decimals)
    {
        // Regular values
        auto max_int_id = static_cast<std::size_t>(std::pow(2, 4)); // 4 components can be varied
        for (std::size_t int_id = 0; int_id < max_int_id; ++int_id)
        {
            std::bitset<4> bitset_id (int_id);
            if (bitset_id[3] && decimals == 0)
                continue; // cases with micros don't make sense for fields with no decimals
            auto dt = datetime_from_id(int_id, decimals);
            output.push_back(create_datetime_sample(decimals, move(dt.first), value(dt.second), type));
        }

        // Tests for leap years (valid dates)
        output.push_back(create_datetime_sample(
            decimals, "date_leap4", value(makedt(2004, 2, 29)), type));
        output.push_back(create_datetime_sample(
            decimals, "date_leap400", value(makedt(2000, 2, 29)), type));
    }
}

void add_datetime_samples(std::vector<database_types_sample>& output)
{
    add_common_datetime_samples(field_type::datetime, output);

    for (int decimals = 0; decimals <= 6; ++decimals)
    {
        // min and max
        output.push_back(create_datetime_sample(decimals, "min",
                value(makedt(0, 1, 1)), field_type::datetime));
        output.push_back(create_datetime_sample(decimals, "max",
                value(makedt(9999, 12, 31, 23, 59, 59,
                    round_micros(999999, decimals))), field_type::datetime));

        // invalid dates
        const char* lengths [] = {"date", "hms", decimals ? "hmsu" : nullptr };
        for (const char* length:  lengths)
        {
            if (!length)
                continue;
            for (const char* invalid_date_case: invalid_date_cases)
            {
                output.push_back(create_datetime_sample(
                    decimals,
                    stringize(length, '_', invalid_date_case),
                    value(),
                    field_type::datetime
                ));
            }
        }
    }
}

void add_timestamp_samples(std::vector<database_types_sample>& output)
{
    add_common_datetime_samples(field_type::timestamp, output);

    // Only the full-zero TIMESTAMP is allowed - timestamps with
    // invalid date parts are converted to the zero TIMESTAMP
    for (int decimals = 0; decimals <= 6; ++decimals)
    {
        output.push_back(create_datetime_sample(decimals,
            "zero", value(), field_type::timestamp));
    }
}

void add_time_samples(std::vector<database_types_sample>& output)
{
    for (int decimals = 0; decimals <= 6; ++decimals)
    {
        // Regular values
        auto max_int_id = static_cast<std::size_t>(std::pow(2, 6)); // 6 components can be varied
        for (std::size_t int_id = 0; int_id < max_int_id; ++int_id)
        {
            std::bitset<6> bitset_id (int_id);
            if (bitset_id[5] && decimals == 0) continue; // cases with micros don't make sense for fields with no decimals
            if (bitset_id.to_ulong() == 1) continue; // negative zero does not make sense
            auto t = time_from_id(int_id, decimals);
            output.push_back(create_datetime_sample(
                decimals, move(t.first), value(t.second), field_type::time));
        }

        // min and max
        auto max_value = decimals == 0 ? maket(838, 59, 59) : maket(838, 59, 58, round_micros(999999, decimals));
        output.push_back(create_datetime_sample(
            decimals, "min", value(-max_value), field_type::time));
        output.push_back(create_datetime_sample(
            decimals, "max",  value(max_value), field_type::time));
    }
}

// Year
void add_year_samples(std::vector<database_types_sample>& output)
{
    output.emplace_back("types_year", "field_default", "regular",
        std::uint64_t(2019), field_type::year, flags_zerofill);
    output.emplace_back("types_year", "field_default", "min",
        std::uint64_t(1901), field_type::year, flags_zerofill);
    output.emplace_back("types_year", "field_default", "max",
        std::uint64_t(2155), field_type::year, flags_zerofill);
    output.emplace_back("types_year", "field_default", "zero",
        std::uint64_t(0), field_type::year, flags_zerofill);
}

// String types
void add_string_samples(std::vector<database_types_sample>& output)
{
    output.emplace_back("types_string", "field_char", "regular", "test_char", field_type::char_);
    output.emplace_back("types_string", "field_char", "utf8", "\xc3\xb1", field_type::char_);
    output.emplace_back("types_string", "field_char", "empty", "", field_type::char_);

    output.emplace_back("types_string", "field_varchar", "regular", "test_varchar", field_type::varchar);
    output.emplace_back("types_string", "field_varchar", "utf8", "\xc3\x91", field_type::varchar);
    output.emplace_back("types_string", "field_varchar", "empty", "", field_type::varchar);

    output.emplace_back("types_string", "field_tinytext", "regular", "test_tinytext", field_type::text);
    output.emplace_back("types_string", "field_tinytext", "utf8", "\xc3\xa1", field_type::text);
    output.emplace_back("types_string", "field_tinytext", "empty", "", field_type::text);

    output.emplace_back("types_string", "field_text", "regular", "test_text", field_type::text);
    output.emplace_back("types_string", "field_text", "utf8", "\xc3\xa9", field_type::text);
    output.emplace_back("types_string", "field_text", "empty", "", field_type::text);

    output.emplace_back("types_string", "field_mediumtext", "regular", "test_mediumtext", field_type::text);
    output.emplace_back("types_string", "field_mediumtext", "utf8", "\xc3\xad", field_type::text);
    output.emplace_back("types_string", "field_mediumtext", "empty", "", field_type::text);

    output.emplace_back("types_string", "field_longtext", "regular", "test_longtext", field_type::text);
    output.emplace_back("types_string", "field_longtext", "utf8", "\xc3\xb3", field_type::text);
    output.emplace_back("types_string", "field_longtext", "empty", "", field_type::text);

    output.emplace_back("types_string", "field_enum", "regular", "red", field_type::enum_);

    output.emplace_back("types_string", "field_set", "regular", "red,green", field_type::set);
    output.emplace_back("types_string", "field_set", "empty", "", field_type::set);
}

void add_binary_samples(std::vector<database_types_sample>& output)
{
    // BINARY values get padded with zeros to the declared length
    output.emplace_back("types_binary", "field_binary", "regular", makesv("\0_binary\0\0"), field_type::binary);
    output.emplace_back("types_binary", "field_binary", "nonascii", makesv("\0\xff" "\0\0\0\0\0\0\0\0"), field_type::binary);
    output.emplace_back("types_binary", "field_binary", "empty", makesv("\0\0\0\0\0\0\0\0\0\0"), field_type::binary);

    output.emplace_back("types_binary", "field_varbinary", "regular", makesv("\0_varbinary"), field_type::varbinary);
    output.emplace_back("types_binary", "field_varbinary", "nonascii", makesv("\1\xfe"), field_type::varbinary);
    output.emplace_back("types_binary", "field_varbinary", "empty", "", field_type::varbinary);

    output.emplace_back("types_binary", "field_tinyblob", "regular", makesv("\0_tinyblob"), field_type::blob);
    output.emplace_back("types_binary", "field_tinyblob", "nonascii", makesv("\2\xfd"), field_type::blob);
    output.emplace_back("types_binary", "field_tinyblob", "empty", "", field_type::blob);

    output.emplace_back("types_binary", "field_blob", "regular", makesv("\0_blob"), field_type::blob);
    output.emplace_back("types_binary", "field_blob", "nonascii", makesv("\3\xfc"), field_type::blob);
    output.emplace_back("types_binary", "field_blob", "empty", "", field_type::blob);

    output.emplace_back("types_binary", "field_mediumblob", "regular", makesv("\0_mediumblob"), field_type::blob);
    output.emplace_back("types_binary", "field_mediumblob", "nonascii", makesv("\4\xfb"), field_type::blob);
    output.emplace_back("types_binary", "field_mediumblob", "empty", "", field_type::blob);

    output.emplace_back("types_binary", "field_longblob", "regular", makesv("\0_longblob"), field_type::blob);
    output.emplace_back("types_binary", "field_longblob", "nonascii", makesv("\5\xfa"), field_type::blob);
    output.emplace_back("types_binary", "field_longblob", "empty", "", field_type::blob);
}

// Tests for not implemented types
// These types do not have a more concrete representation in the library yet.
// Check we get them as strings and we get the metadata correctly
std::uint8_t geometry_value [] = {
    0x00, 0x00, 0x00,
    0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x40
};

void add_not_implemented_samples(std::vector<database_types_sample>& output)
{
    output.emplace_back("types_not_implemented", "field_bit", "regular",
        "\xfe", field_type::bit, flags_unsigned);
    output.emplace_back("types_not_implemented", "field_decimal", "regular",
        "300", field_type::decimal);
    output.emplace_back("types_not_implemented", "field_geometry", "regular",
        makesv(geometry_value), field_type::geometry);
}

// Tests for certain metadata flags and NULL values
void add_flags_samples(std::vector<database_types_sample>& output)
{
    output.emplace_back("types_flags", "field_timestamp", "default",
        nullptr, field_type::timestamp, flagsvec{&field_metadata::is_set_to_now_on_update},
            0, flagsvec{&field_metadata::is_unsigned});
    output.emplace_back("types_flags", "field_primary_key", "default",
        std::int64_t(50), field_type::int_,
            flagsvec{&field_metadata::is_primary_key, &field_metadata::is_not_null,
                &field_metadata::is_auto_increment});
    output.emplace_back("types_flags", "field_not_null", "default",
        "char", field_type::char_, flagsvec{&field_metadata::is_not_null});
    output.emplace_back("types_flags", "field_unique", "default",
        std::int64_t(21), field_type::int_, flagsvec{&field_metadata::is_unique_key});
    output.emplace_back("types_flags", "field_indexed", "default",
        std::int64_t(42), field_type::int_, flagsvec{&field_metadata::is_multiple_key});
}

std::vector<database_types_sample> make_all_samples()
{
    std::vector<database_types_sample> res;
    add_int_samples(res);
    add_float_samples(res);
    add_double_samples(res);
    add_date_samples(res);
    add_datetime_samples(res);
    add_timestamp_samples(res);
    add_time_samples(res);
    add_year_samples(res);
    add_string_samples(res);
    add_binary_samples(res);
    add_not_implemented_samples(res);
    add_flags_samples(res);
    return res;
}

std::vector<database_types_sample> all_samples = make_all_samples();

BOOST_DATA_TEST_CASE_F(database_types_fixture, query, data::make(all_samples))
{
    // Compose the query
    auto query = stringize(
        "SELECT ", sample.field,
        " FROM ", sample.table,
        " WHERE id = '", sample.row_id, "'"
    );

    // Execute it
    auto result = conn.query(query);
    auto rows = result.read_all();

    // Validate the received metadata
    validate_meta(result.fields(), {sample.mvalid});

    // Validate the returned value
    BOOST_TEST_REQUIRE(rows.size() == 1);
    BOOST_TEST_REQUIRE(rows[0].values().size() == 1);
    BOOST_TEST(rows[0].values()[0] == sample.expected_value);
}

BOOST_DATA_TEST_CASE_F(database_types_fixture, prepared_statement, data::make(all_samples))
{
    // Prepare the statement
    auto stmt_sql = stringize(
        "SELECT ", sample.field,
        " FROM ", sample.table,
        " WHERE id = ?"
    );
    auto stmt = conn.prepare_statement(stmt_sql);

    // Execute it with the provided parameters
    auto result = stmt.execute(make_values(sample.row_id));
    auto rows = result.read_all();

    // Validate the received metadata
    validate_meta(result.fields(), {sample.mvalid});

    // Validate the returned value
    BOOST_TEST_REQUIRE(rows.size() == 1);
    BOOST_TEST_REQUIRE(rows[0].values().size() == 1);
    BOOST_TEST(rows[0].values()[0] == sample.expected_value);
}

// The prepared statement param tests binary serialization.
// This test is not applicable (yet) to nullptr values or bit values.
// Doing "field = ?" where ? is nullptr never matches anything.
// Bit values are returned as strings bit need to be sent as integers in
// prepared statements. Filter the cases to remove the ones that
// are not applicable
std::vector<database_types_sample>
make_prepared_stmt_param_samples()
{
    std::vector<database_types_sample> res;
    res.reserve(all_samples.size());
    for (const auto& test : all_samples)
    {
        if (!test.expected_value.is_null() &&
             test.mvalid.type() != field_type::bit)
        {
            res.push_back(test);
        }
    }
    return res;
}

BOOST_DATA_TEST_CASE_F(database_types_fixture,
    prepared_statement_execute_param,
    data::make(make_prepared_stmt_param_samples()))
{
    // Prepare the statement
    auto stmt_sql = stringize(
        "SELECT ", sample.field,
        " FROM ", sample.table,
        " WHERE id = ? AND ", sample.field, " = ?"
    );
    auto stmt = conn.prepare_statement(stmt_sql);

    // Execute it with the provided parameters
    auto result = stmt.execute(make_values(sample.row_id, sample.expected_value));
    auto rows = result.read_all();

    // Validate the returned value
    BOOST_TEST_REQUIRE(rows.size() == 1);
    BOOST_TEST_REQUIRE(rows[0].values().size() == 1);
    BOOST_TEST(rows[0].values()[0] == sample.expected_value);
}

// Validate that the metadata we retrieve with certain queries is correct
BOOST_FIXTURE_TEST_CASE(aliased_table_metadata, database_types_fixture)
{
    auto result = conn.query(
        "SELECT field_varchar AS field_alias FROM empty_table table_alias");
    std::vector<meta_validator> validators {
        { "table_alias", "empty_table", "field_alias",
            "field_varchar", field_type::varchar }
    };
    validate_meta(result.fields(), validators);
}

BOOST_AUTO_TEST_SUITE_END() // test_database_types

ext: cpp

max_stars_repo_name: sehe/mysql

max_issues_repo_name: sehe/mysql

max_forks_repo_name: sehe/mysql


text:
#include <stan/math/prim.hpp>
#include <test/unit/math/prim/fun/binary_scalar_tester.hpp>
#include <boost/math/special_functions/owens_t.hpp>
#include <gtest/gtest.h>
#include <cmath>
#include <limits>

TEST(MathFunctions, owens_t) {
  double a = 1.0;
  double b = 2.0;
  EXPECT_FLOAT_EQ(stan::math::owens_t(a, b), boost::math::owens_t(a, b));
}

TEST(MathFunctions, owens_t_nan) {
  double nan = std::numeric_limits<double>::quiet_NaN();

  EXPECT_TRUE(std::isnan(stan::math::owens_t(1.0, nan)));
  EXPECT_TRUE(std::isnan(stan::math::owens_t(nan, 2.0)));
  EXPECT_TRUE(std::isnan(stan::math::owens_t(nan, nan)));
}

TEST(MathFunctions, owens_t_vec) {
  auto f = [](const auto& x1, const auto& x2) {
    using stan::math::owens_t;
    return owens_t(x1, x2);
  };

  Eigen::VectorXd in1(3);
  in1 << 1.8, 3.24, 1.8;
  Eigen::VectorXd in2(3);
  in2 << 7.3, 0.7, 2.8;
  stan::test::binary_scalar_tester(f, in1, in2);
}


text:
// Copyright 2019 Lawrence Livermore National Security, LLC and other Metall Project Developers.
// See the top-level COPYRIGHT file for details.
//
// SPDX-License-Identifier: (Apache-2.0 OR MIT)

#include "gtest/gtest.h"

#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/container/map.hpp>
#include <metall/container/concurrent_map.hpp>
#include <metall/detail/file.hpp>
#include "../test_utility.hpp"

namespace bip = boost::interprocess;

namespace {

TEST (ConcurrentMapTest, SequentialInsert) {
  boost::container::map<char, int> ref_map;
  metall::container::concurrent_map<char, int> map;

  const auto v1 = std::make_pair('a', 0);
  const auto v1_2 = std::make_pair('a', 0);
  GTEST_ASSERT_EQ(ref_map.insert(v1).second, map.insert(v1));
  GTEST_ASSERT_EQ(ref_map.insert(v1).second, map.insert(v1)); // Check unique insertion
  GTEST_ASSERT_EQ(ref_map.insert(v1_2).second, map.insert(v1_2)); // Duplicate key

  const auto v2 = std::make_pair('b', 1);
  GTEST_ASSERT_EQ(ref_map.insert(v2).second, map.insert(v2)); // Another key
}

TEST (ConcurrentMapTest, Count) {
  metall::container::concurrent_map<char, int> map;

  const auto v1 = std::make_pair('a', 0);
  GTEST_ASSERT_EQ(map.count(v1.first), 0);
  map.insert(v1);
  GTEST_ASSERT_EQ(map.count(v1.first), 1);

  const auto v2 = std::make_pair('b', 1);
  GTEST_ASSERT_EQ(map.count(v2.first), 0);
  map.insert(v2);
  GTEST_ASSERT_EQ(map.count(v2.first), 1);
}

TEST (ConcurrentMapTest, Size) {
  metall::container::concurrent_map<char, int> map;

  GTEST_ASSERT_EQ(map.size(), 0);

  map.insert(std::make_pair('a', 0));
  GTEST_ASSERT_EQ(map.size(), 1);

  map.insert(std::make_pair('b', 0));
  GTEST_ASSERT_EQ(map.size(), 2);

  map.insert(std::make_pair('b', 0));
  GTEST_ASSERT_EQ(map.size(), 2);
}

TEST (ConcurrentMapTest, SequentialEdit) {
  boost::container::map<char, int> ref_map;
  metall::container::concurrent_map<char, int> map;

  const auto v1 = std::make_pair('a', 0);
  ref_map[v1.first] = v1.second;
  {
    auto ret = map.scoped_edit(v1.first);
    ret.first = v1.second;
  }
  GTEST_ASSERT_EQ(ref_map.count(v1.first), map.count(v1.first));

  const auto v2 = std::make_pair('b', 1);
  ref_map[v2.first] = v2.second;
  map.edit(v2.first, [&v2](int &v){
    v = v2.second;
  });
  GTEST_ASSERT_EQ(ref_map.count(v2.first), map.count(v2.first));
}

TEST (ConcurrentMapTest, Find) {
  boost::container::map<char, int> ref_map;
  metall::container::concurrent_map<char, int> map;

  const auto v1 = std::make_pair('a', 0);
  GTEST_ASSERT_EQ(map.find(v1.first), map.cend());
  map.insert(v1);
  GTEST_ASSERT_NE(map.find(v1.first), map.cend());
  auto itr1 = map.find(v1.first);
  GTEST_ASSERT_EQ(itr1->first, v1.first);
  GTEST_ASSERT_EQ(itr1->second, v1.second);

  const auto v2 = std::make_pair('b', 1);
  GTEST_ASSERT_EQ(map.find(v2.first), map.cend());
  map.insert(v2);
  GTEST_ASSERT_NE(map.find(v2.first), map.cend());
  auto itr2 = map.find(v2.first);
  GTEST_ASSERT_EQ(itr2->first, v2.first);
  GTEST_ASSERT_EQ(itr2->second, v2.second);
}

TEST (ConcurrentMapTest, Iterator) {
  boost::container::map<char, int> ref_map;
  metall::container::concurrent_map<char, int> map;

  const auto v1 = std::make_pair('a', 0);
  ref_map.insert(v1);
  map.insert(v1);

  const auto v2 = std::make_pair('b', 1);
  ref_map.insert(v2);
  map.insert(v2);

  int num_elems = 0;
  for (auto itr = map.cbegin(), end = map.cend(); itr != end; ++itr) {
    GTEST_ASSERT_EQ(ref_map.count(itr->first), 1) << "Invalid key was found: " << (char)itr->first;
    GTEST_ASSERT_EQ(ref_map.at(itr->first), itr->second);
    ++num_elems;
  }
  GTEST_ASSERT_EQ(num_elems, 2);
}

TEST (ConcurrentMapTest, Persistence) {

  using allocator_type = bip::allocator<std::pair<const char, int>, bip::managed_mapped_file::segment_manager>;
  using map_type =  metall::container::concurrent_map<char, int, std::less<char>, std::hash<char>, allocator_type, 2>;

  const std::string file_path(test_utility::make_test_path());

  test_utility::create_test_dir();
  metall::mtlldetail::remove_file(file_path);

  std::vector<std::pair<char, int>> inputs(10);
  for (int i = 0; i < (int)inputs.size(); ++i) {
    inputs[i].first = (char)('a' + i);
    inputs[i].second = i;
  }

  {
    bip::managed_mapped_file mfile(bip::create_only, file_path.c_str(), 1 << 20);
    auto pmap = mfile.construct<map_type>("map")(mfile.get_allocator<typename allocator_type::value_type>());

    for (auto& elem : inputs) {
        pmap->insert(elem);
    }
  }

  {
    bip::managed_mapped_file mfile(bip::open_only, file_path.c_str());
    const auto pmap = mfile.find<map_type>("map").first;
    GTEST_ASSERT_NE(pmap, nullptr);

    for (auto& elem : inputs) {
      GTEST_ASSERT_NE(pmap->find(elem.first), pmap->cend());
      auto itr = pmap->find(elem.first);
      GTEST_ASSERT_EQ(itr->first, elem.first);
      GTEST_ASSERT_EQ(itr->second, elem.second);
    }
  }
}
}

max_stars_repo_name: dice-group/metall

max_issues_repo_name: dice-group/metall

max_forks_repo_name: dice-group/metall


text:
//
//  Copyright (c) 2021 Kannav Mehta
//
//  Distributed under the Boost Software License, Version 1.0. (See
//  accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef LA_MATRIX_EXPRESSION_HPP
#define LA_MATRIX_EXPRESSION_HPP

#include <boost/numeric/ublas/matrix/matrix/traits/matrix_traits.hpp>
#include <type_traits>

namespace boost::numeric::ublas::experimental::detail {

/**
 * @brief Template class to store both unary and binary matrix expressions
 *
 * @tparam callable The function object for the respective expression
 * @tparam operands The one or two operands for the expression
 */
template<class callable, class... operands>
class matrix_expression {
    using expr_t = callable;
public:
    explicit matrix_expression(callable f, operands const &... args) : f(f), args(args...) {}

private:
    callable f;
    std::tuple<operands const &...> args;
};

template<class L, class R>
auto operator*(const L &l, const R &r) {
    return matrix_expression{[](const L &l, const R &r) {
        return l * r;
    }, l, r};
}

}
#endif //LA_MATRIX_EXPRESSION_HPP

ext: hpp

max_stars_repo_name: abakfja/linear-algebra

max_issues_repo_name: abakfja/linear-algebra

max_forks_repo_name: abakfja/linear-algebra


text:
// [===========================================================================]
// [                                M o n e t a                                ]
// [---------------------------------------------------------------------------]
// [                                                                           ]
// [                          Copyright (C) 2005-2015                          ]
// [                      Rodrigo Madera <madera@acm.org>                      ]
// [                                                                           ]
// [---------------------------------------------------------------------------]
// [         Distributed under the Boost Software License, Version 1.0         ]
// [ Read accompanying LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt ]
// [===========================================================================]

#include "pch.hxx"
#include <moneta/codec/_aux/path_tabs.hxx>
#include <boost/mpl/vector.hpp>
#include "../../model/SportsTeam.hxx"

/*
	TODO: Delete this table after coding.

	|								      PathSz   Cmem    CwdC    Extra	|  Total
--------+-----------------------------------------------------------------------+-------+-------+--------+------+-------
/	|<Country>								0	0	0	 0	|    0
/	|	<Name>Germany</Name>						0	0	0	 1	|    1
/Address|	<Address Number="1" Street="Strange St.">			1	0	0	 0	|    1
/Address|	   +1   <Reference>Abbey Something</Reference>			1	0	0	 1	|    2
/Address|	</Address>							1	0	0	 0	|    1
/Persons|	<Persons>							1	1	0	-1	|    1
/Persons|	        <Person ID="0">						1	1	0	 0	|    2
/Persons|		   +1   <Name>Michael Jordan</Name>			1	1	0	 1	|    3
/Persons|		   +1   <Height>2</Height>				1	1	0	 1	|    3
/Persons|		   +1   <Ratings>0</Ratings>				1	1	0	 1	|    3
/Persons|		</Person>						1	1	0	 0	|    2
/Persons|		<Person ID="0">						1	1	0	 0	|    2
/Persons|		   +1   <Name>Magic Johnson</Name>			1	1	0	 1	|    3
/Persons|		   +1   <Height>2.1000000000000001</Height>		1	1	0	 1	|    3
/Persons|		   +1   <Ratings>0</Ratings>				1	1	0	 1	|    3
/Persons|		</Person>						1	1	0	 0	|    2
/Persons|	</Persons>							1	1	0	-1	|    1
/Tags	|	<Tags>								1	1	0	-1	|    1
/Tags	|		<CrazyTag>Sometag</CrazyTag>				1	1	0	 0	|    2
/Tags	|		<CrazyTag>Anothertag</CrazyTag>				1	1	0	 0	|    2
/Tags	|	</Tags>								1	1	0	-1	|    1
/	|</Country>								0	0	0	 0	|    0

*/

BOOST_AUTO_TEST_CASE(path_tabs_test) {
	using moneta::codec::aux::path_tabs;

	typedef MONETA_MEMBER(SportsTeam, std::string, Name) M;

	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector0<                   > >()), ""              );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector1<M                  > >()), "\t"            );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector2<M, M               > >()), "\t\t"          );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector3<M, M, M            > >()), "\t\t\t"        );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector4<M, M, M, M         > >()), "\t\t\t\t"      );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector5<M, M, M, M, M      > >()), "\t\t\t\t\t"    );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector6<M, M, M, M, M, M   > >()), "\t\t\t\t\t\t"  );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector7<M, M, M, M, M, M, M> >()), "\t\t\t\t\t\t\t");

	typedef MONETA_MEMBER(SportsTeam, std::vector<std::string>, Tags) C;

	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector0<                   > >()),      ""              );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector1<C                  > >()), "\t" "\t"            );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector2<C, M               > >()), "\t" "\t\t"          );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector3<C, M, M            > >()), "\t" "\t\t\t"        );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector4<C, M, M, M         > >()), "\t" "\t\t\t\t"      );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector5<C, M, M, M, M      > >()), "\t" "\t\t\t\t\t"    );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector6<C, M, M, M, M, M   > >()), "\t" "\t\t\t\t\t\t"  );
	BOOST_CHECK_EQUAL((path_tabs<boost::mpl::vector7<C, M, M, M, M, M, M> >()), "\t" "\t\t\t\t\t\t\t");

	typedef boost::mpl::vector7<
		MONETA_MEMBER(SportsTeam, std::vector<std::string>, Tags),
		MONETA_MEMBER(SportsTeam, std::string, Name),
		MONETA_MEMBER(SportsTeam, std::vector<Person>, Players),
		MONETA_MEMBER(SportsTeam, std::string, Name),
		MONETA_MEMBER(SportsTeam, std::vector<std::string>, Tags),
		MONETA_MEMBER(SportsTeam, std::string, Name),
		MONETA_MEMBER(SportsTeam, std::vector<std::string>, Tags)
	> fake_path;

	BOOST_CHECK_EQUAL((path_tabs<fake_path      >()), "\t\t\t\t\t\t\t\t\t\t\t"); // 7 + 4 = 11 tabs
	BOOST_CHECK_EQUAL((path_tabs<fake_path,   -1>()), "\t\t\t\t\t\t\t\t\t\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path,   -2>()), "\t\t\t\t\t\t\t\t\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path,   -3>()), "\t\t\t\t\t\t\t\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path,   -4>()), "\t\t\t\t\t\t\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path,   -5>()), "\t\t\t\t\t\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path,   -6>()), "\t\t\t\t\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path,   -7>()), "\t\t\t\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path,   -8>()), "\t\t\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path,   -9>()), "\t\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path,  -10>()), "\t");
	BOOST_CHECK_EQUAL((path_tabs<fake_path, -100>()), "");
	BOOST_CHECK_EQUAL((path_tabs<fake_path, -999>()), "");
}

ext: cxx

max_stars_repo_name: madera/Moneta

max_issues_repo_name: madera/Moneta

max_forks_repo_name: madera/Moneta


text:
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <vector>

class UDPServer
{
public:
  /// Creates a UDP socket and runs it on the specified port
  /// port is in normal endianness (no ntohs)
  UDPServer(unsigned short port);
  /// Runs the Server
  void Run();

private:
  void HandleReceiveFrom(const boost::system::error_code &error, size_t bytesReceived);
  void HandleSendTo([[maybe_unused]] const boost::system::error_code &error,
                    [[maybe_unused]] size_t bytesSent);

  boost::asio::io_service io_service_;
  boost::asio::ip::udp::socket socket_;
  boost::asio::ip::udp::endpoint sender_endpoint_;
  static constexpr int maxLength = 1024;
  char data_[maxLength];
  /// This is the list of every clients that sent something
  /// to the server at one point in time (UDP has no handshake)
  std::vector<boost::asio::ip::udp::endpoint> clients;
};

max_stars_repo_name: PhilippeDeSousa/EpitechBundle

max_issues_repo_name: PhilippeDeSousa/EpitechBundle

max_forks_repo_name: PhilippeDeSousa/EpitechBundle


text:
#include "logit_sb_mixing.h"

#include <google/protobuf/stubs/casts.h>

#include <Eigen/Dense>
#include <memory>
#include <numeric>
#include <stan/math/prim.hpp>
#include <vector>

#include "mixing_prior.pb.h"
#include "mixing_state.pb.h"
#include "src/hierarchies/abstract_hierarchy.h"
#include "src/utils/proto_utils.h"
#include "src/utils/rng.h"

void LogitSBMixing::initialize() {
  if (prior == nullptr) {
    throw std::invalid_argument("Mixing prior was not provided");
  }
  auto priorcast = cast_prior();
  num_components = priorcast->num_components();
  initialize_state();
  acceptance_rates = Eigen::VectorXd::Zero(num_components - 1);
  n_iter = 0;
}

void LogitSBMixing::initialize_state() {
  auto priorcast = cast_prior();
  if (priorcast->has_normal_prior()) {
    Eigen::VectorXd prior_vec =
        bayesmix::to_eigen(priorcast->normal_prior().mean());
    dim = prior_vec.size();
    state.precision = stan::math::inverse_spd(
        bayesmix::to_eigen(priorcast->normal_prior().var()));
    if (dim != state.precision.cols()) {
      throw std::invalid_argument(
          "Hyperparameters dimensions are not consisent");
    }
    if (priorcast->step_size() <= 0) {
      throw std::invalid_argument("Step size parameter must be > 0");
    }

    state.regression_coeffs = Eigen::MatrixXd(dim, num_components - 1);
    for (int i = 0; i < num_components - 1; i++) {
      state.regression_coeffs.col(i) = prior_vec;
    }

  } else {
    throw std::invalid_argument("Unrecognized mixing prior");
  }
}

double LogitSBMixing::full_cond_lpdf(
    const Eigen::VectorXd &alpha, const unsigned int clust,
    const std::vector<unsigned int> &allocations) {
  auto priorcast = cast_prior();
  Eigen::VectorXd prior_mean =
      bayesmix::to_eigen(priorcast->normal_prior().mean());
  double like = -0.5 * ((alpha - prior_mean).transpose() * state.precision *
                        (alpha - prior_mean))(0);
  for (int i = 0; i < allocations.size(); i++) {
    if (allocations[i] >= clust) {
      double is_curr_clus = (allocations[i] == clust);
      double prob = sigmoid(covariates_ptr->row(i).dot(alpha));
      like += is_curr_clus * std::log(prob) +
              (1.0 - is_curr_clus) * std::log(1.0 - prob);
    }
  }
  return like;
}

Eigen::VectorXd LogitSBMixing::grad_full_cond_lpdf(
    const Eigen::VectorXd &alpha, const unsigned int clust,
    const std::vector<unsigned int> &allocations) {
  auto priorcast = cast_prior();
  Eigen::VectorXd prior_mean =
      bayesmix::to_eigen(priorcast->normal_prior().mean());
  Eigen::VectorXd grad = state.precision * (prior_mean - alpha);
  for (int i = 0; i < allocations.size(); i++) {
    if (allocations[i] >= clust) {
      double is_curr_clus = (allocations[i] == clust);
      double prob = sigmoid(covariates_ptr->row(i).dot(alpha));
      grad += (is_curr_clus - prob) * covariates_ptr->row(i);
    }
  }
  return grad;
}

void LogitSBMixing::update_state(
    const std::vector<std::shared_ptr<AbstractHierarchy>> &unique_values,
    const std::vector<unsigned int> &allocations) {
  n_iter += 1;
  // Langevin-Adjusted Metropolis-Hastings step
  unsigned int n = allocations.size();
  auto &rng = bayesmix::Rng::Instance().get();
  auto priorcast = cast_prior();
  Eigen::VectorXd prior_mean =
      bayesmix::to_eigen(priorcast->normal_prior().mean());
  double step = priorcast->step_size();
  double prop_var = std::sqrt(2.0 * step);
  // Loop over components
  for (int h = 0; h < num_components - 1; h++) {
    Eigen::VectorXd state_c = state.regression_coeffs.col(h);
    // Draw proposed state from its distribution
    Eigen::VectorXd prop_mean =
        state_c + step * grad_full_cond_lpdf(state_c, h, allocations);
    auto prop_covar = prop_var * Eigen::MatrixXd::Identity(dim, dim);
    Eigen::VectorXd state_prop =
        stan::math::multi_normal_rng(prop_mean, prop_covar, rng);
    // Compute acceptance ratio
    double full_cond_ratio = full_cond_lpdf(state_prop, h, allocations) -
                             full_cond_lpdf(state_c, h, allocations);
    double prop_ratio = (-0.5 / prop_var) *
                        ((state_prop - prop_mean).dot(state_prop - prop_mean) -
                         (state_c - prop_mean).dot(state_c - prop_mean));
    double log_accept_ratio = full_cond_ratio - prop_ratio;
    // Accept with probability ratio
    double p = stan::math::uniform_rng(0.0, 1.0, rng);
    if (p < std::exp(log_accept_ratio)) {
      state.regression_coeffs.col(h) = state_prop;
      acceptance_rates(h) += 1;
    }
  }
}

void LogitSBMixing::set_state_from_proto(
    const google::protobuf::Message &state_) {
  auto &statecast =
      google::protobuf::internal::down_cast<const bayesmix::MixingState &>(
          state_);
  state.regression_coeffs =
      bayesmix::to_eigen(statecast.log_sb_state().regression_coeffs());
}

void LogitSBMixing::write_state_to_proto(
    google::protobuf::Message *out) const {
  bayesmix::LogSBState state_;
  bayesmix::to_proto(state.regression_coeffs,
                     state_.mutable_regression_coeffs());
  google::protobuf::internal::down_cast<bayesmix::MixingState *>(out)
      ->mutable_log_sb_state()
      ->CopyFrom(state_);
}

Eigen::VectorXd LogitSBMixing::get_weights(
    const bool log, const bool propto,
    const Eigen::RowVectorXd &covariate /*= Eigen::RowVectorXd(0)*/) const {
  // Compute eta
  std::vector<double> eta(num_components);
  for (int h = 0; h < num_components - 1; h++) {
    eta[h] = covariate.dot(state.regression_coeffs.col(h));
  }
  eta[num_components - 1] = 1.0;
  // Compute cumulative sums of logarithms
  std::vector<double> cumsum(num_components + 1, 0.0);
  for (int h = 1; h < num_components + 1; h++) {
    cumsum[h] = cumsum[h - 1] + std::log(sigmoid(-eta[h - 1]));
  }
  // Compute weights
  Eigen::VectorXd logweights(num_components);
  for (int h = 0; h < num_components; h++) {
    logweights(h) = std::log(sigmoid(eta[h])) + cumsum[h];
  }
  if (log) {
    return logweights;
  } else {
    return logweights.array().exp();
  }
}

ext: cc

max_stars_repo_name: bayesmix-dev/bayesmix

max_issues_repo_name: bayesmix-dev/bayesmix

max_forks_repo_name: bayesmix-dev/bayesmix


text:
/*ckwg +29
 * Copyright 2015-2017 by Kitware, Inc.
 * 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.
 *
 *  * Neither name of Kitware, Inc. nor the names of any contributors may be used
 *    to endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * 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 AUTHORS 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
 * \brief Homography and derived class functionality regression tests
 */

#include <test_eigen.h>

#include <vital/exceptions/math.h>
#include <vital/types/homography.h>
#include <vital/types/homography_f2f.h>

#include <Eigen/LU>

// ----------------------------------------------------------------------------
int main(int argc, char** argv)
{
  ::testing::InitGoogleTest( &argc, argv );
  return RUN_ALL_TESTS();
}

// ----------------------------------------------------------------------------
template <typename T>
class homography : public ::testing::Test
{
};

using types = ::testing::Types<float, double>;
TYPED_TEST_CASE(homography, types);

// ----------------------------------------------------------------------------
// Test invert function Eigen::Matrix derived classes
TYPED_TEST(homography, inversion)
{
  using homography_t = typename kwiver::vital::homography_<TypeParam>;

  homography_t invertible;
  homography_t expected_result;
  homography_t noninvertable;
  homography_t ni_result;

  invertible.get_matrix() << 1, 1, 2,
                             3, 4, 5,
                             6, 7, 9;
  expected_result.get_matrix() << -0.5, -2.5,  1.5,
                                  -1.5,  1.5, -0.5,
                                   1.5,  0.5, -0.5;

  homography_t h_inverse( *invertible.inverse() );
  EXPECT_MATRIX_EQ( expected_result.matrix(), h_inverse.matrix() );

  noninvertable.get_matrix() << 1, 2, 3,
                                4, 5, 6,
                                7, 8, 9;
  bool is_invertible;
  noninvertable.get_matrix().computeInverseWithCheck(
    ni_result.get_matrix(), is_invertible );
  EXPECT_FALSE( is_invertible );
}

// ----------------------------------------------------------------------------
// Test mapping a point for a homography/point data type
TYPED_TEST(homography, map_point_zero_div)
{
  using homography_t = typename kwiver::vital::homography_<TypeParam>;
  using vector_t = typename Eigen::Matrix<TypeParam, 2, 1>;

  vector_t test_p{ 1, 1 };
  TypeParam e = Eigen::NumTraits<TypeParam>::dummy_precision();

  // Where [2,2] = 0
  homography_t h_0;
  h_0.get_matrix() << 1.0, 0.0, 1.0,
                      0.0, 1.0, 1.0,
                      0.0, 0.0, 0.0;
  EXPECT_THROW(
    h_0.map_point( test_p ),
    kwiver::vital::point_maps_to_infinity )
    << "Applying point to matrix with 0-value lower-right corner";

  // Where [2,2] = e, which is the approximately-zero threshold
  homography_t h_e;
  h_e.get_matrix() << 1.0, 0.0, 1.0,
                      0.0, 1.0, 1.0,
                      0.0, 0.0,  e ;
  EXPECT_THROW(
    h_e.map_point( test_p ),
    kwiver::vital::point_maps_to_infinity )
    << "Applying point to matrix with e-value lower-right corner";

  // Where [2,2] = 0.5, which should be valid.
  homography_t h_half;
  h_half.get_matrix() << 1.0, 0.0, 1.0,
                         0.0, 1.0, 1.0,
                         0.0, 0.0, 0.5;
  EXPECT_MATRIX_NEAR( ( vector_t{ 4, 4 } ), h_half.map_point( test_p ), e );
}

// ----------------------------------------------------------------------------
TYPED_TEST(homography, map_point)
{
  using homography_t = typename kwiver::vital::homography_<TypeParam>;
  using vector_t = typename Eigen::Matrix<TypeParam, 2, 1>;

  // Identity transformation
  homography_t h;
  vector_t p{ static_cast<TypeParam>( 2.2 ), static_cast<TypeParam>( 5.5 ) };
  EXPECT_EQ( p, h.map_point( p ) );
}

// ----------------------------------------------------------------------------
TEST(f2f_homography, inversion)
{
  // Testing from and to frame swapping during inversion
  kwiver::vital::matrix_3x3d i{ kwiver::vital::matrix_3x3d::Identity() };
  kwiver::vital::f2f_homography h{ i, 0, 10 };
  kwiver::vital::f2f_homography h_inv = h.inverse();

  EXPECT_EQ( 10, h_inv.from_id() );
  EXPECT_EQ( 0, h_inv.to_id() );
}

max_stars_repo_name: neal-siekierski/kwiver

max_issues_repo_name: neal-siekierski/kwiver

max_forks_repo_name: neal-siekierski/kwiver


text:
//  Copyright Neil Groves 2010. Use, modification and
//  distribution is subject to the Boost Software License, Version
//  1.0. (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//
//
// For more information, see http://www.boost.org/libs/range/
//
#ifndef BOOST_RANGE_ISTREAM_RANGE_HPP_INCLUDED
#define BOOST_RANGE_ISTREAM_RANGE_HPP_INCLUDED

/*!
 * \file istream_range.hpp
 */

#include <iterator>
#include <istream>
#include <boost/config.hpp>
#include <boost/range/iterator_range.hpp>

namespace boost
{
    namespace range
    {
        template<class Type, class Elem, class Traits> inline
            iterator_range<std::istream_iterator<Type, Elem, Traits> >
        istream_range(std::basic_istream<Elem, Traits>& in)
        {
            return iterator_range<std::istream_iterator<Type, Elem, Traits> >(
                std::istream_iterator<Type>(in),
                std::istream_iterator<Type>());
        }
    } // namespace range
    using range::istream_range;
} // namespace boost

#endif // include guard

max_stars_repo_name: importlib/klib

max_issues_repo_name: isuhao/klib

max_forks_repo_name: isuhao/klib