<commit_before>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// Single cell view graph panels widget
//==============================================================================

#include "singlecellviewgraphpanelplotwidget.h"
#include "singlecellviewgraphpanelswidget.h"
#include "singlecellviewgraphpanelwidget.h"

//==============================================================================

#include <QSettings>
#include <QWheelEvent>

//==============================================================================

namespace OpenCOR {
namespace SingleCellView {

//==============================================================================

SingleCellViewGraphPanelsWidget::SingleCellViewGraphPanelsWidget(QWidget *pParent) :
    QSplitter(pParent),
    CommonWidget(pParent),
    mSplitterSizes(QList<int>()),
    mActiveGraphPanels(QMap<QString, SingleCellViewGraphPanelWidget *>()),
    mActiveGraphPanel(0),
    mPlotsRects(QMap<QString, QMap<SingleCellViewGraphPanelPlotWidget *, QRectF> >())
{
    // Set our orientation

    setOrientation(Qt::Vertical);

    // Keep track of our movement
    // Note: we need to keep track of our movement so that saveSettings() can
    //       work fine even when we are not visible (which happens when a CellML
    //       file cannot be run for some reason or another)...

    connect(this, SIGNAL(splitterMoved(int, int)),
            this, SLOT(splitterMoved()));
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::retranslateUi()
{
    // Retranslate all our graph panels

    for (int i = 0, iMax = count(); i < iMax; ++i)
        qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i))->retranslateUi();
}

//==============================================================================

static const auto SettingsGraphPanelSizes = QStringLiteral("GraphPanelSizes");

//==============================================================================

void SingleCellViewGraphPanelsWidget::loadSettings(QSettings *pSettings)
{
    // Let the user know of a few default things about ourselves by emitting a
    // few signals

    emit removeGraphPanelsEnabled(false);

    // Retrieve and set the size of each graph panel, as well as create the
    // corresponding number of graphs

    qRegisterMetaTypeStreamOperators< QList<int> >("QList<int>");

    QVariant defaultGraphPanelSizes = QVariant::fromValue< QList<int> >(QList<int>());

    mSplitterSizes = pSettings->value(SettingsGraphPanelSizes, defaultGraphPanelSizes).value< QList<int> >();

    int graphPanelsCount = mSplitterSizes.count();

    if (!graphPanelsCount)
        // For some reason, the settings for the number of graph panels to be
        // created got messed up, so...

        graphPanelsCount = 1;

    for (int i = 0; i < graphPanelsCount; ++i)
        addGraphPanel();

    setSizes(mSplitterSizes);
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::saveSettings(QSettings *pSettings) const
{
    // Keep track of the size of each graph panel

    pSettings->setValue(SettingsGraphPanelSizes, QVariant::fromValue< QList<int> >(mSplitterSizes));
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::initialize(const QString &pFileName)
{
    // Set the active graph panel or select the first one, if no backup exists

    SingleCellViewGraphPanelWidget *activeGraphPanel = mActiveGraphPanels.value(pFileName);

    if (activeGraphPanel)
        activeGraphPanel->setActive(true);
    else
        qobject_cast<SingleCellViewGraphPanelWidget *>(widget(0))->setActive(true);

    // Update our plots' axes' values
    // Note: we always want our plot to be replotted. Indeed, say that you plot
    //       a graph that doesn't require changing the axes' values of the plot
    //       (i.e. they still have their default values), and then you switch to
    //       a different file. In that case, the axes' values won't be updated
    //       and the plot not replotted. That is, unless we replot the plot no
    //       matter what...

    QMap<SingleCellViewGraphPanelPlotWidget *, QRectF> plotsRects = mPlotsRects.value(pFileName);

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellViewGraphPanelPlotWidget *plot = qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i))->plot();

        QRectF dataRect = plotsRects.value(plot);

        if (dataRect == QRectF()) {
            if (!plot->resetAxes())
                plot->replotNow();
        } else {
            if (!plot->setAxes(dataRect))
                plot->replotNow();
        }
    }
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::backup(const QString &pFileName)
{
    // Keep track of our active graph panel

    mActiveGraphPanels.insert(pFileName, mActiveGraphPanel);

    // Keep track of the axes' values of our different plots

    QMap<SingleCellViewGraphPanelPlotWidget *, QRectF> plotsRects = QMap<SingleCellViewGraphPanelPlotWidget *, QRectF>();

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellViewGraphPanelPlotWidget *plot = qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i))->plot();

        plotsRects.insert(plot, QRectF(QPointF(plot->minX(), plot->minY()),
                                       QPointF(plot->maxX(), plot->maxY())));
    }

    mPlotsRects.insert(pFileName, plotsRects);
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::finalize(const QString &pFileName)
{
    // Remove track of our active graph panel and the axes' values of our
    // different plots

    mActiveGraphPanels.remove(pFileName);
    mPlotsRects.remove(pFileName);
}

//==============================================================================

QList<SingleCellViewGraphPanelWidget *> SingleCellViewGraphPanelsWidget::graphPanels() const
{
    // Return all our graph panels

    QList<SingleCellViewGraphPanelWidget *> res = QList<SingleCellViewGraphPanelWidget *>();

    for (int i = 0, iMax = count(); i < iMax; ++i)
        res << qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i));

    return res;
}

//==============================================================================

SingleCellViewGraphPanelWidget * SingleCellViewGraphPanelsWidget::activeGraphPanel() const
{
    // Return our active graph panel

    return mActiveGraphPanel;
}

//==============================================================================

SingleCellViewGraphPanelWidget * SingleCellViewGraphPanelsWidget::addGraphPanel()
{
    // Keep track of the graph panels' original size

    QList<int> origSizes = sizes();

    // Create a new graph panel

    SingleCellViewGraphPanelWidget *res = new SingleCellViewGraphPanelWidget(this);

    // Add the graph panel to ourselves

    addWidget(res);

    // Resize the graph panels, thus making sure that their size is what it
    // should be (see issue #58)

    double scalingFactor = double(count()-1)/count();

    for (int i = 0, iMax = origSizes.count(); i < iMax; ++i)
        origSizes[i] *= scalingFactor;

    setSizes(origSizes << height()/count());

    // Keep track of whenever a graph panel gets activated

    connect(res, SIGNAL(activated(SingleCellViewGraphPanelWidget *)),
            this, SIGNAL(graphPanelActivated(SingleCellViewGraphPanelWidget *)));

    connect(res, SIGNAL(activated(SingleCellViewGraphPanelWidget *)),
            this, SLOT(updateGraphPanels(SingleCellViewGraphPanelWidget *)));

    // Keep track of the addition and removal of a graph

    connect(res, SIGNAL(graphAdded(SingleCellViewGraphPanelPlotWidget *, SingleCellViewGraphPanelPlotGraph *)),
            this, SIGNAL(graphAdded(SingleCellViewGraphPanelPlotWidget *, SingleCellViewGraphPanelPlotGraph *)));
    connect(res, SIGNAL(graphsRemoved(SingleCellViewGraphPanelPlotWidget *, const QList<SingleCellViewGraphPanelPlotGraph *> &)),
            this, SIGNAL(graphsRemoved(SingleCellViewGraphPanelPlotWidget *, const QList<SingleCellViewGraphPanelPlotGraph *> &)));

    // Activate the graph panel

    res->setActive(true);

    // Keep track of our new sizes

    splitterMoved();

    // Let people know that we have added a graph panel

    emit graphPanelAdded(res);

    // Let people know whether graph panels can be removed

    emit removeGraphPanelsEnabled(count() > 1);

    // Return our newly created graph panel

    return res;
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::removeGraphPanel(SingleCellViewGraphPanelWidget *pGraphPanel)
{
    if (!pGraphPanel)
        return;

    // Retrieve the index of the given graph panel

    int index = indexOf(pGraphPanel);

    // Hide the graph panel and let people know that we have removed it (or,
    // rather, about to remove it)
    // Note: we let people know before we actually delete the graph panel,
    //       because some people interested in that signal might have used the
    //       pointer to keep track of some information, as is done in
    //       SingleCellViewInformationGraphsWidget for example...

    pGraphPanel->hide();

    emit graphPanelRemoved(pGraphPanel);

    // Now, we can delete our graph panel

    delete pGraphPanel;

    // Let people know whether graph panels can be removed

    emit removeGraphPanelsEnabled(count() > 1);

    // Activate the next graph panel or the last one available, if any

    if (index < count())
        // There is a next graph panel, so activate it

        qobject_cast<SingleCellViewGraphPanelWidget *>(widget(index))->setActive(true);
    else
        // We were dealing with the last graph panel, so just activate the new
        // last graph panel

        qobject_cast<SingleCellViewGraphPanelWidget *>(widget(count()-1))->setActive(true);

    // Keep track of our new sizes

    splitterMoved();
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::removeCurrentGraphPanel()
{
    // Make sure that we don't have only one graph panel left

    if (count() == 1)
        return;

    // Remove the current graph panel

    removeGraphPanel(mActiveGraphPanel);
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::removeAllGraphPanels()
{
    // Make sure that we don't have only one graph panel left

    if (count() == 1)
        return;

    // Remove all the graph panels but one
    // Note: the one we keep is the very first one since it may be the user's
    //       most important graph panel...

    while (count() > 1)
        removeGraphPanel(qobject_cast<SingleCellViewGraphPanelWidget *>(widget(count()-1)));
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::splitterMoved()
{
    // Our splitter has been moved, so keep track of its new sizes

    mSplitterSizes = sizes();
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::updateGraphPanels(SingleCellViewGraphPanelWidget *pGraphPanel)
{
    // Keep track of the newly activated graph panel

    mActiveGraphPanel = pGraphPanel;

    // Inactivate all the other graph panels

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellViewGraphPanelWidget *graphPanel = qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i));

        if (graphPanel != pGraphPanel)
            // We are not dealing with the graph panel that just got activated,
            // so inactivate it

            graphPanel->setActive(false);
    }
}

//==============================================================================

}   // namespace SingleCellView
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Fixed a small issue with f21e5d21cca64301583e5bf337a8f0c0d6f5b486.<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// Single cell view graph panels widget
//==============================================================================

#include "singlecellviewgraphpanelplotwidget.h"
#include "singlecellviewgraphpanelswidget.h"
#include "singlecellviewgraphpanelwidget.h"

//==============================================================================

#include <QSettings>
#include <QWheelEvent>

//==============================================================================

namespace OpenCOR {
namespace SingleCellView {

//==============================================================================

SingleCellViewGraphPanelsWidget::SingleCellViewGraphPanelsWidget(QWidget *pParent) :
    QSplitter(pParent),
    CommonWidget(pParent),
    mSplitterSizes(QList<int>()),
    mActiveGraphPanels(QMap<QString, SingleCellViewGraphPanelWidget *>()),
    mActiveGraphPanel(0),
    mPlotsRects(QMap<QString, QMap<SingleCellViewGraphPanelPlotWidget *, QRectF> >())
{
    // Set our orientation

    setOrientation(Qt::Vertical);

    // Keep track of our movement
    // Note: we need to keep track of our movement so that saveSettings() can
    //       work fine even when we are not visible (which happens when a CellML
    //       file cannot be run for some reason or another)...

    connect(this, SIGNAL(splitterMoved(int, int)),
            this, SLOT(splitterMoved()));
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::retranslateUi()
{
    // Retranslate all our graph panels

    for (int i = 0, iMax = count(); i < iMax; ++i)
        qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i))->retranslateUi();
}

//==============================================================================

static const auto SettingsGraphPanelSizes = QStringLiteral("GraphPanelSizes");

//==============================================================================

void SingleCellViewGraphPanelsWidget::loadSettings(QSettings *pSettings)
{
    // Let the user know of a few default things about ourselves by emitting a
    // few signals

    emit removeGraphPanelsEnabled(false);

    // Retrieve the number of graph panels and create the corresponding number
    // of graphs
    // Note: we don't assign the value of SettingsGraphPanelSizes directly to
    //       mSplitterSizes because adding a graph panel will reset it. So,
    //       instead, we assign the value to splitterSizes, which we then use to
    //       properly initialise mSplitterSizes...

    qRegisterMetaTypeStreamOperators< QList<int> >("QList<int>");

    QVariant defaultGraphPanelSizes = QVariant::fromValue< QList<int> >(QList<int>());

    QList<int> splitterSizes = pSettings->value(SettingsGraphPanelSizes, defaultGraphPanelSizes).value< QList<int> >();
    int graphPanelsCount = splitterSizes.count();

    if (!graphPanelsCount)
        // For some reason, the settings for the number of graph panels to be
        // created got messed up, so...

        graphPanelsCount = 1;

    for (int i = 0; i < graphPanelsCount; ++i)
        addGraphPanel();

    // Retrieve and set the size of each graph panel

    mSplitterSizes = splitterSizes;

    setSizes(mSplitterSizes);
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::saveSettings(QSettings *pSettings) const
{
    // Keep track of the size of each graph panel

    pSettings->setValue(SettingsGraphPanelSizes, QVariant::fromValue< QList<int> >(mSplitterSizes));
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::initialize(const QString &pFileName)
{
    // Set the active graph panel or select the first one, if no backup exists

    SingleCellViewGraphPanelWidget *activeGraphPanel = mActiveGraphPanels.value(pFileName);

    if (activeGraphPanel)
        activeGraphPanel->setActive(true);
    else
        qobject_cast<SingleCellViewGraphPanelWidget *>(widget(0))->setActive(true);

    // Update our plots' axes' values
    // Note: we always want our plot to be replotted. Indeed, say that you plot
    //       a graph that doesn't require changing the axes' values of the plot
    //       (i.e. they still have their default values), and then you switch to
    //       a different file. In that case, the axes' values won't be updated
    //       and the plot not replotted. That is, unless we replot the plot no
    //       matter what...

    QMap<SingleCellViewGraphPanelPlotWidget *, QRectF> plotsRects = mPlotsRects.value(pFileName);

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellViewGraphPanelPlotWidget *plot = qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i))->plot();

        QRectF dataRect = plotsRects.value(plot);

        if (dataRect == QRectF()) {
            if (!plot->resetAxes())
                plot->replotNow();
        } else {
            if (!plot->setAxes(dataRect))
                plot->replotNow();
        }
    }
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::backup(const QString &pFileName)
{
    // Keep track of our active graph panel

    mActiveGraphPanels.insert(pFileName, mActiveGraphPanel);

    // Keep track of the axes' values of our different plots

    QMap<SingleCellViewGraphPanelPlotWidget *, QRectF> plotsRects = QMap<SingleCellViewGraphPanelPlotWidget *, QRectF>();

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellViewGraphPanelPlotWidget *plot = qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i))->plot();

        plotsRects.insert(plot, QRectF(QPointF(plot->minX(), plot->minY()),
                                       QPointF(plot->maxX(), plot->maxY())));
    }

    mPlotsRects.insert(pFileName, plotsRects);
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::finalize(const QString &pFileName)
{
    // Remove track of our active graph panel and the axes' values of our
    // different plots

    mActiveGraphPanels.remove(pFileName);
    mPlotsRects.remove(pFileName);
}

//==============================================================================

QList<SingleCellViewGraphPanelWidget *> SingleCellViewGraphPanelsWidget::graphPanels() const
{
    // Return all our graph panels

    QList<SingleCellViewGraphPanelWidget *> res = QList<SingleCellViewGraphPanelWidget *>();

    for (int i = 0, iMax = count(); i < iMax; ++i)
        res << qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i));

    return res;
}

//==============================================================================

SingleCellViewGraphPanelWidget * SingleCellViewGraphPanelsWidget::activeGraphPanel() const
{
    // Return our active graph panel

    return mActiveGraphPanel;
}

//==============================================================================

SingleCellViewGraphPanelWidget * SingleCellViewGraphPanelsWidget::addGraphPanel()
{
    // Keep track of the graph panels' original size

    QList<int> origSizes = sizes();

    // Create a new graph panel

    SingleCellViewGraphPanelWidget *res = new SingleCellViewGraphPanelWidget(this);

    // Add the graph panel to ourselves

    addWidget(res);

    // Resize the graph panels, thus making sure that their size is what it
    // should be (see issue #58)

    double scalingFactor = double(count()-1)/count();

    for (int i = 0, iMax = origSizes.count(); i < iMax; ++i)
        origSizes[i] *= scalingFactor;

    setSizes(origSizes << height()/count());

    // Keep track of whenever a graph panel gets activated

    connect(res, SIGNAL(activated(SingleCellViewGraphPanelWidget *)),
            this, SIGNAL(graphPanelActivated(SingleCellViewGraphPanelWidget *)));

    connect(res, SIGNAL(activated(SingleCellViewGraphPanelWidget *)),
            this, SLOT(updateGraphPanels(SingleCellViewGraphPanelWidget *)));

    // Keep track of the addition and removal of a graph

    connect(res, SIGNAL(graphAdded(SingleCellViewGraphPanelPlotWidget *, SingleCellViewGraphPanelPlotGraph *)),
            this, SIGNAL(graphAdded(SingleCellViewGraphPanelPlotWidget *, SingleCellViewGraphPanelPlotGraph *)));
    connect(res, SIGNAL(graphsRemoved(SingleCellViewGraphPanelPlotWidget *, const QList<SingleCellViewGraphPanelPlotGraph *> &)),
            this, SIGNAL(graphsRemoved(SingleCellViewGraphPanelPlotWidget *, const QList<SingleCellViewGraphPanelPlotGraph *> &)));

    // Activate the graph panel

    res->setActive(true);

    // Keep track of our new sizes

    splitterMoved();

    // Let people know that we have added a graph panel

    emit graphPanelAdded(res);

    // Let people know whether graph panels can be removed

    emit removeGraphPanelsEnabled(count() > 1);

    // Return our newly created graph panel

    return res;
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::removeGraphPanel(SingleCellViewGraphPanelWidget *pGraphPanel)
{
    if (!pGraphPanel)
        return;

    // Retrieve the index of the given graph panel

    int index = indexOf(pGraphPanel);

    // Hide the graph panel and let people know that we have removed it (or,
    // rather, about to remove it)
    // Note: we let people know before we actually delete the graph panel,
    //       because some people interested in that signal might have used the
    //       pointer to keep track of some information, as is done in
    //       SingleCellViewInformationGraphsWidget for example...

    pGraphPanel->hide();

    emit graphPanelRemoved(pGraphPanel);

    // Now, we can delete our graph panel

    delete pGraphPanel;

    // Let people know whether graph panels can be removed

    emit removeGraphPanelsEnabled(count() > 1);

    // Activate the next graph panel or the last one available, if any

    if (index < count())
        // There is a next graph panel, so activate it

        qobject_cast<SingleCellViewGraphPanelWidget *>(widget(index))->setActive(true);
    else
        // We were dealing with the last graph panel, so just activate the new
        // last graph panel

        qobject_cast<SingleCellViewGraphPanelWidget *>(widget(count()-1))->setActive(true);

    // Keep track of our new sizes

    splitterMoved();
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::removeCurrentGraphPanel()
{
    // Make sure that we don't have only one graph panel left

    if (count() == 1)
        return;

    // Remove the current graph panel

    removeGraphPanel(mActiveGraphPanel);
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::removeAllGraphPanels()
{
    // Make sure that we don't have only one graph panel left

    if (count() == 1)
        return;

    // Remove all the graph panels but one
    // Note: the one we keep is the very first one since it may be the user's
    //       most important graph panel...

    while (count() > 1)
        removeGraphPanel(qobject_cast<SingleCellViewGraphPanelWidget *>(widget(count()-1)));
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::splitterMoved()
{
    // Our splitter has been moved, so keep track of its new sizes

    mSplitterSizes = sizes();
}

//==============================================================================

void SingleCellViewGraphPanelsWidget::updateGraphPanels(SingleCellViewGraphPanelWidget *pGraphPanel)
{
    // Keep track of the newly activated graph panel

    mActiveGraphPanel = pGraphPanel;

    // Inactivate all the other graph panels

    for (int i = 0, iMax = count(); i < iMax; ++i) {
        SingleCellViewGraphPanelWidget *graphPanel = qobject_cast<SingleCellViewGraphPanelWidget *>(widget(i));

        if (graphPanel != pGraphPanel)
            // We are not dealing with the graph panel that just got activated,
            // so inactivate it

            graphPanel->setActive(false);
    }
}

//==============================================================================

}   // namespace SingleCellView
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>#include "../include/chase.h"

template <typename T>
void swap_kj(int k, int j, T* array);

void chase(MKL_Complex16* const H, int N, MKL_Complex16* V, MKL_Complex16* W,
           double* ritzv_, int nev, const int nex, int deg_, int* const degrees_,
           const double tol_, const CHASE_MODE_TYPE int_mode,
           const CHASE_OPT_TYPE int_opt)
{
  start_clock( TimePtrs::All );

  chase_filtered_vecs = 0;
  const int nevex = nev + nex; // Block size for the algorithm.
  int unconverged = nev + nex;

  // To store the approximations obtained from lanczos().
  double lowerb, upperb, lambda;
  double normH = std::max(LAPACKE_zlange(LAPACK_COL_MAJOR, '1', N, N, H, N), 1.0);
  const double tol = tol_ * normH;
  double* resid_ = new double[nevex];

  // store input values
  int deg = deg_;
  int * degrees = degrees_;
  double * ritzv = ritzv_;
  double * resid = resid_;

  MKL_Complex16 *V_Full = V;
  MKL_Complex16 *W_Full = W;

  //-------------------------------- VALIDATION --------------------------------
  assert(degrees != NULL);
  deg = std::min( deg, chase_max_deg );
  for( auto i = 0; i < nevex; ++i )
    degrees[i] = deg;

  // --------------------------------- LANCZOS ---------------------------------
  start_clock( TimePtrs::Lanczos );
  bool random = int_mode == CHASE_MODE_RANDOM;
  int vecLanczos = lanczos(H, N, 4, random? std::max(1,nevex/4) : chase_lanczos_iter, nevex, &upperb,
          random, random? ritzv : NULL, random? V : NULL );

  end_clock( TimePtrs::Lanczos );
  int locked = 0; // Number of converged eigenpairs.
  int iteration = 0; // Current iteration.

  while( unconverged > nex && iteration < 4*chase_max_iter)
  {
    lambda = * std::min_element( ritzv_, ritzv_ + nevex );
    lowerb = * std::max_element( ritzv, ritzv + unconverged );
    //upperb = lowerb + std::abs(lowerb - lambda);
    assert( lowerb < upperb );
#ifdef OUTPUT
    std::cout
      << "iteration: " << iteration
      << "\t" << lambda << " " << lowerb << " " << upperb
      << " " << unconverged
      << std::endl;
#endif
    //--------------------------------- FILTER ---------------------------------
    if( int_opt != CHASE_OPT_NONE && iteration != 0 )
    {
      deg = calc_degrees(
        N, unconverged, nex, upperb, lowerb, tol, ritzv,
        resid, degrees, V, W );
    }

    start_clock( TimePtrs::Filter );
#ifdef CHASE_BUILD_CUDA

    int Av = cuda_filter(
      H, V, N, unconverged, deg, degrees,
      lambda, lowerb, upperb, W );

#else

    int Av = filter(
      H, V, N, unconverged, deg, degrees,
      lambda, lowerb, upperb, W );

#endif
    end_clock( TimePtrs::Filter );

    chase_filtered_vecs += Av;

    if( deg % 2 == 0 ) // To 'fix' the situation if the filter swapped them.
    {
      std::swap( V, W );
      std::swap( V_Full, W_Full );
    }
    // Solution is now in W

    //----------------------------------- QR -----------------------------------
    // Orthogonalize W_Full, then copy the locked vector from V_Full to W_Full
    QR( N, nevex, locked, W_Full, V, V_Full);

    // ----------------------------- RAYLEIGH  RITZ ----------------------------
    // returns eigenvectors in V
    RR( N, unconverged, ritzv, H, V, W );

    // -------------------------------- RESIDUAL -------------------------------
    // overwrites W with H*V
    calc_residuals( N, unconverged, normH, ritzv, resid, H, V, W );

    // -------------------------------- LOCKING --------------------------------
    int new_converged = locking(
      N, unconverged, tol, ritzv, resid, degrees, V );

    // ---------------------------- Update pointers ----------------------------
    // Since we double buffer we need the entire locked portion in W and V
    memcpy( W, V, N*new_converged*sizeof(MKL_Complex16) );

    locked += new_converged;
    unconverged -= new_converged;

    W += N*new_converged;
    V += N*new_converged;
    resid += new_converged;
    ritzv += new_converged;
    degrees += new_converged;

    iteration++;
  } // while ( converged < nev && iteration < omp_maxiter )

  //---------------------SORT-EIGENPAIRS-ACCORDING-TO-EIGENVALUES---------------
  for( auto i = 0; i < nev-1; ++i )
    for( auto j = i+1; j < nev; ++j )
    {
      if( ritzv_[i] > ritzv_[j] )
      {
        swap_kj( i, j, ritzv_ );
        ColSwap( W_Full, N, i, j );
        ColSwap( V_Full, N, i, j );
      }
    }

  chase_iteration_count = iteration;
  delete[] resid_;

  end_clock( TimePtrs::All );
}

int get_iter_count()
{
  return chase_iteration_count;
}

int get_filtered_vecs()
{
  return chase_filtered_vecs;
}

template <typename T>
void swap_kj(int k, int j, T* array)
{
  T tmp = array[k];
  array[k] = array[j];
  array[j] = tmp;
}

void ColSwap( MKL_Complex16 *V, int N, int i, int j )
{
  MKL_Complex16 *ztmp = new MKL_Complex16[N];
  memcpy(ztmp,  V+N*i, N*sizeof(MKL_Complex16));
  memcpy(V+N*i, V+N*j, N*sizeof(MKL_Complex16));
  memcpy(V+N*j, ztmp,  N*sizeof(MKL_Complex16));
  delete[] ztmp;
}

int calc_degrees( int N, int unconverged, int nex,  double upperb, double lowerb,
                  double tol, double *ritzv, double *resid, int *degrees,
                  MKL_Complex16 *V, MKL_Complex16 *W)
{
  start_clock( TimePtrs::Degrees );
  double c = (upperb + lowerb) / 2; // Center of the interval.
  double e = (upperb - lowerb) / 2; // Half-length of the interval.
  double rho;

  for( auto i = 0; i < unconverged-nex; ++i )
  {
    double t = (ritzv[i] - c)/e;
    rho = std::max(
      std::abs( t - sqrt( t*t-1 ) ),
      std::abs( t + sqrt( t*t-1 ) )
      );

    degrees[i] = ceil(std::abs(log(resid[i]/(tol))/log(rho)));
    degrees[i] = std::min(
      degrees[i] + chase_deg_extra,
      chase_max_deg
      );
  }

  for( auto i = unconverged-nex; i < unconverged; ++i )
  {
    degrees[i] = degrees[unconverged-1-nex];
  }

  for( auto  j = 0; j < unconverged-1; ++j )
    for( auto  k = j; k < unconverged; ++k )
      if( degrees[k] < degrees[j] )
      {
        swap_kj(k, j, degrees); // for filter
        swap_kj(k, j, ritzv);
//        swap_kj(k, j, resid);
        ColSwap( V, N, k, j );
      }

  end_clock( TimePtrs::Degrees );
  return degrees[unconverged-1];
}

int locking( int N, int unconverged, double tol,
             double *ritzv, double *resid, int *degrees,
             MKL_Complex16 *V)
{
  int converged = 0;
  for (int j = 0; j < unconverged; ++j)
  {
    if (resid[j] > tol) continue;
    if (j != converged)
    {
      swap_kj( j, converged, resid ); // if we filter again
      swap_kj( j, converged, ritzv );
      ColSwap( V, N, j, converged );
    }
    converged++;
  }
  end_clock( TimePtrs::Resids_Locking );
  return converged;
}

void calc_residuals(
  int N, int unconverged, double norm,
  double *ritzv, double *resid, MKL_Complex16 *H, MKL_Complex16 *V,
  MKL_Complex16 *W)
{
  start_clock( TimePtrs::Resids_Locking );
  MKL_Complex16 alpha = MKL_Complex16 (1.0, 0.0);
  MKL_Complex16 beta  = MKL_Complex16 (0.0, 0.0);

  cblas_zgemm(
    CblasColMajor,    CblasNoTrans,    CblasNoTrans,
    N,    unconverged,    N,
    &alpha,
    H,    N,
    V,    N,
    &beta,
    W,    N
    );

  double norm1, norm2;
  for( int i = 0 ; i < unconverged ; ++i )
  {
    beta  = MKL_Complex16 (-ritzv[i], 0.0);
    cblas_zaxpy( N, &beta, V+N*i, 1, W+N*i, 1);

    norm1 = cblas_dznrm2( N, W+N*i, 1);
    resid[i] = norm1/norm;
  }
}

void QR( int N, int nevex, int converged, MKL_Complex16 *W,
         MKL_Complex16 *tau, MKL_Complex16 *saveW )
{
  start_clock( TimePtrs::Qr );
  // TODO: don't make lapacke do the memory management
  // |-> lapack_zgeqrf
  LAPACKE_zgeqrf(
    LAPACK_COL_MAJOR,
    N,    nevex,
    W,    N,
    tau
    );

  LAPACKE_zungqr(
    LAPACK_COL_MAJOR,
    N,    nevex,    nevex,
    W,    N,
    tau
    );

  memcpy( W, saveW, N*converged*sizeof(MKL_Complex16) );
  end_clock( TimePtrs::Qr );
}

void RR( int N, int block, double *Lambda,
         MKL_Complex16 *H, MKL_Complex16 *V, MKL_Complex16 *W )
{
  start_clock( TimePtrs::Rr );
  MKL_Complex16 * A = new MKL_Complex16[block*block]; // For LAPACK.

  MKL_Complex16 One = MKL_Complex16 (1.0, 0.0);
  MKL_Complex16 Zero  = MKL_Complex16 (0.0, 0.0);

  // V <- H*V
  cblas_zgemm(
    CblasColMajor,    CblasNoTrans,    CblasNoTrans,
    N,    block,    N,
    &One,
    H,    N,
    W,    N,
    &Zero,
    V,    N
    );

  // A <- W * V
  cblas_zgemm(
    CblasColMajor,    CblasConjTrans,    CblasNoTrans,
    block,    block,    N,
    &One,
    W,    N,
    V,    N,
    &Zero,
    A,    block
    );

  LAPACKE_zheevd(
    LAPACK_COL_MAJOR,
    'V',    'L',
    block,
    A,    block,
    Lambda
    );

  cblas_zgemm(
    CblasColMajor,    CblasNoTrans,    CblasNoTrans,
    N,    block,    block,
    &One,
    W,    N,
    A,    block,
    &Zero,
    V,    N
    );

  delete[] A;

  end_clock( TimePtrs::Rr );
}
<commit_msg>Only change lowerb & lambda if at least one vector converged<commit_after>#include "../include/chase.h"

template <typename T>
void swap_kj(int k, int j, T* array);

void chase(MKL_Complex16* const H, int N, MKL_Complex16* V, MKL_Complex16* W,
           double* ritzv_, int nev, const int nex, int deg_, int* const degrees_,
           const double tol_, const CHASE_MODE_TYPE int_mode,
           const CHASE_OPT_TYPE int_opt)
{
  start_clock( TimePtrs::All );

  chase_filtered_vecs = 0;
  const int nevex = nev + nex; // Block size for the algorithm.
  int unconverged = nev + nex;

  // To store the approximations obtained from lanczos().
  double lowerb, upperb, lambda;
  double normH = std::max(LAPACKE_zlange(LAPACK_COL_MAJOR, '1', N, N, H, N), 1.0);
  const double tol = tol_ * normH;
  double* resid_ = new double[nevex];

  // store input values
  int deg = deg_;
  int * degrees = degrees_;
  double * ritzv = ritzv_;
  double * resid = resid_;

  MKL_Complex16 *V_Full = V;
  MKL_Complex16 *W_Full = W;

  //-------------------------------- VALIDATION --------------------------------
  assert(degrees != NULL);
  deg = std::min( deg, chase_max_deg );
  for( auto i = 0; i < nevex; ++i )
    degrees[i] = deg;

  // --------------------------------- LANCZOS ---------------------------------
  start_clock( TimePtrs::Lanczos );
  bool random = int_mode == CHASE_MODE_RANDOM;
  int vecLanczos = lanczos(H, N, 4, random? std::max(1,nevex/4) : chase_lanczos_iter, nevex, &upperb,
          random, random? ritzv : NULL, random? V : NULL );

  end_clock( TimePtrs::Lanczos );
  int locked = 0; // Number of converged eigenpairs.
  int iteration = 0; // Current iteration.

  while( unconverged > nex && iteration < 4*chase_max_iter)
  {
    if(unconverged < nevex || iteration == 0){
      lambda = * std::min_element( ritzv_, ritzv_ + nevex );
      lowerb = * std::max_element( ritzv, ritzv + unconverged );
      //upperb = lowerb + std::abs(lowerb - lambda);
    }
    assert( lowerb < upperb );
#ifdef OUTPUT
    std::cout
      << "iteration: " << iteration
      << "\t" << lambda << " " << lowerb << " " << upperb
      << " " << unconverged
      << std::endl;
#endif
    //--------------------------------- FILTER ---------------------------------
    if( int_opt != CHASE_OPT_NONE && iteration != 0 )
    {
      deg = calc_degrees(
        N, unconverged, nex, upperb, lowerb, tol, ritzv,
        resid, degrees, V, W );
    }

    start_clock( TimePtrs::Filter );
#ifdef CHASE_BUILD_CUDA

    int Av = cuda_filter(
      H, V, N, unconverged, deg, degrees,
      lambda, lowerb, upperb, W );

#else

    int Av = filter(
      H, V, N, unconverged, deg, degrees,
      lambda, lowerb, upperb, W );

#endif
    end_clock( TimePtrs::Filter );

    chase_filtered_vecs += Av;

    if( deg % 2 == 0 ) // To 'fix' the situation if the filter swapped them.
    {
      std::swap( V, W );
      std::swap( V_Full, W_Full );
    }
    // Solution is now in W

    //----------------------------------- QR -----------------------------------
    // Orthogonalize W_Full, then copy the locked vector from V_Full to W_Full
    QR( N, nevex, locked, W_Full, V, V_Full);

    // ----------------------------- RAYLEIGH  RITZ ----------------------------
    // returns eigenvectors in V
    RR( N, unconverged, ritzv, H, V, W );

    // -------------------------------- RESIDUAL -------------------------------
    // overwrites W with H*V
    calc_residuals( N, unconverged, normH, ritzv, resid, H, V, W );

    // -------------------------------- LOCKING --------------------------------
    int new_converged = locking(
      N, unconverged, tol, ritzv, resid, degrees, V );

    // ---------------------------- Update pointers ----------------------------
    // Since we double buffer we need the entire locked portion in W and V
    memcpy( W, V, N*new_converged*sizeof(MKL_Complex16) );

    locked += new_converged;
    unconverged -= new_converged;

    W += N*new_converged;
    V += N*new_converged;
    resid += new_converged;
    ritzv += new_converged;
    degrees += new_converged;

    iteration++;
  } // while ( converged < nev && iteration < omp_maxiter )

  //---------------------SORT-EIGENPAIRS-ACCORDING-TO-EIGENVALUES---------------
  for( auto i = 0; i < nev-1; ++i )
    for( auto j = i+1; j < nev; ++j )
    {
      if( ritzv_[i] > ritzv_[j] )
      {
        swap_kj( i, j, ritzv_ );
        ColSwap( W_Full, N, i, j );
        ColSwap( V_Full, N, i, j );
      }
    }

  chase_iteration_count = iteration;
  delete[] resid_;

  end_clock( TimePtrs::All );
}

int get_iter_count()
{
  return chase_iteration_count;
}

int get_filtered_vecs()
{
  return chase_filtered_vecs;
}

template <typename T>
void swap_kj(int k, int j, T* array)
{
  T tmp = array[k];
  array[k] = array[j];
  array[j] = tmp;
}

void ColSwap( MKL_Complex16 *V, int N, int i, int j )
{
  MKL_Complex16 *ztmp = new MKL_Complex16[N];
  memcpy(ztmp,  V+N*i, N*sizeof(MKL_Complex16));
  memcpy(V+N*i, V+N*j, N*sizeof(MKL_Complex16));
  memcpy(V+N*j, ztmp,  N*sizeof(MKL_Complex16));
  delete[] ztmp;
}

int calc_degrees( int N, int unconverged, int nex,  double upperb, double lowerb,
                  double tol, double *ritzv, double *resid, int *degrees,
                  MKL_Complex16 *V, MKL_Complex16 *W)
{
  start_clock( TimePtrs::Degrees );
  double c = (upperb + lowerb) / 2; // Center of the interval.
  double e = (upperb - lowerb) / 2; // Half-length of the interval.
  double rho;

  for( auto i = 0; i < unconverged-nex; ++i )
  {
    double t = (ritzv[i] - c)/e;
    rho = std::max(
      std::abs( t - sqrt( t*t-1 ) ),
      std::abs( t + sqrt( t*t-1 ) )
      );

    degrees[i] = ceil(std::abs(log(resid[i]/(tol))/log(rho)));
    degrees[i] = std::min(
      degrees[i] + chase_deg_extra,
      chase_max_deg
      );
  }

  for( auto i = unconverged-nex; i < unconverged; ++i )
  {
    degrees[i] = degrees[unconverged-1-nex];
  }

  for( auto  j = 0; j < unconverged-1; ++j )
    for( auto  k = j; k < unconverged; ++k )
      if( degrees[k] < degrees[j] )
      {
        swap_kj(k, j, degrees); // for filter
        swap_kj(k, j, ritzv);
//        swap_kj(k, j, resid);
        ColSwap( V, N, k, j );
      }

  end_clock( TimePtrs::Degrees );
  return degrees[unconverged-1];
}

int locking( int N, int unconverged, double tol,
             double *ritzv, double *resid, int *degrees,
             MKL_Complex16 *V)
{
  int converged = 0;
  for (int j = 0; j < unconverged; ++j)
  {
    if (resid[j] > tol) continue;
    if (j != converged)
    {
      swap_kj( j, converged, resid ); // if we filter again
      swap_kj( j, converged, ritzv );
      ColSwap( V, N, j, converged );
    }
    converged++;
  }
  end_clock( TimePtrs::Resids_Locking );
  return converged;
}

void calc_residuals(
  int N, int unconverged, double norm,
  double *ritzv, double *resid, MKL_Complex16 *H, MKL_Complex16 *V,
  MKL_Complex16 *W)
{
  start_clock( TimePtrs::Resids_Locking );
  MKL_Complex16 alpha = MKL_Complex16 (1.0, 0.0);
  MKL_Complex16 beta  = MKL_Complex16 (0.0, 0.0);

  cblas_zgemm(
    CblasColMajor,    CblasNoTrans,    CblasNoTrans,
    N,    unconverged,    N,
    &alpha,
    H,    N,
    V,    N,
    &beta,
    W,    N
    );

  double norm1, norm2;
  for( int i = 0 ; i < unconverged ; ++i )
  {
    beta  = MKL_Complex16 (-ritzv[i], 0.0);
    cblas_zaxpy( N, &beta, V+N*i, 1, W+N*i, 1);

    norm1 = cblas_dznrm2( N, W+N*i, 1);
    resid[i] = norm1/norm;
  }
}

void QR( int N, int nevex, int converged, MKL_Complex16 *W,
         MKL_Complex16 *tau, MKL_Complex16 *saveW )
{
  start_clock( TimePtrs::Qr );
  // TODO: don't make lapacke do the memory management
  // |-> lapack_zgeqrf
  LAPACKE_zgeqrf(
    LAPACK_COL_MAJOR,
    N,    nevex,
    W,    N,
    tau
    );

  LAPACKE_zungqr(
    LAPACK_COL_MAJOR,
    N,    nevex,    nevex,
    W,    N,
    tau
    );

  memcpy( W, saveW, N*converged*sizeof(MKL_Complex16) );
  end_clock( TimePtrs::Qr );
}

void RR( int N, int block, double *Lambda,
         MKL_Complex16 *H, MKL_Complex16 *V, MKL_Complex16 *W )
{
  start_clock( TimePtrs::Rr );
  MKL_Complex16 * A = new MKL_Complex16[block*block]; // For LAPACK.

  MKL_Complex16 One = MKL_Complex16 (1.0, 0.0);
  MKL_Complex16 Zero  = MKL_Complex16 (0.0, 0.0);

  // V <- H*V
  cblas_zgemm(
    CblasColMajor,    CblasNoTrans,    CblasNoTrans,
    N,    block,    N,
    &One,
    H,    N,
    W,    N,
    &Zero,
    V,    N
    );

  // A <- W * V
  cblas_zgemm(
    CblasColMajor,    CblasConjTrans,    CblasNoTrans,
    block,    block,    N,
    &One,
    W,    N,
    V,    N,
    &Zero,
    A,    block
    );

  LAPACKE_zheevd(
    LAPACK_COL_MAJOR,
    'V',    'L',
    block,
    A,    block,
    Lambda
    );

  cblas_zgemm(
    CblasColMajor,    CblasNoTrans,    CblasNoTrans,
    N,    block,    block,
    &One,
    W,    N,
    A,    block,
    &Zero,
    V,    N
    );

  delete[] A;

  end_clock( TimePtrs::Rr );
}
<|endoftext|>


<commit_before>#include "Vajra/Engine/AssetLibrary/AssetLibrary.h"
#include "Vajra/Engine/AssetLibrary/Assets/UiFontType/UiFontType.h"
#include "Vajra/Engine/Components/DerivedComponents/Armature/Armature.h"
#include "Vajra/Engine/Components/DerivedComponents/Renderer/UiFontRenderer.h"
#include "Vajra/Engine/Components/DerivedComponents/Transform/Transform.h"
#include "Vajra/Engine/Core/Engine.h"
#include "Vajra/Engine/GameObject/GameObject.h"
#include "Vajra/Engine/SceneGraph/SceneGraph3D.h"
#include "Vajra/Framework/OpenGL/OpenGLWrapper/OpenGLWrapper.h"
#include "Vajra/Framework/OpenGL/ShaderSet/ShaderSet.h"
#include "Vajra/Utilities/MathUtilities.h"

#ifdef __APPLE__
#include "TargetConditionals.h"
#endif

UiFontRenderer::UiFontRenderer() : Renderer() {
	this->init();
}

UiFontRenderer::UiFontRenderer(Object* object_) : Renderer(object_) {
	this->init();
}

UiFontRenderer::~UiFontRenderer() {
	this->destroy();
}

void UiFontRenderer::initTextToDisplay(std::string text, unsigned int width, unsigned int height, std::string pathToFontSpecificationFile, float fontSize_, UiFontAlignment_type fontAlignment_ /* = UI_FONT_ALIGNMENT_left */) {
	this->fontType = ENGINE->GetAssetLibrary()->GetAsset<UiFontType>(pathToFontSpecificationFile);
	this->SetShaderName(this->fontType->GetShaderName());

	this->fontAlignment = fontAlignment_;
	//
	this->fontSize = fontSize_;
	GameObject* gameObjectRef = (GameObject*)this->GetObject();
	ASSERT(gameObjectRef->GetClassType() & CLASS_TYPE_GAMEOBJECT, "Object is a game object");
	gameObjectRef->GetTransform()->SetScale(this->fontSize, this->fontSize, this->fontSize);

	this->required_width  = width;
	this->required_height = height;

	this->textToDisplay = text;
	this->makeText();
	this->initVbos();

	Renderer::addToRenderList();
}

void UiFontRenderer::changeText(std::string text) {
	this->textToDisplay = text;
	this->makeText();
	this->initVbos();
}

void UiFontRenderer::initVbos() {
    if (this->vboPositions != 0 && this->vboTextureCoords != 0 && this->vboIndices != 0) {
		GLCALL(glDeleteBuffers, 1, &this->vboPositions);
		GLCALL(glDeleteBuffers, 1, &this->vboTextureCoords);
		GLCALL(glDeleteBuffers, 1, &this->vboIndices);
    }

    if (this->vertices != nullptr) {
		GLCALL(glGenBuffers, 1, &this->vboPositions);
		GLCALL(glBindBuffer, GL_ARRAY_BUFFER, this->vboPositions);
		GLCALL(glBufferData, GL_ARRAY_BUFFER, sizeof(glm::vec3) * this->numVertices, this->vertices, GL_STATIC_DRAW);
    } else {
        FRAMEWORK->GetLogger()->errlog("ERROR: Uninited vertices");
        return;
    }

    if (this->textureCoords != nullptr) {
		GLCALL(glGenBuffers, 1, &this->vboTextureCoords);
		GLCALL(glBindBuffer, GL_ARRAY_BUFFER, this->vboTextureCoords);
		GLCALL(glBufferData, GL_ARRAY_BUFFER, sizeof(glm::vec2) * this->numVertices, this->textureCoords, GL_STATIC_DRAW);
    }

    if (this->indices.size() != 0) {
		GLCALL(glGenBuffers, 1, &this->vboIndices);
		GLCALL(glBindBuffer, GL_ELEMENT_ARRAY_BUFFER, this->vboIndices);
		GLCALL(glBufferData, GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * this->indices.size(), &this->indices[0], GL_STATIC_DRAW);
    } else {
        FRAMEWORK->GetLogger()->errlog("ERROR: Uninited indices");
        return;
    }

    FRAMEWORK->GetLogger()->errlog("\nVBOs made successfully");
}

void UiFontRenderer::HandleMessage(MessageChunk /* messageChunk */) {
}

void UiFontRenderer::Draw() {
	// Write Material properties to shader
	if (FRAMEWORK->GetOpenGLWrapper()->GetCurrentShaderSet()->HasHandle(SHADER_VARIABLE_VARIABLENAME_MaterialDiffuseColor)) {
		GLCALL(glUniform4f, FRAMEWORK->GetOpenGLWrapper()->GetCurrentShaderSet()->GetHandle(SHADER_VARIABLE_VARIABLENAME_MaterialDiffuseColor), this->diffuseColor.r, this->diffuseColor.g, this->diffuseColor.b, this->diffuseColor.a);
	}

	if (this->vboPositions == 0 || this->vboIndices == 0) {
        FRAMEWORK->GetLogger()->errlog("ERROR: VBOs not made");
        return;
    }

	ShaderSet* currentShaderSet = FRAMEWORK->GetOpenGLWrapper()->GetCurrentShaderSet();

    GLint positionHandle = currentShaderSet->GetHandle(SHADER_VARIABLE_VARIABLENAME_vPosition);
    GLCALL(glEnableVertexAttribArray, positionHandle);
    GLCALL(glBindBuffer, GL_ARRAY_BUFFER, this->vboPositions);
    GLCALL(glVertexAttribPointer, positionHandle, 3, GL_FLOAT, GL_FALSE, 0, 0);

    ASSERT(this->fontType->GetTextureAsset() != nullptr, "FontType has texture initialized");
	this->fontType->GetTextureAsset()->Draw(0);

	GLint textureCoordsHandle = currentShaderSet->GetHandle(SHADER_VARIABLE_VARIABLENAME_uvCoords_in);
	GLCALL(glEnableVertexAttribArray, textureCoordsHandle);
	GLCALL(glBindBuffer, GL_ARRAY_BUFFER, this->vboTextureCoords);
	GLCALL(glVertexAttribPointer, textureCoordsHandle, 2, GL_FLOAT, GL_FALSE, 0, 0);

    GLCALL(glBindBuffer, GL_ELEMENT_ARRAY_BUFFER, this->vboIndices);
    GLCALL(glDrawElements, GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, (void*)0);
}

void UiFontRenderer::init() {
	GameObject* gameObject = dynamic_cast<GameObject*>(this->GetObject());
	if (gameObject != nullptr) {
		ASSERT(typeid(gameObject) == typeid(GameObject*), "Type of Object* (%s) of id %d was %s", typeid(gameObject).name(), gameObject->GetId(), typeid(GameObject*).name());
	}

	this->numVertices = 0;
	this->vertices = nullptr;
	this->textureCoords = nullptr;
	this->diffuseColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);

	this->vboPositions = 0;
	this->vboTextureCoords = 0;
	this->vboIndices = 0;

	this->actual_width  = 0.0f;
	this->actual_height = 0.0f;
	//
	this->required_width  = 0.0f;
	this->required_height = 0.0f;
	//
	this->unscaled_width  = 0.0f;
	this->unscaled_height = 0.0f;

	this->fontSize = 1.0f;
	this->fontAlignment = UI_FONT_ALIGNMENT_left;
}

void UiFontRenderer::destroy() {
	// TODO [Hack] For some reason the simulator crashes in glDrawElements() when we do this
#ifdef __APPLE__
#if TARGET_IPHONE_SIMULATOR
	return;
#endif
#endif
	if (this->vertices != nullptr) {
		delete this->vertices;
	}
	if (this->textureCoords != nullptr) {
		delete this->textureCoords;
	}

    if (this->vboPositions != 0) {
    	FRAMEWORK->GetOpenGLWrapper()->FreeGLBuffer(&this->vboPositions);
    }
    if (this->vboTextureCoords != 0) {
    	FRAMEWORK->GetOpenGLWrapper()->FreeGLBuffer(&this->vboTextureCoords);
    }
    if (this->vboIndices != 0) {
    	FRAMEWORK->GetOpenGLWrapper()->FreeGLBuffer(&this->vboIndices);
    }
}

////////////////////////////////////////////////////////////////////////////////

void UiFontRenderer::makeText() {

	this->unscaled_width = 0.0f;

	unsigned int numVerticesNeeded = this->textToDisplay.size() * 4;
	if (numVerticesNeeded > this->numVertices) {
		if (this->vertices != nullptr) {
			delete this->vertices;
		}
		if (this->textureCoords != nullptr) {
			delete this->textureCoords;
		}
		this->vertices      = new glm::vec3[numVerticesNeeded];
		this->textureCoords = new glm::vec2[numVerticesNeeded];
		this->indices.resize(this->textToDisplay.size () * 6);
	}
	this->numVertices = numVerticesNeeded;

	float woffset = 0.0f;

	for (unsigned int i = 0; i < this->textToDisplay.size (); ++i) {
		char c = this->textToDisplay[i];
		int charIdx = (int) c;
		this->unscaled_width += this->makeACharacter(charIdx, i, woffset);
		woffset += this->fontType->GetCharacterWidth(charIdx);
	}

	float justText_width  = this->unscaled_width  * this->fontSize;

	this->actual_width  = std::max(this->required_width , this->unscaled_width  * this->fontSize);

	// Adjust for font alignment:
	if (this->required_width < justText_width) {
		// Nothing can be done.
	} else {
		switch (this->fontAlignment) {

		case UI_FONT_ALIGNMENT_left: {
			// Nothing to be done
		} break;

		case UI_FONT_ALIGNMENT_center: {
			float offset = (float)(this->required_width - justText_width) / this->fontSize / 2.0f;
			for (unsigned int i = 0; i < this->numVertices; ++i) {
				this->vertices[i].x += offset;
			}
		} break;

		case UI_FONT_ALIGNMENT_right: {
			float offset = (this->required_width - justText_width) / this->fontSize;
			for (unsigned int i = 0; i < this->numVertices; ++i) {
				this->vertices[i].x += offset;
			}
		} break;

		default: {
		} break;

		}
	}
}

float UiFontRenderer::makeACharacter(int charIdxInAscii, int letterIdx, float woffset) {

	float actual_charwidth_on_fontsheet = this->fontType->GetCharacterWidth(charIdxInAscii);

	this->vertices[letterIdx * 4 + 0].x = woffset;
	this->vertices[letterIdx * 4 + 1].x = woffset + actual_charwidth_on_fontsheet;
	this->vertices[letterIdx * 4 + 2].x = woffset + actual_charwidth_on_fontsheet;
	this->vertices[letterIdx * 4 + 3].x = woffset;

	this->vertices[letterIdx * 4 + 0].y = 0.0f;
	this->vertices[letterIdx * 4 + 1].y = 0.0f;
	this->vertices[letterIdx * 4 + 2].y = 0.0f - 1.0f;
	this->vertices[letterIdx * 4 + 3].y = 0.0f - 1.0f;

	// TODO [Hack] Use zorder instead of 0.1f
	this->vertices[letterIdx * 4 + 0].z = 0.1f;
	this->vertices[letterIdx * 4 + 1].z = 0.1f;
	this->vertices[letterIdx * 4 + 2].z = 0.1f;
	this->vertices[letterIdx * 4 + 3].z = 0.1f;

	float each_charwidth_on_fontsheet = 1.0f / NUM_FONT_CHARACTER_COLUMNS;
	float each_charheight_on_fontsheet = 1.0f / NUM_FONT_CHARACTER_ROWS;
	float woffest_into_fontsheet = ((charIdxInAscii - this->fontType->GetStartingCharacter()) % NUM_FONT_CHARACTER_COLUMNS) * each_charwidth_on_fontsheet;
	float hoffest_into_fontsheet = ((charIdxInAscii - this->fontType->GetStartingCharacter()) / NUM_FONT_CHARACTER_ROWS) * each_charheight_on_fontsheet;

	this->textureCoords[letterIdx * 4 + 0].x = woffest_into_fontsheet;
	this->textureCoords[letterIdx * 4 + 1].x = woffest_into_fontsheet + each_charwidth_on_fontsheet * actual_charwidth_on_fontsheet;
	this->textureCoords[letterIdx * 4 + 2].x = woffest_into_fontsheet + each_charwidth_on_fontsheet * actual_charwidth_on_fontsheet;
	this->textureCoords[letterIdx * 4 + 3].x = woffest_into_fontsheet;

	this->textureCoords[letterIdx * 4 + 0].y = hoffest_into_fontsheet;
	this->textureCoords[letterIdx * 4 + 1].y = hoffest_into_fontsheet;
	this->textureCoords[letterIdx * 4 + 2].y = hoffest_into_fontsheet + each_charheight_on_fontsheet;
	this->textureCoords[letterIdx * 4 + 3].y = hoffest_into_fontsheet + each_charheight_on_fontsheet;

	this->indices[letterIdx * 6 + 0] = letterIdx * 4 + 0;
	this->indices[letterIdx * 6 + 1] = letterIdx * 4 + 2;
	this->indices[letterIdx * 6 + 2] = letterIdx * 4 + 1;
	this->indices[letterIdx * 6 + 3] = letterIdx * 4 + 0;
	this->indices[letterIdx * 6 + 4] = letterIdx * 4 + 3;
	this->indices[letterIdx * 6 + 5] = letterIdx * 4 + 2;

	return actual_charwidth_on_fontsheet;
}

<commit_msg>ChangeText() to smaller text dont print old text anymore because that's why we changed text not to display old text but to display new text now ok<commit_after>#include "Vajra/Engine/AssetLibrary/AssetLibrary.h"
#include "Vajra/Engine/AssetLibrary/Assets/UiFontType/UiFontType.h"
#include "Vajra/Engine/Components/DerivedComponents/Armature/Armature.h"
#include "Vajra/Engine/Components/DerivedComponents/Renderer/UiFontRenderer.h"
#include "Vajra/Engine/Components/DerivedComponents/Transform/Transform.h"
#include "Vajra/Engine/Core/Engine.h"
#include "Vajra/Engine/GameObject/GameObject.h"
#include "Vajra/Engine/SceneGraph/SceneGraph3D.h"
#include "Vajra/Framework/OpenGL/OpenGLWrapper/OpenGLWrapper.h"
#include "Vajra/Framework/OpenGL/ShaderSet/ShaderSet.h"
#include "Vajra/Utilities/MathUtilities.h"

#ifdef __APPLE__
#include "TargetConditionals.h"
#endif

UiFontRenderer::UiFontRenderer() : Renderer() {
	this->init();
}

UiFontRenderer::UiFontRenderer(Object* object_) : Renderer(object_) {
	this->init();
}

UiFontRenderer::~UiFontRenderer() {
	this->destroy();
}

void UiFontRenderer::initTextToDisplay(std::string text, unsigned int width, unsigned int height, std::string pathToFontSpecificationFile, float fontSize_, UiFontAlignment_type fontAlignment_ /* = UI_FONT_ALIGNMENT_left */) {
	this->fontType = ENGINE->GetAssetLibrary()->GetAsset<UiFontType>(pathToFontSpecificationFile);
	this->SetShaderName(this->fontType->GetShaderName());

	this->fontAlignment = fontAlignment_;
	//
	this->fontSize = fontSize_;
	GameObject* gameObjectRef = (GameObject*)this->GetObject();
	ASSERT(gameObjectRef->GetClassType() & CLASS_TYPE_GAMEOBJECT, "Object is a game object");
	gameObjectRef->GetTransform()->SetScale(this->fontSize, this->fontSize, this->fontSize);

	this->required_width  = width;
	this->required_height = height;

	this->textToDisplay = text;
	this->makeText();
	this->initVbos();

	Renderer::addToRenderList();
}

void UiFontRenderer::changeText(std::string text) {
	this->textToDisplay = text;
	this->makeText();
	this->initVbos();
}

void UiFontRenderer::initVbos() {
    if (this->vboPositions != 0 && this->vboTextureCoords != 0 && this->vboIndices != 0) {
		GLCALL(glDeleteBuffers, 1, &this->vboPositions);
		GLCALL(glDeleteBuffers, 1, &this->vboTextureCoords);
		GLCALL(glDeleteBuffers, 1, &this->vboIndices);
    }

    if (this->vertices != nullptr) {
		GLCALL(glGenBuffers, 1, &this->vboPositions);
		GLCALL(glBindBuffer, GL_ARRAY_BUFFER, this->vboPositions);
		GLCALL(glBufferData, GL_ARRAY_BUFFER, sizeof(glm::vec3) * this->numVertices, this->vertices, GL_STATIC_DRAW);
    } else {
        FRAMEWORK->GetLogger()->errlog("ERROR: Uninited vertices");
        return;
    }

    if (this->textureCoords != nullptr) {
		GLCALL(glGenBuffers, 1, &this->vboTextureCoords);
		GLCALL(glBindBuffer, GL_ARRAY_BUFFER, this->vboTextureCoords);
		GLCALL(glBufferData, GL_ARRAY_BUFFER, sizeof(glm::vec2) * this->numVertices, this->textureCoords, GL_STATIC_DRAW);
    }

    if (this->indices.size() != 0) {
		GLCALL(glGenBuffers, 1, &this->vboIndices);
		GLCALL(glBindBuffer, GL_ELEMENT_ARRAY_BUFFER, this->vboIndices);
		GLCALL(glBufferData, GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * this->indices.size(), &this->indices[0], GL_STATIC_DRAW);
    } else {
        FRAMEWORK->GetLogger()->errlog("ERROR: Uninited indices");
        return;
    }

    FRAMEWORK->GetLogger()->errlog("\nVBOs made successfully");
}

void UiFontRenderer::HandleMessage(MessageChunk /* messageChunk */) {
}

void UiFontRenderer::Draw() {
	// Write Material properties to shader
	if (FRAMEWORK->GetOpenGLWrapper()->GetCurrentShaderSet()->HasHandle(SHADER_VARIABLE_VARIABLENAME_MaterialDiffuseColor)) {
		GLCALL(glUniform4f, FRAMEWORK->GetOpenGLWrapper()->GetCurrentShaderSet()->GetHandle(SHADER_VARIABLE_VARIABLENAME_MaterialDiffuseColor), this->diffuseColor.r, this->diffuseColor.g, this->diffuseColor.b, this->diffuseColor.a);
	}

	if (this->vboPositions == 0 || this->vboIndices == 0) {
        FRAMEWORK->GetLogger()->errlog("ERROR: VBOs not made");
        return;
    }

	ShaderSet* currentShaderSet = FRAMEWORK->GetOpenGLWrapper()->GetCurrentShaderSet();

    GLint positionHandle = currentShaderSet->GetHandle(SHADER_VARIABLE_VARIABLENAME_vPosition);
    GLCALL(glEnableVertexAttribArray, positionHandle);
    GLCALL(glBindBuffer, GL_ARRAY_BUFFER, this->vboPositions);
    GLCALL(glVertexAttribPointer, positionHandle, 3, GL_FLOAT, GL_FALSE, 0, 0);

    ASSERT(this->fontType->GetTextureAsset() != nullptr, "FontType has texture initialized");
	this->fontType->GetTextureAsset()->Draw(0);

	GLint textureCoordsHandle = currentShaderSet->GetHandle(SHADER_VARIABLE_VARIABLENAME_uvCoords_in);
	GLCALL(glEnableVertexAttribArray, textureCoordsHandle);
	GLCALL(glBindBuffer, GL_ARRAY_BUFFER, this->vboTextureCoords);
	GLCALL(glVertexAttribPointer, textureCoordsHandle, 2, GL_FLOAT, GL_FALSE, 0, 0);

    GLCALL(glBindBuffer, GL_ELEMENT_ARRAY_BUFFER, this->vboIndices);
    GLCALL(glDrawElements, GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, (void*)0);
}

void UiFontRenderer::init() {
	GameObject* gameObject = dynamic_cast<GameObject*>(this->GetObject());
	if (gameObject != nullptr) {
		ASSERT(typeid(gameObject) == typeid(GameObject*), "Type of Object* (%s) of id %d was %s", typeid(gameObject).name(), gameObject->GetId(), typeid(GameObject*).name());
	}

	this->numVertices = 0;
	this->vertices = nullptr;
	this->textureCoords = nullptr;
	this->diffuseColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);

	this->vboPositions = 0;
	this->vboTextureCoords = 0;
	this->vboIndices = 0;

	this->actual_width  = 0.0f;
	this->actual_height = 0.0f;
	//
	this->required_width  = 0.0f;
	this->required_height = 0.0f;
	//
	this->unscaled_width  = 0.0f;
	this->unscaled_height = 0.0f;

	this->fontSize = 1.0f;
	this->fontAlignment = UI_FONT_ALIGNMENT_left;
}

void UiFontRenderer::destroy() {
	// TODO [Hack] For some reason the simulator crashes in glDrawElements() when we do this
#ifdef __APPLE__
#if TARGET_IPHONE_SIMULATOR
	return;
#endif
#endif
	if (this->vertices != nullptr) {
		delete this->vertices;
	}
	if (this->textureCoords != nullptr) {
		delete this->textureCoords;
	}

    if (this->vboPositions != 0) {
    	FRAMEWORK->GetOpenGLWrapper()->FreeGLBuffer(&this->vboPositions);
    }
    if (this->vboTextureCoords != 0) {
    	FRAMEWORK->GetOpenGLWrapper()->FreeGLBuffer(&this->vboTextureCoords);
    }
    if (this->vboIndices != 0) {
    	FRAMEWORK->GetOpenGLWrapper()->FreeGLBuffer(&this->vboIndices);
    }
}

////////////////////////////////////////////////////////////////////////////////

void UiFontRenderer::makeText() {

	this->unscaled_width = 0.0f;

	unsigned int numVerticesNeeded = this->textToDisplay.size() * 4;
	if (numVerticesNeeded > this->numVertices) {
		if (this->vertices != nullptr) {
			delete this->vertices;
		}
		if (this->textureCoords != nullptr) {
			delete this->textureCoords;
		}
		this->vertices      = new glm::vec3[numVerticesNeeded];
		this->textureCoords = new glm::vec2[numVerticesNeeded];
	}
	this->numVertices = numVerticesNeeded;
	this->indices.resize(this->textToDisplay.size() * 6);

	float woffset = 0.0f;

	for (unsigned int i = 0; i < this->textToDisplay.size (); ++i) {
		char c = this->textToDisplay[i];
		int charIdx = (int) c;
		this->unscaled_width += this->makeACharacter(charIdx, i, woffset);
		woffset += this->fontType->GetCharacterWidth(charIdx);
	}

	float justText_width  = this->unscaled_width  * this->fontSize;

	this->actual_width  = std::max(this->required_width , this->unscaled_width  * this->fontSize);

	// Adjust for font alignment:
	if (this->required_width < justText_width) {
		// Nothing can be done.
	} else {
		switch (this->fontAlignment) {

		case UI_FONT_ALIGNMENT_left: {
			// Nothing to be done
		} break;

		case UI_FONT_ALIGNMENT_center: {
			float offset = (float)(this->required_width - justText_width) / this->fontSize / 2.0f;
			for (unsigned int i = 0; i < this->numVertices; ++i) {
				this->vertices[i].x += offset;
			}
		} break;

		case UI_FONT_ALIGNMENT_right: {
			float offset = (this->required_width - justText_width) / this->fontSize;
			for (unsigned int i = 0; i < this->numVertices; ++i) {
				this->vertices[i].x += offset;
			}
		} break;

		default: {
		} break;

		}
	}
}

float UiFontRenderer::makeACharacter(int charIdxInAscii, int letterIdx, float woffset) {

	float actual_charwidth_on_fontsheet = this->fontType->GetCharacterWidth(charIdxInAscii);

	this->vertices[letterIdx * 4 + 0].x = woffset;
	this->vertices[letterIdx * 4 + 1].x = woffset + actual_charwidth_on_fontsheet;
	this->vertices[letterIdx * 4 + 2].x = woffset + actual_charwidth_on_fontsheet;
	this->vertices[letterIdx * 4 + 3].x = woffset;

	this->vertices[letterIdx * 4 + 0].y = 0.0f;
	this->vertices[letterIdx * 4 + 1].y = 0.0f;
	this->vertices[letterIdx * 4 + 2].y = 0.0f - 1.0f;
	this->vertices[letterIdx * 4 + 3].y = 0.0f - 1.0f;

	// TODO [Hack] Use zorder instead of 0.1f
	this->vertices[letterIdx * 4 + 0].z = 0.1f;
	this->vertices[letterIdx * 4 + 1].z = 0.1f;
	this->vertices[letterIdx * 4 + 2].z = 0.1f;
	this->vertices[letterIdx * 4 + 3].z = 0.1f;

	float each_charwidth_on_fontsheet = 1.0f / NUM_FONT_CHARACTER_COLUMNS;
	float each_charheight_on_fontsheet = 1.0f / NUM_FONT_CHARACTER_ROWS;
	float woffest_into_fontsheet = ((charIdxInAscii - this->fontType->GetStartingCharacter()) % NUM_FONT_CHARACTER_COLUMNS) * each_charwidth_on_fontsheet;
	float hoffest_into_fontsheet = ((charIdxInAscii - this->fontType->GetStartingCharacter()) / NUM_FONT_CHARACTER_ROWS) * each_charheight_on_fontsheet;

	this->textureCoords[letterIdx * 4 + 0].x = woffest_into_fontsheet;
	this->textureCoords[letterIdx * 4 + 1].x = woffest_into_fontsheet + each_charwidth_on_fontsheet * actual_charwidth_on_fontsheet;
	this->textureCoords[letterIdx * 4 + 2].x = woffest_into_fontsheet + each_charwidth_on_fontsheet * actual_charwidth_on_fontsheet;
	this->textureCoords[letterIdx * 4 + 3].x = woffest_into_fontsheet;

	this->textureCoords[letterIdx * 4 + 0].y = hoffest_into_fontsheet;
	this->textureCoords[letterIdx * 4 + 1].y = hoffest_into_fontsheet;
	this->textureCoords[letterIdx * 4 + 2].y = hoffest_into_fontsheet + each_charheight_on_fontsheet;
	this->textureCoords[letterIdx * 4 + 3].y = hoffest_into_fontsheet + each_charheight_on_fontsheet;

	this->indices[letterIdx * 6 + 0] = letterIdx * 4 + 0;
	this->indices[letterIdx * 6 + 1] = letterIdx * 4 + 2;
	this->indices[letterIdx * 6 + 2] = letterIdx * 4 + 1;
	this->indices[letterIdx * 6 + 3] = letterIdx * 4 + 0;
	this->indices[letterIdx * 6 + 4] = letterIdx * 4 + 3;
	this->indices[letterIdx * 6 + 5] = letterIdx * 4 + 2;

	return actual_charwidth_on_fontsheet;
}

<|endoftext|>


<commit_before>#define CURL_STATICLIB 1
#include <URL.h>
#include <curl/curl.h>
#include <Utils.h>
#include <map>
#include <string>

#ifdef HX_WINDOWS
#include <stdio.h>
#define snprintf _snprintf
#endif

/**
 * TODO:
 * HTTP redirects
 */

namespace nme
{

static std::string sCACertFile("");
static CURLM *sCurlM = 0;
static int sRunning = 0;
static int sLoaders = 0;

typedef std::map<CURL *,class CURLLoader *> CurlMap;
typedef std::vector<class CURLLoader *> CurlList;
void processMultiMessages();

CurlMap *sCurlMap = 0;
CurlList *sCurlList = 0;

enum { MAX_ACTIVE = 64 };

class CURLLoader : public URLLoader
{
public:
	CURL *mHandle;
	int mBytesLoaded;
	int mBytesTotal;
	URLState mState;
	int mHttpCode;
	char mErrorBuf[CURL_ERROR_SIZE];
	QuickVec<unsigned char> mBytes;

  size_t         mBufferRemaining;
  unsigned char *mBufferPos;
  unsigned char *mPutBuffer;

  struct curl_slist *headerlist;

	CURLLoader(URLRequest &r)
	{
		mState = urlInit;
		if (!sCurlM)
			sCurlM = curl_multi_init();
		mBytesTotal = -1;
		mBytesLoaded = 0;
		mHttpCode = 0;
		sLoaders++;
		mHandle = curl_easy_init();
		if (!sCurlMap)
			sCurlMap = new CurlMap;

    mBufferRemaining = 0;
    mPutBuffer = 0;
    mBufferPos = 0;
    headerlist = NULL;

		curl_easy_setopt(mHandle, CURLOPT_URL, r.url);

    /* send all data to this function  */
    curl_easy_setopt(mHandle, CURLOPT_WRITEFUNCTION, staticOnData);
    curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, (void *)this);
		curl_easy_setopt(mHandle, CURLOPT_NOPROGRESS, 0);
		curl_easy_setopt(mHandle, CURLOPT_FOLLOWLOCATION, 1);
    if (r.authType!=0)
    {
      curl_easy_setopt(mHandle, CURLOPT_HTTPAUTH, r.authType);
      if (r.credentials && r.credentials[0])
        curl_easy_setopt(mHandle, CURLOPT_USERPWD, r.credentials);
    }
    curl_easy_setopt(mHandle, CURLOPT_PROGRESSFUNCTION, staticOnProgress);
    curl_easy_setopt(mHandle, CURLOPT_PROGRESSDATA, (void *)this);
    curl_easy_setopt(mHandle, CURLOPT_ERRORBUFFER, mErrorBuf );
    if (r.debug)
      curl_easy_setopt(mHandle, CURLOPT_VERBOSE, 1);
    curl_easy_setopt( mHandle, CURLOPT_COOKIEFILE, "" );
    if (r.cookies && r.cookies[0])
      curl_easy_setopt( mHandle, CURLOPT_COOKIE, r.cookies );
    if (sCACertFile.empty())
      curl_easy_setopt(mHandle, CURLOPT_SSL_VERIFYPEER, false);
    else
      curl_easy_setopt(mHandle, CURLOPT_CAINFO, sCACertFile.c_str());

    if (r.method)
    {
      if (!strcmp(r.method,"POST"))
      {
        curl_easy_setopt(mHandle, CURLOPT_POST, true);

        if (r.postData.Ok())
        {
          curl_easy_setopt(mHandle, CURLOPT_POSTFIELDSIZE, r.postData.Size());
          curl_easy_setopt(mHandle, CURLOPT_COPYPOSTFIELDS, r.postData.Bytes());
        }
      }
      else if (!strcmp(r.method,"PUT"))
      {
        // The file to PUT must be set with CURLOPT_INFILE and CURLOPT_INFILESIZE.
        curl_easy_setopt(mHandle, CURLOPT_PUT, true);

        curl_easy_setopt(mHandle, CURLOPT_UPLOAD, 1);
        if (r.postData.Ok())
          SetPutBuffer(r.postData.Bytes(),r.postData.Size());
      }
      else if (!strcmp(r.method,"GET"))
      {
        // GET is the default, so this is not necessary but here for completeness.
        curl_easy_setopt(mHandle, CURLOPT_HTTPGET, true);
      }
      else if (!strcmp(r.method,"DELETE"))
      {
        curl_easy_setopt(mHandle, CURLOPT_CUSTOMREQUEST, r.method);
      }
      else
      {
        // unsupported method !!
      }
    }

    if (r.contentType)
    {
      std::vector<char> buffer;
      buffer.resize(512);
      snprintf(&buffer[0], buffer.size(), "Content-Type: %s", r.contentType);
      headerlist = curl_slist_append(headerlist, &buffer[0]);
    }
    headerlist = curl_slist_append(headerlist, "Expect:");

    int n = r.headers.size();
    if (n >= 0) {
      for(int i = 0; i < n; i++)
      {
        URLRequestHeader h = r.headers[i];
        std::vector<char> buffer;
        buffer.resize(512);
        snprintf(&buffer[0], buffer.size(), "%s: %s", h.name, h.value);
        headerlist = curl_slist_append(headerlist, &buffer[0]);
      }
    }

    curl_easy_setopt(mHandle, CURLOPT_HTTPHEADER, headerlist);

    mErrorBuf[0] = '\0';

    /* some servers don't like requests that are made without a user-agent
      field, so we provide one */
    curl_easy_setopt(mHandle, CURLOPT_USERAGENT, "libcurl-agent/1.0");

		mState = urlLoading;

    if (sCurlMap->size()<MAX_ACTIVE)
    {
      StartProcessing();
    }
    else
    {
      if (sCurlList==0)
        sCurlList = new CurlList;
      sCurlList->push_back(this);
    }
  }

  size_t ReadFunc( void *ptr, size_t size, size_t nmemb)
  {
    size_t bytes = size * nmemb;
    if (mBufferRemaining<=bytes)
      bytes = mBufferRemaining;

    memcpy(ptr,mBufferPos,bytes);
    mBufferPos += bytes;
    mBufferRemaining -= bytes;

    return bytes;
  }

  static size_t SReadFunc( void *ptr, size_t size, size_t nmemb, void *userdata)
  {
    return ((CURLLoader *)userdata)->ReadFunc(ptr,size,nmemb);
  }

  void SetPutBuffer(const unsigned char *inBuffer, size_t inLen)
  {
    mPutBuffer = new unsigned char[inLen];
    mBufferRemaining = 0;
    mBufferPos = mPutBuffer;
    memcpy(mPutBuffer,inBuffer,inLen);
    curl_easy_setopt(mHandle, CURLOPT_READFUNCTION, SReadFunc);
    curl_easy_setopt(mHandle, CURLOPT_INFILESIZE, inLen);
  }

  void StartProcessing()
  {
		(*sCurlMap)[mHandle] = this;
    //int c1 = curl_multi_add_handle(sCurlM,mHandle);
    curl_multi_add_handle(sCurlM,mHandle);
    //int result = curl_multi_perform(sCurlM, &sRunning);
    curl_multi_perform(sCurlM, &sRunning);
    processMultiMessages();
	}

	~CURLLoader()
	{
    delete [] mPutBuffer;
		curl_easy_cleanup(mHandle);
		sLoaders--;
		if (sLoaders==0)
		{
			curl_multi_cleanup(sCurlM);
			sCurlM = 0;
		}
	}

	size_t onData( void *inBuffer, size_t inItemSize, size_t inItems)
	{
		size_t size = inItemSize*inItems;
		if (size>0)
		{
			int s = mBytes.size();
			mBytes.resize(s+size);
			memcpy(&mBytes[s],inBuffer,size);
		}
		return inItems;
	}

	int onProgress( double inBytesTotal, double inBytesDownloaded,
                    double inUploadTotal, double inBytesUploaded )
	{
		mBytesTotal = inBytesTotal;
		mBytesLoaded = inBytesDownloaded;
		return 0;
	}

	void setResult(CURLcode inResult)
	{
		sCurlMap->erase(mHandle);
		curl_multi_remove_handle(sCurlM,mHandle);
		mState = inResult==0 ? urlComplete : urlError;
	}

	static size_t staticOnData( void *inBuffer, size_t size, size_t inItems, void *userdata)
	{
		return ((CURLLoader *)userdata)->onData(inBuffer,size,inItems);
	}

	static size_t staticOnProgress(void* inCookie, double inBytesTotal, double inBytesDownloaded,
                    double inUploadTotal, double inBytesUploaded)

	{
		return ((CURLLoader *)inCookie)->onProgress(
			inBytesTotal,inBytesDownloaded,inUploadTotal,inBytesUploaded);
	}

   void getCookies( std::vector<std::string> &outCookies )
   {
      curl_slist *list = 0;
		if (CURLE_OK == curl_easy_getinfo(mHandle,CURLINFO_COOKIELIST,&list) && list)
      {
         curl_slist *item = list;
         while(item)
         {
            outCookies.push_back(item->data);
            item = item->next;
         }
         curl_slist_free_all(list);
      }
	}

	URLState getState()
	{
		long http_code = 0;
		int curl_code = curl_easy_getinfo(mHandle,CURLINFO_RESPONSE_CODE,&http_code);
		if (curl_code != CURLE_OK)
			mState = urlError;
		else if (http_code>0)
		{
			// XXX : A HTTP code >= 400 should be an error. Handle this in URLLoader.hx for now.
			mHttpCode = http_code;
		}
		return mState;
	}

	int bytesLoaded() { return mBytesLoaded; }
	int bytesTotal() { return mBytesTotal; }

	virtual int getHttpCode() { return mHttpCode; }

	virtual const char *getErrorMessage() { return mErrorBuf; }
	virtual ByteArray releaseData()
	{
		if (mBytes.size())
		{
         return ByteArray(mBytes);
		}
		return ByteArray();
	}

};

void processMultiMessages()
{
		int remaining;
		CURLMsg *msg;
		while( (msg=curl_multi_info_read(sCurlM,&remaining) ) )
		{
			if (msg->msg==CURLMSG_DONE)
			{
				CurlMap::iterator i = sCurlMap->find(msg->easy_handle);
				if (i!=sCurlMap->end())
					i->second->setResult( msg->data.result );
			}
		}
}

bool URLLoader::processAll()
{
   bool added = false;
   do {
      added = false;
	   bool check = sRunning;
	   for(int go=0; go<10 && sRunning; go++)
	   {
		   int code = curl_multi_perform(sCurlM,&sRunning);
		   if (code!= CURLM_CALL_MULTI_PERFORM)
			   break;
	   }
	   if (check) {
         processMultiMessages();
      }
      while(sCurlMap && sCurlList && !sCurlList->empty() && sCurlMap->size()<MAX_ACTIVE )
      {
         CURLLoader *curl = (*sCurlList)[0];
         sCurlList->erase(sCurlList->begin());
         added = true;
         curl->StartProcessing();
      }

   } while(added);

   return sRunning || (sCurlList && sCurlList->size());
}

URLLoader *URLLoader::create(URLRequest &r)
{
	return new CURLLoader(r);
}

typedef int (*get_file_callback_func)(const char *filename, unsigned char **buf);
extern "C"
{
extern get_file_callback_func get_file_callback;
}

#if (defined(HX_MACOS) || defined(ANDROID) ) && defined(NME_CURL_SSL)
#define TRY_GET_FILE
#endif

#ifdef TRY_GET_FILE
static int sGetFile(const char *inFilename, unsigned char **outBuf)
{
   #ifdef ANDROID
   ByteArray bytes = AndroidGetAssetBytes(inFilename);
   #else
   ByteArray bytes = ByteArray::FromFile(inFilename);
   #endif

   ELOG("Loaded cert %s %d bytes.", inFilename, bytes.Size());
   if (bytes.Size()>0)
   {
      *outBuf = (unsigned char *)malloc(bytes.Size());
      memcpy(*outBuf,bytes.Bytes(),bytes.Size());
      return bytes.Size();
   }

   return -1;
}
#endif

void URLLoader::initialize(const char *inCACertFilePath)
{
  #ifdef HX_WINDOWS
  unsigned int flags = CURL_GLOBAL_WIN32;
  #else
  unsigned int flags = 0;
  #endif
  curl_global_init(flags | CURL_GLOBAL_SSL);
  sCACertFile = std::string(inCACertFilePath);

  #ifdef TRY_GET_FILE
  get_file_callback = sGetFile;
  #endif

  /* Set to 1 to print version information for libcurl. */
  if (!sCACertFile.empty())
  {
     FILE *f = fopen(sCACertFile.c_str(),"rb");
     bool loaded = f;
     if (f)
        fclose(f);
     ELOG("Open cert file: %s %s\n", sCACertFile.c_str(), loaded ? "Yes" : "NO!!" );
     #ifdef IPHONE
     if (!loaded)
     {
        sCACertFile = GetResourcePath() + gAssetBase + inCACertFilePath;
        FILE *f = fopen(sCACertFile.c_str(),"rb");
        loaded = f;
        if (f)
          fclose(f);
        ELOG("Open cert file: %s %s\n", sCACertFile.c_str(), loaded ? "Yes" : "NO!!" );
     }
     #endif
  }

  #if 0
  curl_version_info_data * info = curl_version_info(CURLVERSION_NOW);
  ELOG("SSL cert: %s\n", inCACertFilePath);
  ELOG("libcurl version: %s\n", info->version);
  ELOG("Support for SSL in libcurl: %d\n", (info->features) & CURL_VERSION_SSL);
  ELOG("Supported libcurl protocols: ");
  for (int i=0; info->protocols[i] != 0; i++) {
    ELOG(" protocol %d: %s",i,  info->protocols[i]);
  }
  #endif
}

}
<commit_msg>Allow cURL headers to be larger than 512<commit_after>#define CURL_STATICLIB 1
#include <URL.h>
#include <curl/curl.h>
#include <Utils.h>
#include <map>
#include <string>

#ifdef HX_WINDOWS
#include <stdio.h>
#define snprintf _snprintf
#endif

/**
 * TODO:
 * HTTP redirects
 */

namespace lime
{

static std::string sCACertFile("");
static CURLM *sCurlM = 0;
static int sRunning = 0;
static int sLoaders = 0;

typedef std::map<CURL *,class CURLLoader *> CurlMap;
typedef std::vector<class CURLLoader *> CurlList;
void processMultiMessages();

CurlMap *sCurlMap = 0;
CurlList *sCurlList = 0;

enum { MAX_ACTIVE = 64 };

class CURLLoader : public URLLoader
{
public:
	CURL *mHandle;
	int mBytesLoaded;
	int mBytesTotal;
	URLState mState;
	int mHttpCode;
	char mErrorBuf[CURL_ERROR_SIZE];
	QuickVec<unsigned char> mBytes;

  size_t         mBufferRemaining;
  unsigned char *mBufferPos;
  unsigned char *mPutBuffer;

  struct curl_slist *headerlist;

	CURLLoader(URLRequest &r)
	{
		mState = urlInit;
		if (!sCurlM)
			sCurlM = curl_multi_init();
		mBytesTotal = -1;
		mBytesLoaded = 0;
		mHttpCode = 0;
		sLoaders++;
		mHandle = curl_easy_init();
		if (!sCurlMap)
			sCurlMap = new CurlMap;

    mBufferRemaining = 0;
    mPutBuffer = 0;
    mBufferPos = 0;
    headerlist = NULL;

		curl_easy_setopt(mHandle, CURLOPT_URL, r.url);

    /* send all data to this function  */
    curl_easy_setopt(mHandle, CURLOPT_WRITEFUNCTION, staticOnData);
    curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, (void *)this);
		curl_easy_setopt(mHandle, CURLOPT_NOPROGRESS, 0);
		curl_easy_setopt(mHandle, CURLOPT_FOLLOWLOCATION, 1);
    if (r.authType!=0)
    {
      curl_easy_setopt(mHandle, CURLOPT_HTTPAUTH, r.authType);
      if (r.credentials && r.credentials[0])
        curl_easy_setopt(mHandle, CURLOPT_USERPWD, r.credentials);
    }
    curl_easy_setopt(mHandle, CURLOPT_PROGRESSFUNCTION, staticOnProgress);
    curl_easy_setopt(mHandle, CURLOPT_PROGRESSDATA, (void *)this);
    curl_easy_setopt(mHandle, CURLOPT_ERRORBUFFER, mErrorBuf );
    if (r.debug)
      curl_easy_setopt(mHandle, CURLOPT_VERBOSE, 1);
    curl_easy_setopt( mHandle, CURLOPT_COOKIEFILE, "" );
    if (r.cookies && r.cookies[0])
      curl_easy_setopt( mHandle, CURLOPT_COOKIE, r.cookies );
    if (sCACertFile.empty())
      curl_easy_setopt(mHandle, CURLOPT_SSL_VERIFYPEER, false);
    else
      curl_easy_setopt(mHandle, CURLOPT_CAINFO, sCACertFile.c_str());

    if (r.method)
    {
      if (!strcmp(r.method,"POST"))
      {
        curl_easy_setopt(mHandle, CURLOPT_POST, true);

        if (r.postData.Ok())
        {
          curl_easy_setopt(mHandle, CURLOPT_POSTFIELDSIZE, r.postData.Size());
          curl_easy_setopt(mHandle, CURLOPT_COPYPOSTFIELDS, r.postData.Bytes());
        }
      }
      else if (!strcmp(r.method,"PUT"))
      {
        // The file to PUT must be set with CURLOPT_INFILE and CURLOPT_INFILESIZE.
        curl_easy_setopt(mHandle, CURLOPT_PUT, true);

        curl_easy_setopt(mHandle, CURLOPT_UPLOAD, 1);
        if (r.postData.Ok())
          SetPutBuffer(r.postData.Bytes(),r.postData.Size());
      }
      else if (!strcmp(r.method,"GET"))
      {
        // GET is the default, so this is not necessary but here for completeness.
        curl_easy_setopt(mHandle, CURLOPT_HTTPGET, true);
      }
      else if (!strcmp(r.method,"DELETE"))
      {
        curl_easy_setopt(mHandle, CURLOPT_CUSTOMREQUEST, r.method);
      }
      else
      {
        // unsupported method !!
      }
    }

    if (r.contentType)
    {
      std::vector<char> buffer;
      buffer.resize(512);
      snprintf(&buffer[0], buffer.size(), "Content-Type: %s", r.contentType);
      headerlist = curl_slist_append(headerlist, &buffer[0]);
    }
    headerlist = curl_slist_append(headerlist, "Expect:");

    int n = r.headers.size();
    if (n >= 0) {
      for(int i = 0; i < n; i++)
      {
        URLRequestHeader h = r.headers[i];
        std::vector<char> buffer;
        buffer.resize(strlen(h.name) + strlen(h.value) + 3); // 1 for terminating char, 2 for ': '
        snprintf(&buffer[0], buffer.size(), "%s: %s", h.name, h.value);
        headerlist = curl_slist_append(headerlist, &buffer[0]);
      }
    }

    curl_easy_setopt(mHandle, CURLOPT_HTTPHEADER, headerlist);

    mErrorBuf[0] = '\0';

    /* some servers don't like requests that are made without a user-agent
      field, so we provide one */
    curl_easy_setopt(mHandle, CURLOPT_USERAGENT, "libcurl-agent/1.0");

		mState = urlLoading;

    if (sCurlMap->size()<MAX_ACTIVE)
    {
      StartProcessing();
    }
    else
    {
      if (sCurlList==0)
        sCurlList = new CurlList;
      sCurlList->push_back(this);
    }
  }

  size_t ReadFunc( void *ptr, size_t size, size_t nmemb)
  {
    size_t bytes = size * nmemb;
    if (mBufferRemaining<=bytes)
      bytes = mBufferRemaining;

    memcpy(ptr,mBufferPos,bytes);
    mBufferPos += bytes;
    mBufferRemaining -= bytes;

    return bytes;
  }

  static size_t SReadFunc( void *ptr, size_t size, size_t nmemb, void *userdata)
  {
    return ((CURLLoader *)userdata)->ReadFunc(ptr,size,nmemb);
  }

  void SetPutBuffer(const unsigned char *inBuffer, size_t inLen)
  {
    mPutBuffer = new unsigned char[inLen];
    mBufferRemaining = 0;
    mBufferPos = mPutBuffer;
    memcpy(mPutBuffer,inBuffer,inLen);
    curl_easy_setopt(mHandle, CURLOPT_READFUNCTION, SReadFunc);
    curl_easy_setopt(mHandle, CURLOPT_INFILESIZE, inLen);
  }

  void StartProcessing()
  {
		(*sCurlMap)[mHandle] = this;
    //int c1 = curl_multi_add_handle(sCurlM,mHandle);
    curl_multi_add_handle(sCurlM,mHandle);
    //int result = curl_multi_perform(sCurlM, &sRunning);
    curl_multi_perform(sCurlM, &sRunning);
    processMultiMessages();
	}

	~CURLLoader()
	{
    delete [] mPutBuffer;
		curl_easy_cleanup(mHandle);
		sLoaders--;
		if (sLoaders==0)
		{
			curl_multi_cleanup(sCurlM);
			sCurlM = 0;
		}
	}

	size_t onData( void *inBuffer, size_t inItemSize, size_t inItems)
	{
		size_t size = inItemSize*inItems;
		if (size>0)
		{
			int s = mBytes.size();
			mBytes.resize(s+size);
			memcpy(&mBytes[s],inBuffer,size);
		}
		return inItems;
	}

	int onProgress( double inBytesTotal, double inBytesDownloaded,
                    double inUploadTotal, double inBytesUploaded )
	{
		mBytesTotal = inBytesTotal;
		mBytesLoaded = inBytesDownloaded;
		return 0;
	}

	void setResult(CURLcode inResult)
	{
		sCurlMap->erase(mHandle);
		curl_multi_remove_handle(sCurlM,mHandle);
		mState = inResult==0 ? urlComplete : urlError;
	}

	static size_t staticOnData( void *inBuffer, size_t size, size_t inItems, void *userdata)
	{
		return ((CURLLoader *)userdata)->onData(inBuffer,size,inItems);
	}

	static size_t staticOnProgress(void* inCookie, double inBytesTotal, double inBytesDownloaded,
                    double inUploadTotal, double inBytesUploaded)

	{
		return ((CURLLoader *)inCookie)->onProgress(
			inBytesTotal,inBytesDownloaded,inUploadTotal,inBytesUploaded);
	}

   void getCookies( std::vector<std::string> &outCookies )
   {
      curl_slist *list = 0;
		if (CURLE_OK == curl_easy_getinfo(mHandle,CURLINFO_COOKIELIST,&list) && list)
      {
         curl_slist *item = list;
         while(item)
         {
            outCookies.push_back(item->data);
            item = item->next;
         }
         curl_slist_free_all(list);
      }
	}

	URLState getState()
	{
		long http_code = 0;
		int curl_code = curl_easy_getinfo(mHandle,CURLINFO_RESPONSE_CODE,&http_code);
		if (curl_code != CURLE_OK)
			mState = urlError;
		else if (http_code>0)
		{
			// XXX : A HTTP code >= 400 should be an error. Handle this in URLLoader.hx for now.
			mHttpCode = http_code;
		}
		return mState;
	}

	int bytesLoaded() { return mBytesLoaded; }
	int bytesTotal() { return mBytesTotal; }

	virtual int getHttpCode() { return mHttpCode; }

	virtual const char *getErrorMessage() { return mErrorBuf; }
	virtual ByteArray releaseData()
	{
		if (mBytes.size())
		{
         return ByteArray(mBytes);
		}
		return ByteArray();
	}

};

void processMultiMessages()
{
		int remaining;
		CURLMsg *msg;
		while( (msg=curl_multi_info_read(sCurlM,&remaining) ) )
		{
			if (msg->msg==CURLMSG_DONE)
			{
				CurlMap::iterator i = sCurlMap->find(msg->easy_handle);
				if (i!=sCurlMap->end())
					i->second->setResult( msg->data.result );
			}
		}
}

bool URLLoader::processAll()
{
   bool added = false;
   do {
      added = false;
	   bool check = sRunning;
	   for(int go=0; go<10 && sRunning; go++)
	   {
		   int code = curl_multi_perform(sCurlM,&sRunning);
		   if (code!= CURLM_CALL_MULTI_PERFORM)
			   break;
	   }
	   if (check) {
         processMultiMessages();
      }
      while(sCurlMap && sCurlList && !sCurlList->empty() && sCurlMap->size()<MAX_ACTIVE )
      {
         CURLLoader *curl = (*sCurlList)[0];
         sCurlList->erase(sCurlList->begin());
         added = true;
         curl->StartProcessing();
      }

   } while(added);

   return sRunning || (sCurlList && sCurlList->size());
}

URLLoader *URLLoader::create(URLRequest &r)
{
	return new CURLLoader(r);
}

typedef int (*get_file_callback_func)(const char *filename, unsigned char **buf);
extern "C"
{
extern get_file_callback_func get_file_callback;
}

#if (defined(HX_MACOS) || defined(ANDROID) ) && defined(NME_CURL_SSL)
#define TRY_GET_FILE
#endif

#ifdef TRY_GET_FILE
static int sGetFile(const char *inFilename, unsigned char **outBuf)
{
   #ifdef ANDROID
   ByteArray bytes = AndroidGetAssetBytes(inFilename);
   #else
   ByteArray bytes = ByteArray::FromFile(inFilename);
   #endif

   ELOG("Loaded cert %s %d bytes.", inFilename, bytes.Size());
   if (bytes.Size()>0)
   {
      *outBuf = (unsigned char *)malloc(bytes.Size());
      memcpy(*outBuf,bytes.Bytes(),bytes.Size());
      return bytes.Size();
   }

   return -1;
}
#endif

void URLLoader::initialize(const char *inCACertFilePath)
{
  #ifdef HX_WINDOWS
  unsigned int flags = CURL_GLOBAL_WIN32;
  #else
  unsigned int flags = 0;
  #endif
  curl_global_init(flags | CURL_GLOBAL_SSL);
  sCACertFile = std::string(inCACertFilePath);

  #ifdef TRY_GET_FILE
  get_file_callback = sGetFile;
  #endif

  /* Set to 1 to print version information for libcurl. */
  if (!sCACertFile.empty())
  {
     FILE *f = fopen(sCACertFile.c_str(),"rb");
     bool loaded = f;
     if (f)
        fclose(f);
     ELOG("Open cert file: %s %s\n", sCACertFile.c_str(), loaded ? "Yes" : "NO!!" );
     #ifdef IPHONE
     if (!loaded)
     {
        sCACertFile = GetResourcePath() + gAssetBase + inCACertFilePath;
        FILE *f = fopen(sCACertFile.c_str(),"rb");
        loaded = f;
        if (f)
          fclose(f);
        ELOG("Open cert file: %s %s\n", sCACertFile.c_str(), loaded ? "Yes" : "NO!!" );
     }
     #endif
  }

  #if 0
  curl_version_info_data * info = curl_version_info(CURLVERSION_NOW);
  ELOG("SSL cert: %s\n", inCACertFilePath);
  ELOG("libcurl version: %s\n", info->version);
  ELOG("Support for SSL in libcurl: %d\n", (info->features) & CURL_VERSION_SSL);
  ELOG("Supported libcurl protocols: ");
  for (int i=0; info->protocols[i] != 0; i++) {
    ELOG(" protocol %d: %s",i,  info->protocols[i]);
  }
  #endif
}

}
<|endoftext|>