<commit_before>// Copyright (c) 2011-2015 The Bitcoin Core developers
// Copyright (c) 2015-2017 The Bitcoin Unlimited developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "transactionrecord.h"

#include "consensus/consensus.h"
#include "dstencode.h"
#include "main.h"
#include "timedata.h"

#include <stdint.h>

#include <boost/foreach.hpp>

/* Return positive answer if transaction should be shown in list.
 */
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
    if (wtx.IsCoinBase())
    {
        // Ensures we show generated coins / mined transactions at depth 1
        if (!wtx.IsInMainChain())
        {
            return false;
        }
    }
    return true;
}

/*
 * Decompose CWallet transaction to model transaction records.
 */
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
    QList<TransactionRecord> parts;
    int64_t nTime = wtx.GetTxTime();
    CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
    CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
    CAmount nNet = nCredit - nDebit;
    uint256 hash = wtx.GetHash();
    std::map<std::string, std::string> mapValue = wtx.mapValue;

    if (nNet > 0 || wtx.IsCoinBase())
    {
        //
        // Credit
        //
        std::string labelPublic = "";
        BOOST_FOREACH (const CTxOut &txout, wtx.vout)
        {
            isminetype mine = wallet->IsMine(txout);
            if (mine)
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number
                sub.credit = txout.nValue;
                sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;

                // the public label refers to the following utxo
                if (labelPublic == "")
                {
                    labelPublic = getLabelPublic(txout.scriptPubKey);
                    if (labelPublic != "")
                        continue;
                }

                if (wtx.IsCoinBase())
                {
                    // Generated
                    sub.type = TransactionRecord::Generated;
                }
                else if (ExtractDestination(txout.scriptPubKey, address) && wallet->IsMine(address))
                {
                    // Received by Bitcoin Address
                    sub.type = TransactionRecord::RecvWithAddress;
                    if (labelPublic == "")
                        sub.addresses.push_back(std::make_pair(EncodeDestination(address), txout.scriptPubKey));
                    else
                        sub.addresses.push_back(
                            std::make_pair("<" + labelPublic + "> " + EncodeDestination(address), txout.scriptPubKey));
                }
                else
                {
                    // Received by IP connection (deprecated features), or a multisignature or other non-simple
                    // transaction
                    sub.type = TransactionRecord::RecvFromOther;
                    sub.addresses.push_back(std::make_pair(mapValue["from"], txout.scriptPubKey));
                }

                parts.append(sub);
            }

            labelPublic = "";
        }
    }
    else
    {
        bool involvesWatchAddress = false;
        isminetype fAllToMe = ISMINE_SPENDABLE;
        BOOST_FOREACH (const CTxOut &txout, wtx.vout)
        {
            isminetype mine = wallet->IsMine(txout);
            if (mine & ISMINE_WATCH_ONLY)
                involvesWatchAddress = true;
            if (fAllToMe > mine)
                fAllToMe = mine;
        }

        // load all tx addresses for user display/filter
        AddressList listAllAddresses;
        CTxDestination address;
        BOOST_FOREACH (const CTxOut &txout, wtx.vout)
        {
            if (!fAllToMe && wallet->IsMine(txout))
            {
                continue; // skip change if some tx went to somewhere else
            }
            // get public label if it exists
            std::string labelPublic = getLabelPublic(txout.scriptPubKey);
            if (labelPublic != "")
                // use public label before address
                listAllAddresses.push_back(std::make_pair("<" + labelPublic + ">", txout.scriptPubKey));
            else if (ExtractDestination(txout.scriptPubKey, address))
                // a standard address
                listAllAddresses.push_back(std::make_pair(EncodeDestination(address), txout.scriptPubKey));

            else
                // add the unknown scriptPubKey as n/a - TODO could also skip these if there is no need to
                // display/filter??
                listAllAddresses.push_back(std::make_pair("n/a", txout.scriptPubKey));
        }

        isminetype fAllFromMe = ISMINE_SPENDABLE;
        BOOST_FOREACH (const CTxIn &txin, wtx.vin)
        {
            isminetype mine = wallet->IsMine(txin);
            if (mine & ISMINE_WATCH_ONLY)
                involvesWatchAddress = true;
            if (fAllFromMe > mine)
                fAllFromMe = mine;
        }

        if (fAllFromMe && fAllToMe)
        {
            // Payment to self
            CAmount nChange = wtx.GetChange();
            parts.append(TransactionRecord(
                hash, nTime, TransactionRecord::SendToSelf, listAllAddresses, -(nDebit - nChange), nCredit - nChange));

            // maybe pass to TransactionRecord as constructor argument
            parts.last().involvesWatchAddress = involvesWatchAddress;
        }
        else if (fAllFromMe)
        {
            //
            // Debit
            //
            CAmount nTxFee = nDebit - wtx.GetValueOut();

            for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
            {
                const CTxOut &txout = wtx.vout[nOut];

                if (wallet->IsMine(txout))
                {
                    // Ignore parts sent to self, as this is usually the change
                    // from a transaction sent back to our own address.
                    continue;
                }

                TransactionRecord sub(hash, nTime);
                sub.idx = parts.size();
                sub.involvesWatchAddress = involvesWatchAddress;

                CTxDestination address;
                std::string labelPublic = getLabelPublic(txout.scriptPubKey);
                if (labelPublic != "")
                    continue;
                else if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Sent to Bitcoin Address
                    sub.type = TransactionRecord::SendToAddress;
                    // sub.addresses.push_back(std::make_pair(EncodeDestination(address), txout.scriptPubKey));
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.type = TransactionRecord::SendToOther;
                    // sub.addresses.push_back(std::make_pair(mapValue["to"], txout.scriptPubKey));
                }

                sub.addresses = listAllAddresses;

                CAmount nValue = txout.nValue;
                /* Add fee to first output */
                if (nTxFee > 0)
                {
                    nValue += nTxFee;
                    nTxFee = 0;
                }
                sub.debit = -nValue;

                parts.append(sub);
            }
        }
        else
        {
            //
            // Mixed debit transaction, can't break down payees
            //
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, listAllAddresses, nNet, 0));
            parts.last().involvesWatchAddress = involvesWatchAddress;
        }
    }

    return parts;
}

void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
    AssertLockHeld(cs_main);
    // Determine transaction status

    // Find the block the tx is in
    CBlockIndex *pindex = NULL;
    BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock);
    if (mi != mapBlockIndex.end())
        pindex = (*mi).second;

    // Sort order, unrecorded transactions sort to the top
    status.sortKey = strprintf("%010d-%01d-%010u-%03d", (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
        (wtx.IsCoinBase() ? 1 : 0), wtx.nTimeReceived, idx);
    status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
    status.depth = wtx.GetDepthInMainChain();
    status.cur_num_blocks = chainActive.Height();

    if (!CheckFinalTx(wtx))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
        {
            status.status = TransactionStatus::OpenUntilBlock;
            status.open_for = wtx.nLockTime - chainActive.Height();
        }
        else
        {
            status.status = TransactionStatus::OpenUntilDate;
            status.open_for = wtx.nLockTime;
        }
    }
    // For generated transactions, determine maturity
    else if (type == TransactionRecord::Generated)
    {
        if (wtx.GetBlocksToMaturity() > 0)
        {
            status.status = TransactionStatus::Immature;

            if (wtx.IsInMainChain())
            {
                status.matures_in = wtx.GetBlocksToMaturity();

                // Check if the block was requested by anyone
                if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
                    status.status = TransactionStatus::MaturesWarning;
            }
            else
            {
                status.status = TransactionStatus::NotAccepted;
            }
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
    else
    {
        if (status.depth < 0)
        {
            status.status = TransactionStatus::Conflicted;
        }
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
        {
            status.status = TransactionStatus::Offline;
        }
        else if (status.depth == 0)
        {
            status.status = TransactionStatus::Unconfirmed;
        }
        else if (status.depth < RecommendedNumConfirmations)
        {
            status.status = TransactionStatus::Confirming;
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
}

bool TransactionRecord::statusUpdateNeeded()
{
    AssertLockHeld(cs_main);
    return status.cur_num_blocks != chainActive.Height();
}

QString TransactionRecord::getTxID() const
{
    return QString::fromStdString(hash.ToString());
}

int TransactionRecord::getOutputIndex() const
{
    return idx;
}
<commit_msg>fix formatting<commit_after>// Copyright (c) 2011-2015 The Bitcoin Core developers
// Copyright (c) 2015-2017 The Bitcoin Unlimited developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "transactionrecord.h"

#include "consensus/consensus.h"
#include "dstencode.h"
#include "main.h"
#include "timedata.h"

#include <stdint.h>

#include <boost/foreach.hpp>

/* Return positive answer if transaction should be shown in list.
 */
bool TransactionRecord::showTransaction(const CWalletTx &wtx)
{
    if (wtx.IsCoinBase())
    {
        // Ensures we show generated coins / mined transactions at depth 1
        if (!wtx.IsInMainChain())
        {
            return false;
        }
    }
    return true;
}

/*
 * Decompose CWallet transaction to model transaction records.
 */
QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
{
    QList<TransactionRecord> parts;
    int64_t nTime = wtx.GetTxTime();
    CAmount nCredit = wtx.GetCredit(ISMINE_ALL);
    CAmount nDebit = wtx.GetDebit(ISMINE_ALL);
    CAmount nNet = nCredit - nDebit;
    uint256 hash = wtx.GetHash();
    std::map<std::string, std::string> mapValue = wtx.mapValue;

    if (nNet > 0 || wtx.IsCoinBase())
    {
        //
        // Credit
        //
        std::string labelPublic = "";
        BOOST_FOREACH (const CTxOut &txout, wtx.vout)
        {
            isminetype mine = wallet->IsMine(txout);
            if (mine)
            {
                TransactionRecord sub(hash, nTime);
                CTxDestination address;
                sub.idx = parts.size(); // sequence number
                sub.credit = txout.nValue;
                sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;

                // the public label refers to the following utxo
                if (labelPublic == "")
                {
                    labelPublic = getLabelPublic(txout.scriptPubKey);
                    if (labelPublic != "")
                        continue;
                }

                if (wtx.IsCoinBase())
                {
                    // Generated
                    sub.type = TransactionRecord::Generated;
                }
                else if (ExtractDestination(txout.scriptPubKey, address) && wallet->IsMine(address))
                {
                    // Received by Bitcoin Address
                    sub.type = TransactionRecord::RecvWithAddress;
                    if (labelPublic == "")
                        sub.addresses.push_back(std::make_pair(EncodeDestination(address), txout.scriptPubKey));
                    else
                        sub.addresses.push_back(
                            std::make_pair("<" + labelPublic + "> " + EncodeDestination(address), txout.scriptPubKey));
                }
                else
                {
                    // Received by IP connection (deprecated features), or a multisignature or other non-simple
                    // transaction
                    sub.type = TransactionRecord::RecvFromOther;
                    sub.addresses.push_back(std::make_pair(mapValue["from"], txout.scriptPubKey));
                }

                parts.append(sub);
            }

            labelPublic = "";
        }
    }
    else
    {
        bool involvesWatchAddress = false;
        isminetype fAllToMe = ISMINE_SPENDABLE;
        BOOST_FOREACH (const CTxOut &txout, wtx.vout)
        {
            isminetype mine = wallet->IsMine(txout);
            if (mine & ISMINE_WATCH_ONLY)
                involvesWatchAddress = true;
            if (fAllToMe > mine)
                fAllToMe = mine;
        }

        // load all tx addresses for user display/filter
        AddressList listAllAddresses;
        CTxDestination address;
        BOOST_FOREACH (const CTxOut &txout, wtx.vout)
        {
            if (!fAllToMe && wallet->IsMine(txout))
            {
                continue; // skip change if some tx went to somewhere else
            }
            // get public label if it exists
            std::string labelPublic = getLabelPublic(txout.scriptPubKey);
            if (labelPublic != "")
                // use public label before address
                listAllAddresses.push_back(std::make_pair("<" + labelPublic + ">", txout.scriptPubKey));
            else if (ExtractDestination(txout.scriptPubKey, address))
                // a standard address
                listAllAddresses.push_back(std::make_pair(EncodeDestination(address), txout.scriptPubKey));

            else
                // add the unknown scriptPubKey as n/a - TODO could also skip these if there is no need to
                // display/filter??
                listAllAddresses.push_back(std::make_pair("n/a", txout.scriptPubKey));
        }

        isminetype fAllFromMe = ISMINE_SPENDABLE;
        BOOST_FOREACH (const CTxIn &txin, wtx.vin)
        {
            isminetype mine = wallet->IsMine(txin);
            if (mine & ISMINE_WATCH_ONLY)
                involvesWatchAddress = true;
            if (fAllFromMe > mine)
                fAllFromMe = mine;
        }

        if (fAllFromMe && fAllToMe)
        {
            // Payment to self
            CAmount nChange = wtx.GetChange();
            parts.append(TransactionRecord(
                hash, nTime, TransactionRecord::SendToSelf, listAllAddresses, -(nDebit - nChange), nCredit - nChange));

            // maybe pass to TransactionRecord as constructor argument
            parts.last().involvesWatchAddress = involvesWatchAddress;
        }
        else if (fAllFromMe)
        {
            //
            // Debit
            //
            CAmount nTxFee = nDebit - wtx.GetValueOut();

            for (unsigned int nOut = 0; nOut < wtx.vout.size(); nOut++)
            {
                const CTxOut &txout = wtx.vout[nOut];

                if (wallet->IsMine(txout))
                {
                    // Ignore parts sent to self, as this is usually the change
                    // from a transaction sent back to our own address.
                    continue;
                }

                TransactionRecord sub(hash, nTime);
                sub.idx = parts.size();
                sub.involvesWatchAddress = involvesWatchAddress;

                CTxDestination address;
                std::string labelPublic = getLabelPublic(txout.scriptPubKey);
                if (labelPublic != "")
                    continue;
                else if (ExtractDestination(txout.scriptPubKey, address))
                {
                    // Sent to Bitcoin Address
                    sub.type = TransactionRecord::SendToAddress;
                    // sub.addresses.push_back(std::make_pair(EncodeDestination(address), txout.scriptPubKey));
                }
                else
                {
                    // Sent to IP, or other non-address transaction like OP_EVAL
                    sub.type = TransactionRecord::SendToOther;
                    // sub.addresses.push_back(std::make_pair(mapValue["to"], txout.scriptPubKey));
                }

                sub.addresses = listAllAddresses;

                CAmount nValue = txout.nValue;
                /* Add fee to first output */
                if (nTxFee > 0)
                {
                    nValue += nTxFee;
                    nTxFee = 0;
                }
                sub.debit = -nValue;

                parts.append(sub);
            }
        }
        else
        {
            //
            // Mixed debit transaction, can't break down payees
            //
            parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, listAllAddresses, nNet, 0));
            parts.last().involvesWatchAddress = involvesWatchAddress;
        }
    }

    return parts;
}

void TransactionRecord::updateStatus(const CWalletTx &wtx)
{
    AssertLockHeld(cs_main);
    // Determine transaction status

    // Find the block the tx is in
    CBlockIndex *pindex = NULL;
    BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock);
    if (mi != mapBlockIndex.end())
        pindex = (*mi).second;

    // Sort order, unrecorded transactions sort to the top
    status.sortKey = strprintf("%010d-%01d-%010u-%03d", (pindex ? pindex->nHeight : std::numeric_limits<int>::max()),
        (wtx.IsCoinBase() ? 1 : 0), wtx.nTimeReceived, idx);
    status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0);
    status.depth = wtx.GetDepthInMainChain();
    status.cur_num_blocks = chainActive.Height();

    if (!CheckFinalTx(wtx))
    {
        if (wtx.nLockTime < LOCKTIME_THRESHOLD)
        {
            status.status = TransactionStatus::OpenUntilBlock;
            status.open_for = wtx.nLockTime - chainActive.Height();
        }
        else
        {
            status.status = TransactionStatus::OpenUntilDate;
            status.open_for = wtx.nLockTime;
        }
    }
    // For generated transactions, determine maturity
    else if (type == TransactionRecord::Generated)
    {
        if (wtx.GetBlocksToMaturity() > 0)
        {
            status.status = TransactionStatus::Immature;

            if (wtx.IsInMainChain())
            {
                status.matures_in = wtx.GetBlocksToMaturity();

                // Check if the block was requested by anyone
                if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
                    status.status = TransactionStatus::MaturesWarning;
            }
            else
            {
                status.status = TransactionStatus::NotAccepted;
            }
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
    else
    {
        if (status.depth < 0)
        {
            status.status = TransactionStatus::Conflicted;
        }
        else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
        {
            status.status = TransactionStatus::Offline;
        }
        else if (status.depth == 0)
        {
            status.status = TransactionStatus::Unconfirmed;
        }
        else if (status.depth < RecommendedNumConfirmations)
        {
            status.status = TransactionStatus::Confirming;
        }
        else
        {
            status.status = TransactionStatus::Confirmed;
        }
    }
}

bool TransactionRecord::statusUpdateNeeded()
{
    AssertLockHeld(cs_main);
    return status.cur_num_blocks != chainActive.Height();
}

QString TransactionRecord::getTxID() const { return QString::fromStdString(hash.ToString()); }
int TransactionRecord::getOutputIndex() const { return idx; }
<|endoftext|>


<commit_before>/*ckwg +5
 * Copyright 2012 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "read_video_gravl_process.h"

#include <processes/helpers/image/format.h>
#include <processes/helpers/image/read.h>

#include <vistk/pipeline/config.h>
#include <vistk/pipeline/datum.h>
#include <vistk/pipeline/process_exception.h>

#include <gravl/core/api/data_block.h>
#include <gravl/core/api/frame_ptr.h>
#include <gravl/core/api/image.h>
#include <gravl/core/api/image_data.h>
#include <gravl/core/api/resource_ptr.h>

#include <gravl/raf/raf.h>

#include <vil/vil_image_view.h>

#include <string>

/**
 * \file read_video_gravl_process.cxx
 *
 * \brief Implementation of the read video gravl process.
 */

static size_t compute_block_size(gravl::image::dimension dim,
                                 gravl::image::stride s);
template <typename T> static T* compute_block_start(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s);
template <typename T> static T* compute_top_left(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s);

namespace vistk
{

class read_video_gravl_process::priv
{
  public:
    priv(std::string const& input_uri);
    ~priv();

    std::string const uri;

    gravl::resource_ptr resource;
    gravl::data_block* video;

    static config::key_t const config_pixtype;
    static config::key_t const config_pixfmt;
    static config::key_t const config_uri;
    static config::key_t const config_verify;
    static config::value_t const default_pixtype;
    static config::value_t const default_pixfmt;
    static config::value_t const default_verify;
    static port_t const port_output;
};

config::key_t const read_video_gravl_process::priv::config_pixtype = config::key_t("pixtype");
config::key_t const read_video_gravl_process::priv::config_pixfmt = config::key_t("pixfmt");
config::key_t const read_video_gravl_process::priv::config_uri = config::key_t("input");
config::value_t const read_video_gravl_process::priv::default_pixtype = config::value_t(pixtypes::pixtype_byte());
config::value_t const read_video_gravl_process::priv::default_pixfmt = config::value_t(pixfmts::pixfmt_rgb());
process::port_t const read_video_gravl_process::priv::port_output = port_t("image");

read_video_gravl_process
::read_video_gravl_process(config_t const& config)
  : process(config)
{
  declare_configuration_key(
    priv::config_pixtype,
    priv::default_pixtype,
    config::description_t("The pixel type of the input images."));
  declare_configuration_key(
    priv::config_pixfmt,
    priv::default_pixfmt,
    config::description_t("The pixel format of the input images."));
  declare_configuration_key(
    priv::config_uri,
    config::value_t(),
    config::description_t("The URI of the resource."));

  pixtype_t const pixtype = config_value<pixtype_t>(priv::config_pixtype);
  pixfmt_t const pixfmt = config_value<pixfmt_t>(priv::config_pixfmt);

  port_type_t const port_type_output = port_type_for_pixtype(pixtype, pixfmt);

  port_flags_t required;

  required.insert(flag_required);

  declare_output_port(
    priv::port_output,
    port_type_output,
    required,
    port_description_t("The images that are read in."));
}

read_video_gravl_process
::~read_video_gravl_process()
{
}

void
read_video_gravl_process
::_configure()
{
  // Configure the process.
  {
    pixtype_t const pixtype = config_value<pixtype_t>(priv::config_pixtype);
    std::string const uri = config_value<std::string>(priv::config_uri);

    d.reset(new priv(uri));
  }

  if (d->uri.empty())
  {
    static std::string const reason = "The URI given was empty";
    config::value_t const value = config::value_t(d->uri);

    throw invalid_configuration_value_exception(name(), priv::config_uri, value, reason);
  }

  d->resource = gravl::raf::get_resource(d->uri.c_str());
  if (!d->resource)
  {
    std::string const reason = "Failed to open the resource: " + d->uri;

    throw invalid_configuration_exception(name(), reason);
  }

  d->video = d->resource.get_data<gravl::data_block>();
  if (!d->video)
  {
    static std::string const reason = "Failed to obtain data_block from resource";

    throw invalid_configuration_exception(name(), reason);
  }

  process::_configure();
}

void
read_video_gravl_process
::_init()
{
  d->video->rewind();

  process::_init();
}

void
read_video_gravl_process
::_step()
{
  datum_t dat;

  if (d->video->at_end())
  {
    mark_process_as_complete();
    dat = datum::complete_datum();
  }
  else if (!d->video)
  {
    static datum::error_t const err_string = datum::error_t("Error with input file stream");

    dat = datum::error_datum(err_string);
  }
  else
  {
    gravl::frame_ptr const frame = d->video->current_frame();
    gravl::image_data const* const image_data = frame.get_const_data<gravl::image_data>();
    gravl::image const image = (image_data ? image_data->pixels() : gravl::image());

    if (!image || image.format() != gravl::image::format_of<uint8_t>())
    {
      dat = datum::empty_datum();
    }
    else
    {
      gravl::image::dimension const dim = image.dimensions();
      gravl::image::stride const ps = image.strides();
      uint8_t const* const top_left = image.data<uint8_t>();
      size_t const size = compute_block_size(dim, ps);

      // Ugh, there is no way to create a vil_image_view from existing data
      // without arranging for said existing data to stick around, so stuck
      // having to copy the data (again) :-(
      vil_memory_chunk* const mem = new vil_memory_chunk(size, VIL_PIXEL_FORMAT_BYTE);
      uint8_t* const buffer = reinterpret_cast<uint8_t*>(mem->data());
      memcpy(buffer, compute_block_start(top_left, dim, ps), size);

      vil_image_view<vxl_byte> const vil(vil_memory_chunk_sptr(mem),
                                         compute_top_left(buffer, dim, ps),
                                         dim.width, dim.height, dim.planes,
                                         ps.width, ps.height, ps.planes);
      dat = datum::new_datum(vil);
    }

    d->video->advance();
  }

  push_datum_to_port(priv::port_output, dat);

  process::_step();
}

read_video_gravl_process::priv
::priv(std::string const& input_uri)
  : uri(input_uri)
{
}

read_video_gravl_process::priv
::~priv()
{
}

}

size_t
sabs(ptrdiff_t a)
{
  return static_cast<size_t>((a < 0 ? -a : a));
}

size_t
compute_block_size(gravl::image::dimension dim, gravl::image::stride s)
{
  return ((dim.width  - 1) * sabs(s.width)) +
         ((dim.height - 1) * sabs(s.height)) +
         ((dim.planes - 1) * sabs(s.planes)) + 1;
}

ptrdiff_t
compute_offset(gravl::image::dimension dim, gravl::image::stride s)
{
  ptrdiff_t result = 0;
  if (s.width < 0)
  {
    result -= s.width * (dim.width - 1);
  }
  if (s.height < 0)
  {
    result -= s.height * (dim.height - 1);
  }
  if (s.planes < 0)
  {
    result -= s.planes * (dim.planes - 1);
  }
  return result;
}

template <typename T>
T*
compute_block_start(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s)
{
  return top_left - compute_offset(dim, s);
}

template <typename T>
T*
compute_top_left(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s)
{
  return top_left + compute_offset(dim, s);
}
<commit_msg>Fix pixel type handling in gravl reader<commit_after>/*ckwg +5
 * Copyright 2012 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "read_video_gravl_process.h"

#include <processes/helpers/image/format.h>
#include <processes/helpers/image/read.h>

#include <vistk/pipeline/config.h>
#include <vistk/pipeline/datum.h>
#include <vistk/pipeline/process_exception.h>

#include <gravl/core/api/data_block.h>
#include <gravl/core/api/frame_ptr.h>
#include <gravl/core/api/image.h>
#include <gravl/core/api/image_data.h>
#include <gravl/core/api/resource_ptr.h>

#include <gravl/raf/raf.h>

#include <vil/vil_image_view.h>

#include <string>

/**
 * \file read_video_gravl_process.cxx
 *
 * \brief Implementation of the read video gravl process.
 */

template <typename PixType> static vistk::datum_t convert_image(
  gravl::data_block* video, vistk::pixtype_t const& pixtype,
  vil_pixel_format pixfmt);

namespace vistk
{

class read_video_gravl_process::priv
{
  public:
    priv(std::string const& input_uri);
    ~priv();

    std::string const uri;

    gravl::resource_ptr resource;
    gravl::data_block* video;

    static config::key_t const config_pixtype;
    static config::key_t const config_pixfmt;
    static config::key_t const config_uri;
    static config::key_t const config_verify;
    static config::value_t const default_pixtype;
    static config::value_t const default_pixfmt;
    static config::value_t const default_verify;
    static port_t const port_output;
};

config::key_t const read_video_gravl_process::priv::config_pixtype = config::key_t("pixtype");
config::key_t const read_video_gravl_process::priv::config_pixfmt = config::key_t("pixfmt");
config::key_t const read_video_gravl_process::priv::config_uri = config::key_t("input");
config::value_t const read_video_gravl_process::priv::default_pixtype = config::value_t(pixtypes::pixtype_byte());
config::value_t const read_video_gravl_process::priv::default_pixfmt = config::value_t(pixfmts::pixfmt_rgb());
process::port_t const read_video_gravl_process::priv::port_output = port_t("image");

read_video_gravl_process
::read_video_gravl_process(config_t const& config)
  : process(config)
{
  declare_configuration_key(
    priv::config_pixtype,
    priv::default_pixtype,
    config::description_t("The pixel type of the input images."));
  declare_configuration_key(
    priv::config_pixfmt,
    priv::default_pixfmt,
    config::description_t("The pixel format of the input images."));
  declare_configuration_key(
    priv::config_uri,
    config::value_t(),
    config::description_t("The URI of the resource."));

  pixtype_t const pixtype = config_value<pixtype_t>(priv::config_pixtype);
  pixfmt_t const pixfmt = config_value<pixfmt_t>(priv::config_pixfmt);

  port_type_t const port_type_output = port_type_for_pixtype(pixtype, pixfmt);

  port_flags_t required;

  required.insert(flag_required);

  declare_output_port(
    priv::port_output,
    port_type_output,
    required,
    port_description_t("The images that are read in."));
}

read_video_gravl_process
::~read_video_gravl_process()
{
}

void
read_video_gravl_process
::_configure()
{
  // Configure the process.
  {
    pixtype_t const pixtype = config_value<pixtype_t>(priv::config_pixtype);
    std::string const uri = config_value<std::string>(priv::config_uri);

    d.reset(new priv(uri));
  }

  if (d->uri.empty())
  {
    static std::string const reason = "The URI given was empty";
    config::value_t const value = config::value_t(d->uri);

    throw invalid_configuration_value_exception(name(), priv::config_uri, value, reason);
  }

  d->resource = gravl::raf::get_resource(d->uri.c_str());
  if (!d->resource)
  {
    std::string const reason = "Failed to open the resource: " + d->uri;

    throw invalid_configuration_exception(name(), reason);
  }

  d->video = d->resource.get_data<gravl::data_block>();
  if (!d->video)
  {
    static std::string const reason = "Failed to obtain data_block from resource";

    throw invalid_configuration_exception(name(), reason);
  }

  process::_configure();
}

void
read_video_gravl_process
::_init()
{
  d->video->rewind();

  process::_init();
}

void
read_video_gravl_process
::_step()
{
  datum_t dat;

  if (d->video->at_end())
  {
    mark_process_as_complete();
    dat = datum::complete_datum();
  }
  else if (!d->video)
  {
    static datum::error_t const err_string = datum::error_t("Error with input file stream");

    dat = datum::error_datum(err_string);
  }
  else
  {
    pixtype_t const pixtype = config_value<pixtype_t>(priv::config_pixtype);
    if (pixtype == pixtypes::pixtype_byte()) // uint8_t
    {
      dat = convert_image<uint8_t>(d->video, pixtype, VIL_PIXEL_FORMAT_BYTE);
    }
//     else if (pixtype == pixtypes::pixtype_short()) // uint16_t - TODO
//     {
//       dat = convert_image<uint16_t>(d->video, pixtype, VIL_PIXEL_FORMAT_INT_16);
//     }
//     else if (pixtype == pixtypes::pixtype_int()) // uint32_t - TODO
//     {
//       dat = convert_image<uint32_t>(d->video, pixtype, VIL_PIXEL_FORMAT_INT_32);
//     }
//     else if (pixtype == pixtypes::pixtype_long()) // uint64_t - TODO
//     {
//       dat = convert_image<uint64_t>(d->video, pixtype, VIL_PIXEL_FORMAT_INT_64);
//     }
    else if (pixtype == pixtypes::pixtype_float()) // float
    {
      dat = convert_image<float>(d->video, pixtype, VIL_PIXEL_FORMAT_FLOAT);
    }
    else if (pixtype == pixtypes::pixtype_double()) // double
    {
      dat = convert_image<double>(d->video, pixtype, VIL_PIXEL_FORMAT_DOUBLE);
    }
    else
    {
      std::string const reason = "The pixtype \'" + pixtype + "\' "
                                 "is not supported";
      throw invalid_configuration_exception(name(), reason);
    }

    d->video->advance();
  }

  push_datum_to_port(priv::port_output, dat);

  process::_step();
}

read_video_gravl_process::priv
::priv(std::string const& input_uri)
  : uri(input_uri)
{
}

read_video_gravl_process::priv
::~priv()
{
}

}

static size_t compute_block_size(gravl::image::dimension dim,
                                 gravl::image::stride s);
template <typename T> static T* compute_block_start(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s);
template <typename T> static T* compute_top_left(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s);

template <typename PixType>
vistk::datum_t convert_image(
  gravl::data_block* video, vistk::pixtype_t const& pixtype,
  vil_pixel_format pixfmt)
{
  gravl::frame_ptr const frame = video->current_frame();
  gravl::image_data const* const image_data =
    frame.get_const_data<gravl::image_data>();
  gravl::image const image =
    (image_data ? image_data->pixels() : gravl::image());

  if (!image)
  {
    return vistk::datum::empty_datum();
  }
  else if (image.format() != gravl::image::format_of<PixType>())
  {
    std::stringstream reason;
    reason << "Unable to convert the image: the image pixel type "
           << image.format() << " does not match the port output type "
           << '\'' << pixtype << '\'';
    return vistk::datum::error_datum(reason.str());
  }
  else
  {
    gravl::image::dimension const dim = image.dimensions();
    gravl::image::stride const ps = image.strides();
    PixType const* const top_left = image.data<PixType>();
    size_t const size = compute_block_size(dim, ps);

    // vil_image_view is implicitly mutable, even though the ctors would
    // appear to suggest otherwise... so we must copy the pixels
    vil_memory_chunk* const mem = new vil_memory_chunk(size, pixfmt);
    PixType* const buffer = reinterpret_cast<PixType*>(mem->data());
    memcpy(buffer, compute_block_start(top_left, dim, ps), size);

    vil_image_view<PixType> const vil(vil_memory_chunk_sptr(mem),
                                      compute_top_left(buffer, dim, ps),
                                      dim.width, dim.height, dim.planes,
                                      ps.width, ps.height, ps.planes);
    return vistk::datum::new_datum(vil);
  }
}

static size_t
sabs(ptrdiff_t a)
{
  return static_cast<size_t>((a < 0 ? -a : a));
}

size_t
compute_block_size(gravl::image::dimension dim, gravl::image::stride s)
{
  return ((dim.width  - 1) * sabs(s.width)) +
         ((dim.height - 1) * sabs(s.height)) +
         ((dim.planes - 1) * sabs(s.planes)) + 1;
}

static ptrdiff_t
compute_offset(gravl::image::dimension dim, gravl::image::stride s)
{
  ptrdiff_t result = 0;
  if (s.width < 0)
  {
    result -= s.width * (dim.width - 1);
  }
  if (s.height < 0)
  {
    result -= s.height * (dim.height - 1);
  }
  if (s.planes < 0)
  {
    result -= s.planes * (dim.planes - 1);
  }
  return result;
}

template <typename T>
T*
compute_block_start(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s)
{
  return top_left - compute_offset(dim, s);
}

template <typename T>
T*
compute_top_left(
  T* top_left, gravl::image::dimension dim, gravl::image::stride s)
{
  return top_left + compute_offset(dim, s);
}
<|endoftext|>


<commit_before>/**
 * \file
 * \brief Mutex class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-09-27
 */

#include "distortos/scheduler/Mutex.hpp"

#include "distortos/scheduler/schedulerInstance.hpp"
#include "distortos/scheduler/Scheduler.hpp"

#include "distortos/architecture/InterruptMaskingLock.hpp"

#include <cerrno>

namespace distortos
{

namespace scheduler
{

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

Mutex::Mutex(const Type type) :
		blockedList_{schedulerInstance.getThreadControlBlockListAllocator(), ThreadControlBlock::State::BlockedOnMutex},
		owner_{},
		recursiveLocksCount_{},
		type_{type}
{

}

int Mutex::lock()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	if (owner_ == nullptr)
	{
		owner_ = &schedulerInstance.getCurrentThreadControlBlock();
		return 0;
	}

	if (type_ == Type::ErrorChecking && owner_ == &schedulerInstance.getCurrentThreadControlBlock())
		return EDEADLK;

	schedulerInstance.block(blockedList_);
	return 0;
}

int Mutex::tryLock()
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	return tryLockInternal();
}

int Mutex::tryLockFor(const TickClock::duration duration)
{
	return tryLockUntil(TickClock::now() + duration + TickClock::duration{1});
}

int Mutex::tryLockUntil(const TickClock::time_point timePoint)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	if (owner_ == nullptr)
	{
		owner_ = &schedulerInstance.getCurrentThreadControlBlock();
		return 0;
	}

	return schedulerInstance.blockUntil(blockedList_, timePoint);
}

int Mutex::unlock()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	if (type_ != Type::Normal && owner_ != &schedulerInstance.getCurrentThreadControlBlock())
		return EPERM;

	if (blockedList_.empty() == false)
	{
		owner_ = &blockedList_.begin()->get();	// pass ownership to the unblocked thread
		schedulerInstance.unblock(blockedList_.begin());
	}
	else
		owner_ = nullptr;

	return 0;
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

int Mutex::tryLockInternal()
{
	if (owner_ == nullptr)
	{
		owner_ = &schedulerInstance.getCurrentThreadControlBlock();
		return 0;
	}

	return EBUSY;
}

}	// namespace scheduler

}	// namespace distortos
<commit_msg>Mutex: implement main part of Mutex::lock() with Mutex::tryLockInternal()<commit_after>/**
 * \file
 * \brief Mutex class implementation
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * \date 2014-09-27
 */

#include "distortos/scheduler/Mutex.hpp"

#include "distortos/scheduler/schedulerInstance.hpp"
#include "distortos/scheduler/Scheduler.hpp"

#include "distortos/architecture/InterruptMaskingLock.hpp"

#include <cerrno>

namespace distortos
{

namespace scheduler
{

/*---------------------------------------------------------------------------------------------------------------------+
| public functions
+---------------------------------------------------------------------------------------------------------------------*/

Mutex::Mutex(const Type type) :
		blockedList_{schedulerInstance.getThreadControlBlockListAllocator(), ThreadControlBlock::State::BlockedOnMutex},
		owner_{},
		recursiveLocksCount_{},
		type_{type}
{

}

int Mutex::lock()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	const auto ret = tryLockInternal();
	if (ret == 0 || ret == EAGAIN)	// lock successful or recursive lock not possible?
		return ret;

	if (type_ == Type::ErrorChecking && owner_ == &schedulerInstance.getCurrentThreadControlBlock())
		return EDEADLK;

	schedulerInstance.block(blockedList_);
	return 0;
}

int Mutex::tryLock()
{
	architecture::InterruptMaskingLock interruptMaskingLock;
	return tryLockInternal();
}

int Mutex::tryLockFor(const TickClock::duration duration)
{
	return tryLockUntil(TickClock::now() + duration + TickClock::duration{1});
}

int Mutex::tryLockUntil(const TickClock::time_point timePoint)
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	if (owner_ == nullptr)
	{
		owner_ = &schedulerInstance.getCurrentThreadControlBlock();
		return 0;
	}

	return schedulerInstance.blockUntil(blockedList_, timePoint);
}

int Mutex::unlock()
{
	architecture::InterruptMaskingLock interruptMaskingLock;

	if (type_ != Type::Normal && owner_ != &schedulerInstance.getCurrentThreadControlBlock())
		return EPERM;

	if (blockedList_.empty() == false)
	{
		owner_ = &blockedList_.begin()->get();	// pass ownership to the unblocked thread
		schedulerInstance.unblock(blockedList_.begin());
	}
	else
		owner_ = nullptr;

	return 0;
}

/*---------------------------------------------------------------------------------------------------------------------+
| private functions
+---------------------------------------------------------------------------------------------------------------------*/

int Mutex::tryLockInternal()
{
	if (owner_ == nullptr)
	{
		owner_ = &schedulerInstance.getCurrentThreadControlBlock();
		return 0;
	}

	return EBUSY;
}

}	// namespace scheduler

}	// namespace distortos
<|endoftext|>


<commit_before>#include "string.h"
#include <vector>

using namespace std;

namespace wobble {
namespace str {

namespace {

/**
 * Return the substring of 'str' without all leading characters for which
 * 'classifier' returns true.
 */
template<typename FUN>
std::string lstrip(const std::string& str, const FUN& classifier)
{
    if (str.empty())
        return str;

    size_t beg = 0;
    while (beg < str.size() && classifier(str[beg]))
        ++beg;

    return str.substr(beg, str.size() - beg + 1);
}

/**
 * Return the substring of 'str' without all trailing characters for which
 * 'classifier' returns true.
 */
template<typename FUN>
std::string rstrip(const std::string& str, const FUN& classifier)
{
    if (str.empty())
        return str;

    size_t end = str.size();
    while (end > 0 && classifier(str[end - 1]))
        --end;

    if (end == 0)
        return std::string();
    else
        return str.substr(0, end);
}

/**
 * Return the substring of 'str' without all leading and trailing characters
 * for which 'classifier' returns true.
 */
template<typename FUN>
std::string strip(const std::string& str, const FUN& classifier)
{
    if (str.empty())
        return str;

    size_t beg = 0;
    size_t end = str.size() - 1;
    while (beg < end && classifier(str[beg]))
        ++beg;
    while (end >= beg && classifier(str[end]))
        --end;

    return str.substr(beg, end-beg+1);
}

}

std::string lstrip(const std::string& str)
{
    return lstrip(str, ::isspace);
}

std::string rstrip(const std::string& str)
{
    return rstrip(str, ::isspace);
}

std::string strip(const std::string& str)
{
    return strip(str, ::isspace);
}

std::string basename(const std::string& pathname)
{
    size_t pos = pathname.rfind("/");
    if (pos == std::string::npos)
        return pathname;
    else
        return pathname.substr(pos+1);
}

std::string dirname(const std::string& pathname)
{
    if (pathname.empty()) return ".";

    // Skip trailing separators
    size_t end = pathname.size();
    while (end > 0 && pathname[end - 1] == '/')
        --end;

    // If the result is empty again, then the string was only / characters
    if (!end) return "/";

    // Find the previous separator
    end = pathname.rfind("/", end - 1);

    if (end == std::string::npos)
        // No previous separator found, everything should be chopped
        return std::string(".");
    else
    {
        while (end > 0 && pathname[end - 1] == '/')
            --end;
        if (!end) return "/";
        return pathname.substr(0, end);
    }
}

void appendpath(std::string& dest, const char* path2)
{
    if (!*path2)
        return;

    if (dest.empty())
    {
        dest = path2;
        return;
    }

    if (dest[dest.size() - 1] == '/')
        if (path2[0] == '/')
            dest += (path2 + 1);
        else
            dest += path2;
    else
        if (path2[0] == '/')
            dest += path2;
        else
        {
            dest += '/';
            dest += path2;
        }
}

void appendpath(std::string& dest, const std::string& path2)
{
    if (path2.empty())
        return;

    if (dest.empty())
    {
        dest = path2;
        return;
    }

    if (dest[dest.size() - 1] == '/')
        if (path2[0] == '/')
            dest += path2.substr(1);
        else
            dest += path2;
    else
        if (path2[0] == '/')
            dest += path2;
        else
        {
            dest += '/';
            dest += path2;
        }
}

std::string joinpath(const std::string& path1, const std::string& path2)
{
    string res = path1;
    appendpath(res, path2);
    return res;
}

std::string normpath(const std::string& pathname)
{
    vector<string> st;
    if (pathname[0] == '/')
        st.push_back("/");

    Split split(pathname, "/");
    for (const auto& i: split)
    {
        if (i == "." || i.empty()) continue;
        if (i == "..")
            if (st.back() == "..")
                st.emplace_back(i);
            else if (st.back() == "/")
                continue;
            else
                st.pop_back();
        else
            st.emplace_back(i);
    }

    if (st.empty())
        return ".";

    string res;
    for (const auto& i: st)
        appendpath(res, i);
    return res;
}

Split::const_iterator::const_iterator(const Split& split)
    : split(&split)
{
    if (split.str.empty())
        this->split = nullptr;
    else
    {
        // Ignore leading separators if skip_end is true
        if (split.skip_empty) skip_separators();
        ++*this;
    }
}

Split::const_iterator::~const_iterator()
{
}

std::string Split::const_iterator::remainder() const
{
    if (end == std::string::npos)
        return std::string();
    else
        return split->str.substr(end);
};

void Split::const_iterator::skip_separators()
{
    const std::string& str = split->str;
    const std::string& sep = split->sep;

    while (end + sep.size() <= str.size())
    {
        unsigned i = 0;
        for ( ; i < sep.size(); ++i)
            if (str[end + i] != sep[i])
                break;
        if (i < sep.size())
            break;
        else
            end += sep.size();
    }
}

Split::const_iterator& Split::const_iterator::operator++()
{
    if (!split) return *this;

    const std::string& str = split->str;
    const std::string& sep = split->sep;
    bool skip_empty = split->skip_empty;

    /// Convert into an end iterator
    if (end == std::string::npos)
    {
        split = nullptr;
        return *this;
    }

    /// The string ended with an iterator, and we do not skip empty tokens:
    /// return it
    if (end == str.size())
    {
        cur = string();
        end = std::string::npos;
        return *this;
    }

    /// Position of the first character past the token that starts at 'end'
    size_t tok_end;
    if (sep.empty())
        /// If separator is empty, advance one character at a time
        tok_end = end + 1;
    else
    {
        /// The token ends at the next separator
        tok_end = str.find(sep, end);
    }

    /// No more separators found, return from end to the end of the string
    if (tok_end == std::string::npos)
    {
        cur = str.substr(end);
        end = std::string::npos;
        return *this;
    }

    /// We have the boundaries of the current token
    cur = str.substr(end, tok_end - end);

    /// Skip the separator
    end = tok_end + sep.size();

    /// Skip all the following separators if skip_empty is true
    if (skip_empty)
    {
        skip_separators();
        if (end == str.size())
        {
            end = std::string::npos;
            return *this;
        }
    }

    return *this;
}

const std::string& Split::const_iterator::operator*() const { return cur; }
const std::string* Split::const_iterator::operator->() const { return &cur; }

bool Split::const_iterator::operator==(const const_iterator& ti) const
{
    if (!split && !ti.split) return true;
    if (split != ti.split) return false;
    return end == ti.end;
}

bool Split::const_iterator::operator!=(const const_iterator& ti) const
{
    if (!split && !ti.split) return false;
    if (split != ti.split) return true;
    return end != ti.end;
}

std::string encode_cstring(const std::string& str)
{
    string res;
    for (string::const_iterator i = str.begin(); i != str.end(); ++i)
        if (*i == '\n')
            res += "\\n";
        else if (*i == '\t')
            res += "\\t";
        else if (*i == 0 || iscntrl(*i))
        {
            char buf[5];
            snprintf(buf, 5, "\\x%02x", (unsigned int)*i);
            res += buf;
        }
        else if (*i == '"' || *i == '\\')
        {
            res += "\\";
            res += *i;
        }
        else
            res += *i;
    return res;
}

std::string decode_cstring(const std::string& str, size_t& lenParsed)
{
    string res;
    string::const_iterator i = str.begin();
    for ( ; i != str.end() && *i != '"'; ++i)
        if (*i == '\\' && (i+1) != str.end())
        {
            switch (*(i+1))
            {
                case 'n': res += '\n'; break;
                case 't': res += '\t'; break;
                case 'x': {
                              size_t j;
                              char buf[5] = "0x\0\0";
                              // Read up to 2 extra hex digits
                              for (j = 0; j < 2 && i+2+j != str.end() && isxdigit(*(i+2+j)); ++j)
                                  buf[2+j] = *(i+2+j);
                              i += j;
                              res += (char)atoi(buf);
                              break;
                          }
                default:
                          res += *(i+1);
                          break;
            }
            ++i;
        } else
            res += *i;
    if (i != str.end() && *i == '"')
        ++i;
    lenParsed = i - str.begin();
    return res;
}

std::string encode_url(const std::string& str)
{
    string res;
    for (string::const_iterator i = str.begin(); i != str.end(); ++i)
    {
        if ( (*i >= '0' && *i <= '9') || (*i >= 'A' && *i <= 'Z')
          || (*i >= 'a' && *i <= 'z') || *i == '-' || *i == '_'
          || *i == '!' || *i == '*' || *i == '\'' || *i == '(' || *i == ')')
            res += *i;
        else {
            char buf[4];
            snprintf(buf, 4, "%%%02x", static_cast<unsigned>(static_cast<unsigned char>(*i)));
            res += buf;
        }
    }
    return res;
}

std::string decode_url(const std::string& str)
{
    string res;
    for (size_t i = 0; i < str.size(); ++i)
    {
        if (str[i] == '%')
        {
            // If there's a partial %something at the end, ignore it
            if (i >= str.size() - 2)
                return res;
            res += static_cast<char>(strtoul(str.substr(i+1, 2).c_str(), 0, 16));
            i += 2;
        }
        else
            res += str[i];
    }
    return res;
}

static const char* base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

template<typename T>
static const char invbase64(const T& idx)
{
    static const char data[] = {62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
    if (idx < 43) return 0;
    if (static_cast<unsigned>(idx) > 43 + (sizeof(data)/sizeof(data[0]))) return 0;
    return data[idx - 43];
}

std::string encode_base64(const std::string& str)
{
    return encode_base64(str.data(), str.size());
}

std::string encode_base64(const void* data, size_t size)
{
    std::string res;
    const uint8_t* str = (const uint8_t*)data;

    for (size_t i = 0; i < size; i += 3)
    {
        // Pack every triplet into 24 bits
        unsigned int enc;
        if (i + 3 < size)
            enc = (str[i] << 16) | (str[i + 1] << 8) | str[i + 2];
        else
        {
            enc = (str[i] << 16);
            if (i + 1 < size)
                enc |= str[i + 1] << 8;
            if (i + 2 < size)
                enc |= str[i + 2];
        }

        // Divide in 4 6-bit values and use them as indexes in the base64 char
        // array
        for (int j = 18; j >= 0; j -= 6)
            res += base64[(enc >> j) & 63];
    }

    // Replace padding characters with '='
    if (size % 3)
        for (size_t i = 0; i < 3 - (size % 3); ++i)
            res[res.size() - i - 1] = '=';

    return res;
}

std::string decode_base64(const std::string& str)
{
    std::string res;

    for (size_t i = 0; i < str.size(); i += 4)
    {
        // Pack every quadruplet into 24 bits
        unsigned int enc;
        if (i+4 < str.size())
        {
            enc = (invbase64(str[i]) << 18)
                + (invbase64(str[i+1]) << 12)
                + (invbase64(str[i+2]) << 6)
                + (invbase64(str[i+3]));
        } else {
            enc = (invbase64(str[i]) << 18);
            if (i+1 < str.size())
                enc += (invbase64(str[i+1]) << 12);
            if (i+2 < str.size())
                enc += (invbase64(str[i+2]) << 6);
            if (i+3 < str.size())
                enc += (invbase64(str[i+3]));
        }

        // Divide in 3 8-bit values and append them to the result
        res += enc >> 16 & 0xff;
        res += enc >> 8 & 0xff;
        res += enc & 0xff;
    }

    // Remove trailing padding
    if (str.size() > 0)
        for (size_t i = str.size() - 1; str[i] == '='; --i)
        {
            if (res.size() > 0)
                res.resize(res.size() - 1);
            if (i == 0 || res.size() == 0 )
                break;
        }

    return res;
}

}
}
<commit_msg>Changed strip algorithm to prevent an assertion on some distributions<commit_after>#include "string.h"
#include <vector>

using namespace std;

namespace wobble {
namespace str {

namespace {

/**
 * Return the substring of 'str' without all leading characters for which
 * 'classifier' returns true.
 */
template<typename FUN>
std::string lstrip(const std::string& str, const FUN& classifier)
{
    if (str.empty())
        return str;

    size_t beg = 0;
    while (beg < str.size() && classifier(str[beg]))
        ++beg;

    return str.substr(beg, str.size() - beg + 1);
}

/**
 * Return the substring of 'str' without all trailing characters for which
 * 'classifier' returns true.
 */
template<typename FUN>
std::string rstrip(const std::string& str, const FUN& classifier)
{
    if (str.empty())
        return str;

    size_t end = str.size();
    while (end > 0 && classifier(str[end - 1]))
        --end;

    if (end == 0)
        return std::string();
    else
        return str.substr(0, end);
}

/**
 * Return the substring of 'str' without all leading and trailing characters
 * for which 'classifier' returns true.
 */
template<typename FUN>
std::string strip(const std::string& str, const FUN& classifier)
{
    if (str.empty())
        return str;

    size_t beg = 0;
    size_t end = str.size();
    while (beg < end && classifier(str[beg]))
        ++beg;
    while (beg < end && classifier(str[end - 1]))
        --end;

    return str.substr(beg, end - beg);
}

}

std::string lstrip(const std::string& str)
{
    return lstrip(str, ::isspace);
}

std::string rstrip(const std::string& str)
{
    return rstrip(str, ::isspace);
}

std::string strip(const std::string& str)
{
    return strip(str, ::isspace);
}

std::string basename(const std::string& pathname)
{
    size_t pos = pathname.rfind("/");
    if (pos == std::string::npos)
        return pathname;
    else
        return pathname.substr(pos+1);
}

std::string dirname(const std::string& pathname)
{
    if (pathname.empty()) return ".";

    // Skip trailing separators
    size_t end = pathname.size();
    while (end > 0 && pathname[end - 1] == '/')
        --end;

    // If the result is empty again, then the string was only / characters
    if (!end) return "/";

    // Find the previous separator
    end = pathname.rfind("/", end - 1);

    if (end == std::string::npos)
        // No previous separator found, everything should be chopped
        return std::string(".");
    else
    {
        while (end > 0 && pathname[end - 1] == '/')
            --end;
        if (!end) return "/";
        return pathname.substr(0, end);
    }
}

void appendpath(std::string& dest, const char* path2)
{
    if (!*path2)
        return;

    if (dest.empty())
    {
        dest = path2;
        return;
    }

    if (dest[dest.size() - 1] == '/')
        if (path2[0] == '/')
            dest += (path2 + 1);
        else
            dest += path2;
    else
        if (path2[0] == '/')
            dest += path2;
        else
        {
            dest += '/';
            dest += path2;
        }
}

void appendpath(std::string& dest, const std::string& path2)
{
    if (path2.empty())
        return;

    if (dest.empty())
    {
        dest = path2;
        return;
    }

    if (dest[dest.size() - 1] == '/')
        if (path2[0] == '/')
            dest += path2.substr(1);
        else
            dest += path2;
    else
        if (path2[0] == '/')
            dest += path2;
        else
        {
            dest += '/';
            dest += path2;
        }
}

std::string joinpath(const std::string& path1, const std::string& path2)
{
    string res = path1;
    appendpath(res, path2);
    return res;
}

std::string normpath(const std::string& pathname)
{
    vector<string> st;
    if (pathname[0] == '/')
        st.push_back("/");

    Split split(pathname, "/");
    for (const auto& i: split)
    {
        if (i == "." || i.empty()) continue;
        if (i == "..")
            if (st.back() == "..")
                st.emplace_back(i);
            else if (st.back() == "/")
                continue;
            else
                st.pop_back();
        else
            st.emplace_back(i);
    }

    if (st.empty())
        return ".";

    string res;
    for (const auto& i: st)
        appendpath(res, i);
    return res;
}

Split::const_iterator::const_iterator(const Split& split)
    : split(&split)
{
    if (split.str.empty())
        this->split = nullptr;
    else
    {
        // Ignore leading separators if skip_end is true
        if (split.skip_empty) skip_separators();
        ++*this;
    }
}

Split::const_iterator::~const_iterator()
{
}

std::string Split::const_iterator::remainder() const
{
    if (end == std::string::npos)
        return std::string();
    else
        return split->str.substr(end);
};

void Split::const_iterator::skip_separators()
{
    const std::string& str = split->str;
    const std::string& sep = split->sep;

    while (end + sep.size() <= str.size())
    {
        unsigned i = 0;
        for ( ; i < sep.size(); ++i)
            if (str[end + i] != sep[i])
                break;
        if (i < sep.size())
            break;
        else
            end += sep.size();
    }
}

Split::const_iterator& Split::const_iterator::operator++()
{
    if (!split) return *this;

    const std::string& str = split->str;
    const std::string& sep = split->sep;
    bool skip_empty = split->skip_empty;

    /// Convert into an end iterator
    if (end == std::string::npos)
    {
        split = nullptr;
        return *this;
    }

    /// The string ended with an iterator, and we do not skip empty tokens:
    /// return it
    if (end == str.size())
    {
        cur = string();
        end = std::string::npos;
        return *this;
    }

    /// Position of the first character past the token that starts at 'end'
    size_t tok_end;
    if (sep.empty())
        /// If separator is empty, advance one character at a time
        tok_end = end + 1;
    else
    {
        /// The token ends at the next separator
        tok_end = str.find(sep, end);
    }

    /// No more separators found, return from end to the end of the string
    if (tok_end == std::string::npos)
    {
        cur = str.substr(end);
        end = std::string::npos;
        return *this;
    }

    /// We have the boundaries of the current token
    cur = str.substr(end, tok_end - end);

    /// Skip the separator
    end = tok_end + sep.size();

    /// Skip all the following separators if skip_empty is true
    if (skip_empty)
    {
        skip_separators();
        if (end == str.size())
        {
            end = std::string::npos;
            return *this;
        }
    }

    return *this;
}

const std::string& Split::const_iterator::operator*() const { return cur; }
const std::string* Split::const_iterator::operator->() const { return &cur; }

bool Split::const_iterator::operator==(const const_iterator& ti) const
{
    if (!split && !ti.split) return true;
    if (split != ti.split) return false;
    return end == ti.end;
}

bool Split::const_iterator::operator!=(const const_iterator& ti) const
{
    if (!split && !ti.split) return false;
    if (split != ti.split) return true;
    return end != ti.end;
}

std::string encode_cstring(const std::string& str)
{
    string res;
    for (string::const_iterator i = str.begin(); i != str.end(); ++i)
        if (*i == '\n')
            res += "\\n";
        else if (*i == '\t')
            res += "\\t";
        else if (*i == 0 || iscntrl(*i))
        {
            char buf[5];
            snprintf(buf, 5, "\\x%02x", (unsigned int)*i);
            res += buf;
        }
        else if (*i == '"' || *i == '\\')
        {
            res += "\\";
            res += *i;
        }
        else
            res += *i;
    return res;
}

std::string decode_cstring(const std::string& str, size_t& lenParsed)
{
    string res;
    string::const_iterator i = str.begin();
    for ( ; i != str.end() && *i != '"'; ++i)
        if (*i == '\\' && (i+1) != str.end())
        {
            switch (*(i+1))
            {
                case 'n': res += '\n'; break;
                case 't': res += '\t'; break;
                case 'x': {
                              size_t j;
                              char buf[5] = "0x\0\0";
                              // Read up to 2 extra hex digits
                              for (j = 0; j < 2 && i+2+j != str.end() && isxdigit(*(i+2+j)); ++j)
                                  buf[2+j] = *(i+2+j);
                              i += j;
                              res += (char)atoi(buf);
                              break;
                          }
                default:
                          res += *(i+1);
                          break;
            }
            ++i;
        } else
            res += *i;
    if (i != str.end() && *i == '"')
        ++i;
    lenParsed = i - str.begin();
    return res;
}

std::string encode_url(const std::string& str)
{
    string res;
    for (string::const_iterator i = str.begin(); i != str.end(); ++i)
    {
        if ( (*i >= '0' && *i <= '9') || (*i >= 'A' && *i <= 'Z')
          || (*i >= 'a' && *i <= 'z') || *i == '-' || *i == '_'
          || *i == '!' || *i == '*' || *i == '\'' || *i == '(' || *i == ')')
            res += *i;
        else {
            char buf[4];
            snprintf(buf, 4, "%%%02x", static_cast<unsigned>(static_cast<unsigned char>(*i)));
            res += buf;
        }
    }
    return res;
}

std::string decode_url(const std::string& str)
{
    string res;
    for (size_t i = 0; i < str.size(); ++i)
    {
        if (str[i] == '%')
        {
            // If there's a partial %something at the end, ignore it
            if (i >= str.size() - 2)
                return res;
            res += static_cast<char>(strtoul(str.substr(i+1, 2).c_str(), 0, 16));
            i += 2;
        }
        else
            res += str[i];
    }
    return res;
}

static const char* base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

template<typename T>
static const char invbase64(const T& idx)
{
    static const char data[] = {62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
    if (idx < 43) return 0;
    if (static_cast<unsigned>(idx) > 43 + (sizeof(data)/sizeof(data[0]))) return 0;
    return data[idx - 43];
}

std::string encode_base64(const std::string& str)
{
    return encode_base64(str.data(), str.size());
}

std::string encode_base64(const void* data, size_t size)
{
    std::string res;
    const uint8_t* str = (const uint8_t*)data;

    for (size_t i = 0; i < size; i += 3)
    {
        // Pack every triplet into 24 bits
        unsigned int enc;
        if (i + 3 < size)
            enc = (str[i] << 16) | (str[i + 1] << 8) | str[i + 2];
        else
        {
            enc = (str[i] << 16);
            if (i + 1 < size)
                enc |= str[i + 1] << 8;
            if (i + 2 < size)
                enc |= str[i + 2];
        }

        // Divide in 4 6-bit values and use them as indexes in the base64 char
        // array
        for (int j = 18; j >= 0; j -= 6)
            res += base64[(enc >> j) & 63];
    }

    // Replace padding characters with '='
    if (size % 3)
        for (size_t i = 0; i < 3 - (size % 3); ++i)
            res[res.size() - i - 1] = '=';

    return res;
}

std::string decode_base64(const std::string& str)
{
    std::string res;

    for (size_t i = 0; i < str.size(); i += 4)
    {
        // Pack every quadruplet into 24 bits
        unsigned int enc;
        if (i+4 < str.size())
        {
            enc = (invbase64(str[i]) << 18)
                + (invbase64(str[i+1]) << 12)
                + (invbase64(str[i+2]) << 6)
                + (invbase64(str[i+3]));
        } else {
            enc = (invbase64(str[i]) << 18);
            if (i+1 < str.size())
                enc += (invbase64(str[i+1]) << 12);
            if (i+2 < str.size())
                enc += (invbase64(str[i+2]) << 6);
            if (i+3 < str.size())
                enc += (invbase64(str[i+3]));
        }

        // Divide in 3 8-bit values and append them to the result
        res += enc >> 16 & 0xff;
        res += enc >> 8 & 0xff;
        res += enc & 0xff;
    }

    // Remove trailing padding
    if (str.size() > 0)
        for (size_t i = str.size() - 1; str[i] == '='; --i)
        {
            if (res.size() > 0)
                res.resize(res.size() - 1);
            if (i == 0 || res.size() == 0 )
                break;
        }

    return res;
}

}
}
<|endoftext|>


<commit_before>
#pragma once

#include "physics/euler_solver.hpp"

#include <algorithm>

#include "geometry/grassmann.hpp"
#include "numerics/elliptic_functions.hpp"
#include "numerics/elliptic_integrals.hpp"
#include "quantities/elementary_functions.hpp"
#include "quantities/si.hpp"

namespace principia {
namespace physics {
namespace internal_euler_solver {

using geometry::Vector;
using numerics::EllipticF;
using numerics::EllipticΠ;
using numerics::JacobiAmplitude;
using numerics::JacobiSNCNDN;
using quantities::Abs;
using quantities::ArcTan;
using quantities::ArcTanh;
using quantities::Cosh;
using quantities::Pow;
using quantities::Tanh;
using quantities::Energy;
using quantities::Inverse;
using quantities::Sqrt;
using quantities::Square;
using quantities::SquareRoot;
using quantities::SIUnit;
using quantities::Time;
using quantities::si::Joule;
using quantities::si::Radian;

template<typename InertialFrame, typename PrincipalAxesFrame>
EulerSolver<InertialFrame, PrincipalAxesFrame>::EulerSolver(
    R3Element<MomentOfInertia> const& moments_of_inertia,
    AngularMomentumBivector const& initial_angular_momentum,
    AttitudeRotation const& initial_attitude,
    Instant const& initial_time)
    : moments_of_inertia_(moments_of_inertia),
      initial_angular_momentum_(initial_angular_momentum),
      initial_attitude_(initial_attitude),
      initial_time_(initial_time) {
  CHECK_LE(I₁_, I₂_);
  CHECK_LE(I₂_, I₃_);

  auto const& m = initial_angular_momentum.coordinates();

  auto const I₁₂ = I₁_ - I₂_;
  auto const I₁₃ = I₁_ - I₃_;
  auto const I₂₁ = -I₁₂;
  auto const I₂₃ = I₂_ - I₃_;
  auto const I₃₁ = -I₁₃;
  auto const I₃₂ = -I₂₃;

  // The formulæ for the Δs in Celledoni cannot be used directly because of
  // cancellations.
  auto const Δ₁ = m.y * m.y * I₂₁ / I₂_ + m.z * m.z * I₃₁ / I₃_;
  auto const Δ₂ = m.x * m.x * I₁₂ / I₁_ + m.z * m.z * I₃₂ / I₃_;
  auto const Δ₃ = m.x * m.x * I₃₁ / I₁_ + m.y * m.y * I₃₂ / I₂_;
  DCHECK_LE(Square<AngularMomentum>(), Δ₁);
  DCHECK_LE(Square<AngularMomentum>(), Δ₃);

  auto const G² =  initial_angular_momentum_.Norm²();
  auto const two_T = m.x * m.x / I₁_ + m.y * m.y / I₂_ + m.z * m.z / I₃_;

  B₁₃_ = Sqrt(I₁_ * Δ₃ / I₃₁);
  B₃₁_ = Sqrt(I₃_ * Δ₁ / I₃₁);

  // Note that Celledoni et al. give k, but we need mc = 1 - k^2.  We write mc
  // in a way that reduces cancellations when k is close to 1.
  if (Δ₂ < Square<AngularMomentum>()) {
    B₂₁_ = Sqrt(I₂_ * Δ₁ / I₂₁);
    mc_ = -Δ₂ * I₃₁ / (Δ₃ * I₂₁);
    ν_ = EllipticF(ArcTan(m.y / B₂₁_, m.z / B₃₁_), mc_);
    λ₃_ = Sqrt(Δ₃ * I₂₁ / (I₁_ * I₂_ * I₃_));
    if (m.x < AngularMomentum()) {
      B₁₃_ = -B₁₃_;
    } else {
      λ₃_ = -λ₃_;
    }
    auto const B₂₃² = -I₂_ * Δ₃ / I₃₂;
    DCHECK_LE(Square<AngularMomentum>(), B₂₃²);
    n_ = G² / B₂₃²;
    ψ_Π_offset = EllipticΠ(-ν_, n_, mc_);
    ψ_Π_multiplier_ = Δ₂ / (λ₃_ * I₂_ * G_);
    ψ_t_multiplier_ = two_T / G_;
    formula_ = Formula::i;
  } else if (Square<AngularMomentum>() < Δ₂) {
    B₂₃_ = Sqrt(I₂_ * Δ₃ / I₃₂);
    mc_ = Δ₂ * I₃₁ / (Δ₁ * I₃₂);
    ν_ = EllipticF(ArcTan(m.y / B₂₃_, m.x / B₁₃_), mc_);
    λ₁_ = Sqrt(Δ₁ * I₃₂ / (I₁_ * I₂_ * I₃_));
    if (m.z < AngularMomentum()) {
      B₃₁_ = -B₃₁_;
    } else {
      λ₁_ = -λ₁_;
    }
    auto const B₂₁² = I₂_ * Δ₁ / I₂₁;
    DCHECK_LE(Square<AngularMomentum>(), B₂₁²);
    n_ = G² / B₂₁²;
    ψ_Π_offset = EllipticΠ(-ν_, n_, mc_);
    ψ_Π_multiplier_ = Δ₂ / (λ₁_ * I₂_ * G_);
    ψ_t_multiplier_ = two_T / G_;
    formula_ = Formula::ii;
  } else {
    CHECK_EQ(Square<AngularMomentum>(), Δ₂);
    if (I₃₁ == MomentOfInertia()) {
      // The degenerate case of a sphere.  It would create NaNs.
      DCHECK_EQ(MomentOfInertia(), I₂₁);
      DCHECK_EQ(MomentOfInertia(), I₃₂);
      formula_ = Formula::Sphere;
    } else {
      G_ =  Sqrt(G²);
      ν_ = -ArcTanh(m.y / G_);
      λ₂_ = Sqrt(Δ₁ * Δ₃ / (I₁_ * I₃_)) / G_;
      if (m.x < AngularMomentum()) {
        B₁₃_ = -B₁₃_;
        λ₂_ = -λ₂_;
      }
      if (m.z < AngularMomentum()) {
        B₃₁_ = -B₃₁_;
        λ₂_ = -λ₂_;
      }
      formula_ = Formula::iii;
    }
  }
}

template<typename InertialFrame, typename PrincipalAxesFrame>
typename EulerSolver<InertialFrame, PrincipalAxesFrame>::AngularMomentumBivector
EulerSolver<InertialFrame, PrincipalAxesFrame>::AngularMomentumAt(
    Instant const& time) const {
  Time const Δt = time - initial_time_;
  switch (formula_) {
    case Formula::i: {
      double sn;
      double cn;
      double dn;
      JacobiSNCNDN(λ₃_ * Δt - ν_, mc_, sn, cn, dn);
      return AngularMomentumBivector({B₁₃_ * dn, -B₂₁_ * sn, B₃₁_ * cn});
    }
    case Formula::ii: {
      double sn;
      double cn;
      double dn;
      JacobiSNCNDN(λ₁_ * Δt - ν_, mc_, sn, cn, dn);
      return AngularMomentumBivector({B₁₃_ * cn, -B₂₃_ * sn, B₃₁_ * dn});
    }
    case Formula::iii: {
      Angle const angle = λ₂_ * Δt - ν_;
      double const sech = 1.0 / Cosh(angle);
      return AngularMomentumBivector(
          {B₁₃_ * sech, G_ * Tanh(angle), B₃₁_ * sech});
    }
    case Formula::Sphere : {
      // NOTE(phl): It's unclear how the formulæ degenerate in this case, but
      // surely λ₃_ becomes 0, so the dependency in time disappears, so this is
      // my best guess.
      return initial_angular_momentum_;
    }
    default:
      LOG(FATAL) << "Unexpected formula " << static_cast<int>(formula_);
  };
}

template<typename InertialFrame, typename PrincipalAxesFrame>
typename EulerSolver<InertialFrame, PrincipalAxesFrame>::AttitudeRotation
EulerSolver<InertialFrame, PrincipalAxesFrame>::AttitudeAt(
    AngularMomentumBivector const& angular_momentum,
    Instant const& time) const {
  auto const& m = angular_momentum.coordinates();
  Time const Δt = time - initial_time_;
  switch (formula_) {
    case Formula::i: {
      // Note that the sign of λ has been integrated in λ₃_ at construction.
      Angle const φ = JacobiAmplitude(λ₃_ * Δt - ν_, mc_);
      Angle const ψ = ψ_t_multiplier_ * Δt +
                      ψ_Π_multiplier_ * (EllipticΠ(φ, n_, mc_) - ψ_Π_offset);
    }
    case Formula::ii: {
      // Note that the sign of λ has been integrated in λ₁_ at construction.
      Angle const φ = JacobiAmplitude(λ₁_ * Δt - ν_, mc_);
      Angle const ψ = ψ_t_multiplier_ * Δt +
                      ψ_Π_multiplier_ * (EllipticΠ(φ, n_, mc_) - ψ_Π_offset);
    }
    case Formula::iii: {
    }
    case Formula::Sphere : {
    }
    default:
      LOG(FATAL) << "Unexpected formula " << static_cast<int>(formula_);
  };
  return AttitudeRotation::Identity();
}

}  // namespace internal_euler_solver
}  // namespace physics
}  // namespace principia
<commit_msg>A bit of case iii.<commit_after>
#pragma once

#include "physics/euler_solver.hpp"

#include <algorithm>

#include "geometry/grassmann.hpp"
#include "numerics/elliptic_functions.hpp"
#include "numerics/elliptic_integrals.hpp"
#include "quantities/elementary_functions.hpp"
#include "quantities/si.hpp"

namespace principia {
namespace physics {
namespace internal_euler_solver {

using geometry::Vector;
using numerics::EllipticF;
using numerics::EllipticΠ;
using numerics::JacobiAmplitude;
using numerics::JacobiSNCNDN;
using quantities::Abs;
using quantities::ArcTan;
using quantities::ArcTanh;
using quantities::Cosh;
using quantities::Pow;
using quantities::Tanh;
using quantities::Energy;
using quantities::Inverse;
using quantities::Sqrt;
using quantities::Square;
using quantities::SquareRoot;
using quantities::SIUnit;
using quantities::Time;
using quantities::si::Joule;
using quantities::si::Radian;

template<typename InertialFrame, typename PrincipalAxesFrame>
EulerSolver<InertialFrame, PrincipalAxesFrame>::EulerSolver(
    R3Element<MomentOfInertia> const& moments_of_inertia,
    AngularMomentumBivector const& initial_angular_momentum,
    AttitudeRotation const& initial_attitude,
    Instant const& initial_time)
    : moments_of_inertia_(moments_of_inertia),
      initial_angular_momentum_(initial_angular_momentum),
      initial_attitude_(initial_attitude),
      initial_time_(initial_time) {
  CHECK_LE(I₁_, I₂_);
  CHECK_LE(I₂_, I₃_);

  auto const& m = initial_angular_momentum.coordinates();

  auto const I₁₂ = I₁_ - I₂_;
  auto const I₁₃ = I₁_ - I₃_;
  auto const I₂₁ = -I₁₂;
  auto const I₂₃ = I₂_ - I₃_;
  auto const I₃₁ = -I₁₃;
  auto const I₃₂ = -I₂₃;

  // The formulæ for the Δs in Celledoni cannot be used directly because of
  // cancellations.
  auto const Δ₁ = m.y * m.y * I₂₁ / I₂_ + m.z * m.z * I₃₁ / I₃_;
  auto const Δ₂ = m.x * m.x * I₁₂ / I₁_ + m.z * m.z * I₃₂ / I₃_;
  auto const Δ₃ = m.x * m.x * I₃₁ / I₁_ + m.y * m.y * I₃₂ / I₂_;
  DCHECK_LE(Square<AngularMomentum>(), Δ₁);
  DCHECK_LE(Square<AngularMomentum>(), Δ₃);

  auto const G² =  initial_angular_momentum_.Norm²();
  auto const two_T = m.x * m.x / I₁_ + m.y * m.y / I₂_ + m.z * m.z / I₃_;

  auto const B₂₃² = I₂_ * Δ₃ / I₃₂;
  auto const B₂₁² = I₂_ * Δ₁ / I₂₁;
  DCHECK_LE(Square<AngularMomentum>(), B₂₃²);
  DCHECK_LE(Square<AngularMomentum>(), B₂₁²);

  B₁₃_ = Sqrt(I₁_ * Δ₃ / I₃₁);
  B₃₁_ = Sqrt(I₃_ * Δ₁ / I₃₁);

  // Note that Celledoni et al. give k, but we need mc = 1 - k^2.  We write mc
  // in a way that reduces cancellations when k is close to 1.
  if (Δ₂ < Square<AngularMomentum>()) {
    B₂₁_ = Sqrt(B₂₁²);
    mc_ = -Δ₂ * I₃₁ / (Δ₃ * I₂₁);
    ν_ = EllipticF(ArcTan(m.y / B₂₁_, m.z / B₃₁_), mc_);
    λ₃_ = Sqrt(Δ₃ * I₂₁ / (I₁_ * I₂_ * I₃_));
    if (m.x < AngularMomentum()) {
      B₁₃_ = -B₁₃_;
    } else {
      λ₃_ = -λ₃_;
    }
    n_ = G² / B₂₃²;
    ψ_Π_offset = EllipticΠ(-ν_, n_, mc_);
    ψ_Π_multiplier_ = Δ₂ / (λ₃_ * I₂_ * G_);
    ψ_t_multiplier_ = two_T / G_;
    formula_ = Formula::i;
  } else if (Square<AngularMomentum>() < Δ₂) {
    B₂₃_ = Sqrt(B₂₃²);
    mc_ = Δ₂ * I₃₁ / (Δ₁ * I₃₂);
    ν_ = EllipticF(ArcTan(m.y / B₂₃_, m.x / B₁₃_), mc_);
    λ₁_ = Sqrt(Δ₁ * I₃₂ / (I₁_ * I₂_ * I₃_));
    if (m.z < AngularMomentum()) {
      B₃₁_ = -B₃₁_;
    } else {
      λ₁_ = -λ₁_;
    }
    n_ = G² / B₂₁²;
    ψ_Π_offset = EllipticΠ(-ν_, n_, mc_);
    ψ_Π_multiplier_ = Δ₂ / (λ₁_ * I₂_ * G_);
    ψ_t_multiplier_ = two_T / G_;
    formula_ = Formula::ii;
  } else {
    CHECK_EQ(Square<AngularMomentum>(), Δ₂);
    if (I₃₁ == MomentOfInertia()) {
      // The degenerate case of a sphere.  It would create NaNs.
      DCHECK_EQ(MomentOfInertia(), I₂₁);
      DCHECK_EQ(MomentOfInertia(), I₃₂);
      formula_ = Formula::Sphere;
    } else {
      G_ =  Sqrt(G²);
      ν_ = -ArcTanh(m.y / G_);
      λ₂_ = Sqrt(Δ₁ * Δ₃ / (I₁_ * I₃_)) / G_;
      if (m.x < AngularMomentum()) {
        B₁₃_ = -B₁₃_;
        λ₂_ = -λ₂_;
      }
      if (m.z < AngularMomentum()) {
        B₃₁_ = -B₃₁_;
        λ₂_ = -λ₂_;
      }
      // Not quite an elliptic integral characteristic, but we'll stick to that
      // notation.
      n_ = G² * G² / (B₂₁² * B₂₃²);
      formula_ = Formula::iii;
    }
  }
}

template<typename InertialFrame, typename PrincipalAxesFrame>
typename EulerSolver<InertialFrame, PrincipalAxesFrame>::AngularMomentumBivector
EulerSolver<InertialFrame, PrincipalAxesFrame>::AngularMomentumAt(
    Instant const& time) const {
  Time const Δt = time - initial_time_;
  switch (formula_) {
    case Formula::i: {
      double sn;
      double cn;
      double dn;
      JacobiSNCNDN(λ₃_ * Δt - ν_, mc_, sn, cn, dn);
      return AngularMomentumBivector({B₁₃_ * dn, -B₂₁_ * sn, B₃₁_ * cn});
    }
    case Formula::ii: {
      double sn;
      double cn;
      double dn;
      JacobiSNCNDN(λ₁_ * Δt - ν_, mc_, sn, cn, dn);
      return AngularMomentumBivector({B₁₃_ * cn, -B₂₃_ * sn, B₃₁_ * dn});
    }
    case Formula::iii: {
      Angle const angle = λ₂_ * Δt - ν_;
      double const sech = 1.0 / Cosh(angle);
      return AngularMomentumBivector(
          {B₁₃_ * sech, G_ * Tanh(angle), B₃₁_ * sech});
    }
    case Formula::Sphere : {
      // NOTE(phl): It's unclear how the formulæ degenerate in this case, but
      // surely λ₃_ becomes 0, so the dependency in time disappears, so this is
      // my best guess.
      return initial_angular_momentum_;
    }
    default:
      LOG(FATAL) << "Unexpected formula " << static_cast<int>(formula_);
  };
}

template<typename InertialFrame, typename PrincipalAxesFrame>
typename EulerSolver<InertialFrame, PrincipalAxesFrame>::AttitudeRotation
EulerSolver<InertialFrame, PrincipalAxesFrame>::AttitudeAt(
    AngularMomentumBivector const& angular_momentum,
    Instant const& time) const {
  auto const& m = angular_momentum.coordinates();
  Time const Δt = time - initial_time_;
  switch (formula_) {
    case Formula::i: {
      // Note that the sign of λ has been integrated in λ₃_ at construction.
      Angle const φ = JacobiAmplitude(λ₃_ * Δt - ν_, mc_);
      Angle const ψ = ψ_t_multiplier_ * Δt +
                      ψ_Π_multiplier_ * (EllipticΠ(φ, n_, mc_) - ψ_Π_offset);
    }
    case Formula::ii: {
      // Note that the sign of λ has been integrated in λ₁_ at construction.
      Angle const φ = JacobiAmplitude(λ₁_ * Δt - ν_, mc_);
      Angle const ψ = ψ_t_multiplier_ * Δt +
                      ψ_Π_multiplier_ * (EllipticΠ(φ, n_, mc_) - ψ_Π_offset);
    }
    case Formula::iii: {
    }
    case Formula::Sphere : {
    }
    default:
      LOG(FATAL) << "Unexpected formula " << static_cast<int>(formula_);
  };
  return AttitudeRotation::Identity();
}

}  // namespace internal_euler_solver
}  // namespace physics
}  // namespace principia
<|endoftext|>


<commit_before>#define DOCTEST_CONFIG_IMPLEMENT
#include "doctest.h"
#include "iguana/json_reader.hpp"
#include <iguana/json.hpp>
#include <iguana/json_util.hpp>
#include <iostream>
#include <optional>

struct point_t {
  int x;
  double y;
};
REFLECTION(point_t, x, y);

struct person {
  std::string name;
  bool ok;
};
REFLECTION(person, name, ok);

struct bool_t {
  bool ok;
};
REFLECTION(bool_t, ok);

struct optional_t {
  std::optional<bool> p;
};
REFLECTION(optional_t, p);

struct char_t {
  char ch;
};
REFLECTION(char_t, ch);

// nested object
struct simple_nested_t {
  int id;
  person p;
};
REFLECTION(simple_nested_t, id, p);

// c array
struct arr_t {
  int arr[2];
};
REFLECTION(arr_t, arr);

// std array
struct std_array_t {
  std::array<int, 2> arr;
};
REFLECTION(std_array_t, arr);

// vector
struct vector_t {
  std::vector<int> arr;
};
REFLECTION(vector_t, arr);

struct two_fields_t {
  std::array<int, 2> a;
  std::vector<std::string> v;
};
REFLECTION(two_fields_t, a, v);

// map TODO

// list, set, unordered map... TODO

TEST_CASE("test simple object") {
  std::string_view str = R"({"name": "tom", "ok":true})";

  person p{};
  iguana::from_json(p, std::begin(str), std::end(str));
  CHECK(p.name == "tom");
  CHECK(p.ok == true);

  SUBCASE("random order of fields") {
    person p1{};
    std::string_view str1 = R"({"ok":true, "name": "tom"})";
    iguana::from_json(p1, std::begin(str1), std::end(str1));
    CHECK(p1.name == "tom");
    CHECK(p1.ok == true);
  }
}

TEST_CASE("test two_fields object") {
  two_fields_t obj{{1, 2}, {"aa", "bb"}};
  iguana::string_stream ss;
  iguana::json::to_json(ss, obj);

  std::string str = ss.str();
  two_fields_t p{};
  iguana::from_json(p, std::begin(str), std::end(str));
  CHECK(p.v == obj.v);
}

TEST_CASE("test simple nested object") {
  simple_nested_t t{.id = 1, {.name = "tom", .ok = false}};
  iguana::string_stream ss;
  iguana::json::to_json(ss, t);

  std::string str = ss.str();
  simple_nested_t p{};
  iguana::from_json(p, std::begin(str), std::end(str));

  CHECK(t.id == p.id);
  CHECK(t.p.name == p.p.name);
  CHECK(t.p.ok == p.p.ok);
}

TEST_CASE("test c array and std::array") {
  arr_t arr{{1, 2}};
  iguana::string_stream ss;
  iguana::json::to_json(ss, arr);
  arr_t arr1{};
  std::string str = ss.str();

  iguana::from_json(arr1, std::begin(str), std::end(str));
  CHECK(arr.arr[0] == arr1.arr[0]);
  CHECK(arr.arr[1] == arr1.arr[1]);

  std_array_t arr2{};
  iguana::from_json(arr2, std::begin(str), std::end(str));
  CHECK(arr.arr[0] == arr2.arr[0]);
  CHECK(arr.arr[1] == arr2.arr[1]);

  vector_t vec;
  iguana::from_json(vec, std::begin(str), std::end(str));
  CHECK(vec.arr.size() == arr2.arr.size());
  CHECK(arr2.arr[0] == vec.arr[0]);
  CHECK(arr2.arr[1] == vec.arr[1]);
}

TEST_CASE("test bool, null, char, int, float") {
  {
    optional_t p{};
    std::string str = R"({"p": false})";
    iguana::from_json(p, std::begin(str), std::end(str));
    CHECK(p.p.has_value());
    CHECK(*p.p == false);

    std::string str1 = R"({"p": null})";
    optional_t p1{};
    iguana::from_json(p1, std::begin(str1), std::end(str1));
    CHECK(!p1.p.has_value());
  }
  {
    char_t p{};
    std::string str = R"({"ch": "t"})";
    iguana::from_json(p, std::begin(str), std::end(str));
    CHECK(p.ch == 't');
  }

  {
    bool_t p{};
    std::string str = R"({"ok": true})";
    iguana::from_json(p, std::begin(str), std::end(str));
    CHECK(p.ok == true);
  }

  {
    point_t p{};
    std::string str = R"({"x" : 1, "y" : 2})";
    iguana::from_json(p, std::begin(str), std::end(str));
    CHECK(p.x == 1);
    CHECK(p.y == double(2));
  }
}

TEST_CASE("test vector") {
  vector_t arr{{1, 2}};
  iguana::string_stream ss;
  iguana::json::to_json(ss, arr);

  std::string str = ss.str();
  vector_t p{};
  iguana::from_json(p, std::begin(str), std::end(str));
  CHECK(arr.arr == p.arr);
}

TEST_CASE("check some types") {
  using value_type = std::variant<int point_t::*, double point_t::*>;
  constexpr auto map = iguana::get_iguana_struct_map<point_t>();
  static_assert(map.size() == 2);
  static_assert(map.at("x") ==
                value_type{std::in_place_index_t<0>{}, &point_t::x});
  static_assert(map.at("y") ==
                value_type{std::in_place_index_t<1>{}, &point_t::y});
}

// doctest comments
// 'function' : must be 'attribute' - see issue #182
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007)
int main(int argc, char **argv) { return doctest::Context(argc, argv).run(); }
DOCTEST_MSVC_SUPPRESS_WARNING_POP<commit_msg>fix compile error on gcc10<commit_after>#define DOCTEST_CONFIG_IMPLEMENT
#include "doctest.h"
#include "iguana/json_reader.hpp"
#include <iguana/json.hpp>
#include <iguana/json_util.hpp>
#include <iostream>
#include <optional>

struct point_t {
  int x;
  double y;
};
REFLECTION(point_t, x, y);

struct person {
  std::string name;
  bool ok;
};
REFLECTION(person, name, ok);

struct bool_t {
  bool ok;
};
REFLECTION(bool_t, ok);

struct optional_t {
  std::optional<bool> p;
};
REFLECTION(optional_t, p);

struct char_t {
  char ch;
};
REFLECTION(char_t, ch);

// nested object
struct simple_nested_t {
  int id;
  person p;
};
REFLECTION(simple_nested_t, id, p);

// c array
struct arr_t {
  int arr[2];
};
REFLECTION(arr_t, arr);

// std array
struct std_array_t {
  std::array<int, 2> arr;
};
REFLECTION(std_array_t, arr);

// vector
struct vector_t {
  std::vector<int> arr;
};
REFLECTION(vector_t, arr);

struct two_fields_t {
  std::array<int, 2> a;
  std::vector<std::string> v;
};
REFLECTION(two_fields_t, a, v);

// map TODO
struct map_t {
  std::map<int, std::string> map;
};
REFLECTION(map_t, map);

// list, set, unordered map... TODO

TEST_CASE("test simple object") {
  std::string_view str = R"({"name": "tom", "ok":true})";

  person p{};
  iguana::from_json(p, std::begin(str), std::end(str));
  CHECK(p.name == "tom");
  CHECK(p.ok == true);

  SUBCASE("random order of fields") {
    person p1{};
    std::string_view str1 = R"({"ok":true, "name": "tom"})";
    iguana::from_json(p1, std::begin(str1), std::end(str1));
    CHECK(p1.name == "tom");
    CHECK(p1.ok == true);
  }
}

TEST_CASE("test two_fields object") {
  two_fields_t obj{{1, 2}, {"aa", "bb"}};
  iguana::string_stream ss;
  iguana::json::to_json(ss, obj);

  std::string str = ss.str();
  two_fields_t p{};
  iguana::from_json(p, std::begin(str), std::end(str));
  CHECK(p.v == obj.v);
}

TEST_CASE("test simple nested object") {
  person o{.name = "tom", .ok = false};
  simple_nested_t t{1, o};
  iguana::string_stream ss;
  iguana::json::to_json(ss, t);

  std::string str = ss.str();
  simple_nested_t p{};
  iguana::from_json(p, std::begin(str), std::end(str));

  CHECK(t.id == p.id);
  CHECK(t.p.name == p.p.name);
  CHECK(t.p.ok == p.p.ok);
}

TEST_CASE("test c array and std::array") {
  arr_t arr{{1, 2}};
  iguana::string_stream ss;
  iguana::json::to_json(ss, arr);
  arr_t arr1{};
  std::string str = ss.str();

  iguana::from_json(arr1, std::begin(str), std::end(str));
  CHECK(arr.arr[0] == arr1.arr[0]);
  CHECK(arr.arr[1] == arr1.arr[1]);

  std_array_t arr2{};
  iguana::from_json(arr2, std::begin(str), std::end(str));
  CHECK(arr.arr[0] == arr2.arr[0]);
  CHECK(arr.arr[1] == arr2.arr[1]);

  vector_t vec;
  iguana::from_json(vec, std::begin(str), std::end(str));
  CHECK(vec.arr.size() == arr2.arr.size());
  CHECK(arr2.arr[0] == vec.arr[0]);
  CHECK(arr2.arr[1] == vec.arr[1]);
}

TEST_CASE("test bool, null, char, int, float") {
  {
    optional_t p{};
    std::string str = R"({"p": false})";
    iguana::from_json(p, std::begin(str), std::end(str));
    CHECK(p.p.has_value());
    CHECK(*p.p == false);

    std::string str1 = R"({"p": null})";
    optional_t p1{};
    iguana::from_json(p1, std::begin(str1), std::end(str1));
    CHECK(!p1.p.has_value());
  }
  {
    char_t p{};
    std::string str = R"({"ch": "t"})";
    iguana::from_json(p, std::begin(str), std::end(str));
    CHECK(p.ch == 't');
  }

  {
    bool_t p{};
    std::string str = R"({"ok": true})";
    iguana::from_json(p, std::begin(str), std::end(str));
    CHECK(p.ok == true);
  }

  {
    point_t p{};
    std::string str = R"({"x" : 1, "y" : 2})";
    iguana::from_json(p, std::begin(str), std::end(str));
    CHECK(p.x == 1);
    CHECK(p.y == double(2));
  }
}

TEST_CASE("test vector") {
  vector_t arr{{1, 2}};
  iguana::string_stream ss;
  iguana::json::to_json(ss, arr);

  std::string str = ss.str();
  vector_t p{};
  iguana::from_json(p, std::begin(str), std::end(str));
  CHECK(arr.arr == p.arr);
}

TEST_CASE("check some types") {
  using value_type = std::variant<int point_t::*, double point_t::*>;
  constexpr auto map = iguana::get_iguana_struct_map<point_t>();
  static_assert(map.size() == 2);
  static_assert(map.at("x") ==
                value_type{std::in_place_index_t<0>{}, &point_t::x});
  static_assert(map.at("y") ==
                value_type{std::in_place_index_t<1>{}, &point_t::y});
}

// doctest comments
// 'function' : must be 'attribute' - see issue #182
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007)
int main(int argc, char **argv) { return doctest::Context(argc, argv).run(); }
DOCTEST_MSVC_SUPPRESS_WARNING_POP<|endoftext|>