<commit_before>
// Copyright (c) 2008-2009, Regents of the University of Colorado.
// This work was supported by NASA contracts NNJ05HE10G, NNC06CB40C, and
// NNC07CB47C.

#include "node.h"

Node::Node(QString name, QWidget* parent) :
    QWidget(parent)
{
    this->name = name;

    QRegExp streamRX(".*\\..*\\..*\\:.*"); // matches *.*.*:*
    QRegExp nodeRX(".*\\..*\\..*");     // matches *.*.*
    QRegExp habRX(".*\\..*");           // matches *.*

    if ( streamRX.exactMatch(name) ) {
        id = name.section('.', 2, 2).section(':', 1, 1);
        bionetType = STREAM;

        bionet_stream_t* stream = bionet_cache_lookup_stream(
            qPrintable(name.section('.', 0, 0)),
            qPrintable(name.section('.', 1, 1)),
            qPrintable(name.section('.', 2, 2).section(':', 0, 0)),
            qPrintable(id));

        if (bionet_stream_get_direction(stream) == BIONET_STREAM_DIRECTION_CONSUMER)
            endpointType = CONSUMER;
        else if (bionet_stream_get_direction(stream) == BIONET_STREAM_DIRECTION_PRODUCER)
            endpointType = PRODUCER;
        else
            endpointType = NEITHER;

        bionetPtr = (void*)stream;

    } else if ( nodeRX.exactMatch(name) ) {

        bionet_node_t* node = bionet_cache_lookup_node(
            qPrintable(name.section('.', 0, 0)),
            qPrintable(name.section('.', 1, 1)),
            qPrintable(name.section('.', 2, 2)));
        bionetPtr = (void*)node;

        id = name.section('.',2,2);
        bionetType = NODE;
        endpointType = NEITHER;

    } else if ( habRX.exactMatch(name) ) {

        bionet_hab_t* hab = bionet_cache_lookup_hab(
            qPrintable(name.section('.', 0, 0)),
            qPrintable(name.section('.', 1, 1)));
        bionetPtr = (void*)hab;

        id = name;
        bionetType = HAB;
        endpointType = NEITHER;

    } else if ( name.isEmpty() ) {
        id = QString();
        bionetType = ROOT;
        endpointType = NEITHER;
    }

    children = new QList<Node*>;

    /*
    path = new QPainterPath(QPoint(0,0));
    path->addEllipse(area);
    path->addText(area, Qt::AlignHCenter, Qt::AlignVCenter, id);
    */
}

Node::~Node() {
    while ( !children->isEmpty() ) {
        Node* child = children->takeFirst();
        delete child;
    }
    delete children;
}

void Node::addChild(Node* child) {
    children->append(child);
}

void Node::addChildWithoutArea(Node* child) {
    children->append(child);
}

bool Node::removeChild(QString id) {
    int size = children->size();

    for (int i = 0; i < size; i++ ) {
        Node* child = children->at(i);
        if (child->getId() == id) {
            Node* child = children->takeAt(i);
            delete child;
            return true;
        }
    }

    return false;
}

Node* Node::find(QString fullname) {
    if (this->name == fullname)
        return this;

    foreach (Node* node, *children) {
        Node* leaf = node->find(fullname);
        if (leaf != NULL)
            return leaf;
    }

    return NULL;
}

Node* Node::find(QPoint point) {

    if (area.contains(point))
        return this;

    foreach (Node* node, *children) {
        Node *leaf = node->find(point);
        if (leaf != NULL)
            return leaf;
    }

    return NULL;
}

Node* Node::find(QRect rect) {

    if (area.intersects(rect))
        return this;

    foreach (Node* node, *children) {
        Node *leaf = node->find(rect);
        if (leaf != NULL)
            return leaf;
    }

    return NULL;
}

void Node::setArea(QRect newArea) {
    this->area = newArea;
    //cout << "this->area = " << area.x() << ", " << area.y() << endl;
}
<commit_msg>fixing 3 possible uninitialized variables<commit_after>
// Copyright (c) 2008-2009, Regents of the University of Colorado.
// This work was supported by NASA contracts NNJ05HE10G, NNC06CB40C, and
// NNC07CB47C.

#include "node.h"

Node::Node(QString name, QWidget* parent) :
    QWidget(parent)
{
    this->name = name;

    QRegExp streamRX(".*\\..*\\..*\\:.*"); // matches *.*.*:*
    QRegExp nodeRX(".*\\..*\\..*");     // matches *.*.*
    QRegExp habRX(".*\\..*");           // matches *.*

    bionetPtr = NULL;
    bionetType = ROOT;
    endpointType = NEITHER;

    if ( streamRX.exactMatch(name) ) {
        id = name.section('.', 2, 2).section(':', 1, 1);
        bionetType = STREAM;

        bionet_stream_t* stream = bionet_cache_lookup_stream(
            qPrintable(name.section('.', 0, 0)),
            qPrintable(name.section('.', 1, 1)),
            qPrintable(name.section('.', 2, 2).section(':', 0, 0)),
            qPrintable(id));

        if (bionet_stream_get_direction(stream) == BIONET_STREAM_DIRECTION_CONSUMER)
            endpointType = CONSUMER;
        else if (bionet_stream_get_direction(stream) == BIONET_STREAM_DIRECTION_PRODUCER)
            endpointType = PRODUCER;
        else
            endpointType = NEITHER;

        bionetPtr = (void*)stream;

    } else if ( nodeRX.exactMatch(name) ) {

        bionet_node_t* node = bionet_cache_lookup_node(
            qPrintable(name.section('.', 0, 0)),
            qPrintable(name.section('.', 1, 1)),
            qPrintable(name.section('.', 2, 2)));
        bionetPtr = (void*)node;

        id = name.section('.',2,2);
        bionetType = NODE;
        endpointType = NEITHER;

    } else if ( habRX.exactMatch(name) ) {

        bionet_hab_t* hab = bionet_cache_lookup_hab(
            qPrintable(name.section('.', 0, 0)),
            qPrintable(name.section('.', 1, 1)));
        bionetPtr = (void*)hab;

        id = name;
        bionetType = HAB;
        endpointType = NEITHER;

    } else if ( name.isEmpty() ) {
        id = QString();
        bionetType = ROOT;
        endpointType = NEITHER;
    }

    children = new QList<Node*>;

    /*
    path = new QPainterPath(QPoint(0,0));
    path->addEllipse(area);
    path->addText(area, Qt::AlignHCenter, Qt::AlignVCenter, id);
    */
}

Node::~Node() {
    while ( !children->isEmpty() ) {
        Node* child = children->takeFirst();
        delete child;
    }
    delete children;
}

void Node::addChild(Node* child) {
    children->append(child);
}

void Node::addChildWithoutArea(Node* child) {
    children->append(child);
}

bool Node::removeChild(QString id) {
    int size = children->size();

    for (int i = 0; i < size; i++ ) {
        Node* child = children->at(i);
        if (child->getId() == id) {
            Node* child = children->takeAt(i);
            delete child;
            return true;
        }
    }

    return false;
}

Node* Node::find(QString fullname) {
    if (this->name == fullname)
        return this;

    foreach (Node* node, *children) {
        Node* leaf = node->find(fullname);
        if (leaf != NULL)
            return leaf;
    }

    return NULL;
}

Node* Node::find(QPoint point) {

    if (area.contains(point))
        return this;

    foreach (Node* node, *children) {
        Node *leaf = node->find(point);
        if (leaf != NULL)
            return leaf;
    }

    return NULL;
}

Node* Node::find(QRect rect) {

    if (area.intersects(rect))
        return this;

    foreach (Node* node, *children) {
        Node *leaf = node->find(rect);
        if (leaf != NULL)
            return leaf;
    }

    return NULL;
}

void Node::setArea(QRect newArea) {
    this->area = newArea;
    //cout << "this->area = " << area.x() << ", " << area.y() << endl;
}
<|endoftext|>


<commit_before>#include "recvfileprogressdialog.h"

#include <QStringList>
#include <QMessageBox>
#include <QFileDialog>
#include <QTimer>

#include "constants.h"

/**
 * Returns a list of requested files
 */
QList<RecvFileInfo> RecvFileProgressDialog::parsePayloadFileList(QByteArray payload)
{
    QList<RecvFileInfo> recvFiles;
    QList<QByteArray> headers = payload.split(QOM_FILELIST_SEPARATOR);
    foreach(QByteArray header, headers)
    {
        RecvFileInfo info;
        QList<QByteArray> tokens = header.split(':');
        qDebug() << "Tokens:" << tokens;
        if(tokens.size() < 5)
            continue;
        info.fileID = tokens[0].toInt();
        info.fileName = tokens[1];
        info.size = tokens[2].toInt(0, 16);
        info.mtime = tokens[3].toInt(0, 16);
        info.type = tokens[4].toInt(0, 16);
        qDebug() << "Parsed ID:"<<info.fileID<<"name:"<<info.fileName<<"size:"<<info.size<<"mtime"<<info.mtime<<"type:"<<info.type;

        //NOTE: additional tokens can be parsed here if required

        recvFiles << info ;
    }
    return recvFiles;
}

void RecvFileProgressDialog::error(QAbstractSocket::SocketError e)
{
    qDebug() << "Socket error:" << e << m_socket->errorString();
    reject();
}

void RecvFileProgressDialog::accept()
{
    qDebug() << "$$$$$ Socket disconnected";
    QProgressDialog::accept();
}

void RecvFileProgressDialog::startReceiving()
{
    m_socket = new QTcpSocket(this);
    m_socket->abort();
    m_socket->connectToHost(m_msg.sender()->addressString(), UDP_PORT);

    connect(m_socket, SIGNAL(readyRead()), this, SLOT(readRequest()));
    connect(m_socket, SIGNAL(connected()), this, SLOT(requestFiles()));
    connect(m_socket, SIGNAL(disconnected()), this, SLOT(accept()));
    connect(m_socket, SIGNAL(disconnected()), m_socket, SLOT(deleteLater()));
    connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
}

void RecvFileProgressDialog::requestFiles()
{
    while(!m_fileHeaders.isEmpty())
    {
        if(m_waitingForData > 0)
        {
            qDebug() << "Waiting for"<< m_waitingForData;
            QTimer::singleShot(1000, this, SLOT(requestFiles()));
            break;
        }
        startReceiving();
        RecvFileInfo info = m_fileHeaders.takeFirst();
        m_requestType = info.type;

        QByteArray payload = QByteArray::number(m_msg.packetNo(), 16);
        payload += ":";
        payload += QByteArray::number(info.fileID, 16);
        qDebug() << "Writing request for file data"<<info.fileName << "id" << info.fileID;

        QString path = m_saveDir + QDir::separator() + info.fileName;

        //TODO: fix offset call
        if(m_requestType == QOM_FILE_REGULAR)
        {
            m_currentSize = info.size;
            m_waitingForData = info.size;
            if(!openFile(path))
                continue;
            payload += ":0";
            writeBlock(messenger()->makeMessage(QOM_GETFILEDATA, payload).toAscii());
        }
        else if(m_requestType == QOM_FILE_DIR)
        {
            writeBlock(messenger()->makeMessage(QOM_GETDIRFILES, payload).toAscii());
        }
    }
}

void RecvFileProgressDialog::informUser()
{
    QString message(m_msg.sender()->name());
    message.append(tr(" has sent the following files\n\n"));

    qint64 totalSize = 0;

    foreach(RecvFileInfo info, m_fileHeaders)
    {
        message.append(info.fileName);
        message.append(" (");
        if(info.type == QOM_FILE_REGULAR)
        {
            message.append(QString::number(info.size));
            totalSize += info.size;
            message.append(" bytes)\n");
        }
        else if(info.type == QOM_FILE_DIR)
        {
            message.append("Directory, unknown size)\n");
        }
    }

    message.append(tr("\nTotal Size: "));
    if(totalSize > 1024*1024)
    {
        message.append(QString::number(totalSize / (1024*1024) ));
        message.append("Mb");
    }
    else if( totalSize > 1024 )
    {
        message.append(QString::number(totalSize / 1024));
        message.append("Kb");
    }
    else
    {
        message.append(QString::number(totalSize));
        message.append("bytes");
    }

    QString msg = m_msg.payload().left(m_msg.payload().indexOf("\a"));
    if(!msg.isEmpty())
        message.append("\n\nMessage: " + msg);

    message.append(tr("\n\nDo you want to accept the files?"));
    if(QMessageBox::No == QMessageBox::question(NULL,
                           tr("Receiving files"),
                           message,
                           QMessageBox::Yes | QMessageBox::No,
                           QMessageBox::Yes)) // last is default button
        reject();
    else
        m_saveDir = QFileDialog::getExistingDirectory(this, tr("Save To"));

    if(!m_saveDir.isEmpty())
    {
        qDebug() << m_saveDir;
    }
}

// TODO: share with sending dialog?
bool RecvFileProgressDialog::writeBlock(QByteArray b)
{
    int bytesToWrite = b.size();
    int bytesWritten = 0;
    do
    {
        qDebug() << "Writing" << b << "to socket";
        bytesWritten = m_socket->write(b);
        if(bytesWritten == -1)
            return false;
        b.remove(0, bytesWritten);
        bytesToWrite -= bytesWritten;
    } while(bytesToWrite > 0);
    return true;
}

/**
* Instead of actually reading the data
* it notifies seperate functions depending
* on what we are reading, which read as required
*/
void RecvFileProgressDialog::readRequest()
{
//     QByteArray data;
//     while(m_socket->bytesAvailable())
//     {
//         data += m_socket->read(1024);
//         //qDebug() << "Reading";
//     }
//     qDebug() << ":: readRequest data size" << data.size();
//     qDebug() << data << "\n\n";
    if(m_requestType == QOM_FILE_REGULAR)
        requestWriteToFile();
    else if(m_requestType == QOM_FILE_DIR)
        requestWriteToDirectory();
}

void RecvFileProgressDialog::requestWriteToFile()
{
    QByteArray data = m_socket->readAll();
    writeToFile(data);
}

/**
 * Attempts to read headers individually
 * and then read the file as required
 */
void RecvFileProgressDialog::requestWriteToDirectory()
{
    while(m_socket->bytesAvailable())
    {
        if(m_inHeader)
        {
            m_header += m_socket->read(1);
            //qDebug() << m_header;
            QByteArray drop; // TODO: ideally not required
            RecvFileInfo info = parseDirectoryHeader(m_header, &drop);

            if(info.fileID >= 0)
            {
                qDebug() << "# Header parsed successfully";
                qDebug() << info.fileName << info.size << info.type;
                // we got a valid header, process it
                m_inHeader = false;
                m_header.clear();

                if(info.type == QOM_FILE_REGULAR)
                {
                    qDebug() << "Now writing new file" << info.fileName;
                    if(!openFile(m_dir->absoluteFilePath(info.fileName)))
                        return ;
                    m_waitingForData = info.size;
                    m_currentSize = info.size;
                }
                else if(info.type == QOM_FILE_DIR)
                {
                    qDebug() << "Creating directory" << info.fileName;
                    bool made = false;
                    if(m_dir == NULL)
                        made = makeDirectory(m_saveDir + QDir::separator() + info.fileName);
                    else
                        made = makeDirectory(m_dir->absoluteFilePath(info.fileName));
                    if(!made)
                        return;
                    m_inHeader = true;
                    continue;
                }
                else if(info.type == QOM_FILE_RETPARENT)
                {
                    qDebug() << "Going up";
                    m_dir->cdUp();
                    if(m_dir->absolutePath() == m_saveDir)
                        accept();
                    m_inHeader = true;
                    continue;
                }
                else
                {
                    QMessageBox::critical(this, tr("Receive failed"), tr("Bad header! Could not understand file type %1").arg(info.type));
                    reject();
                }
            }
        }
        // write to file whatever we get
        if(!m_inHeader)
        {
            QByteArray fileData = m_socket->read(m_waitingForData);
            writeToFile(fileData);
            if(m_waitingForData == 0)
            {
                m_inHeader = true;
            }
        }
    }

}

bool RecvFileProgressDialog::openFile(const QString& fileName)
{
    m_currentFile = new QFile(fileName);
    if(m_currentFile->exists())
    {
        if(QMessageBox::No == QMessageBox::question(this, tr("Replace file"), tr("File %1 exists, overwrite?").arg(fileName),
                               QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
        {
            m_waitingForData = 0;
            return false;
        }
    }
    if(!m_currentFile->open(QIODevice::Truncate | QIODevice::WriteOnly))
    {
        qWarning() << "Cannot open" << fileName << "for writing";
        m_currentFile->close();
        m_waitingForData = 0;
        return false;
    }
    qDebug() << fileName << "Opened with truncate";
    return true;
}

/*
 * TODO: i know, the remainder thing is a bad hack.
 */
bool RecvFileProgressDialog::writeToFile(QByteArray& b, QByteArray* remainder)
{
    if(m_currentFile == NULL)
        return false;
    while(b.size() > 0)
    {
        if(m_waitingForData < b.size())
        {
            qDebug() << "Excess data, setting remainder";
            if(remainder != NULL)
            {
                *remainder = b.right(b.size() - m_waitingForData);
            }
            b = b.left(m_waitingForData);
        }
        int written = m_currentFile->write(b);
        //qDebug() << "Wrote" << written << "bytes to" << m_currentFile->fileName();
        if(written == -1)
        {
            qWarning() << "Write error" << m_currentFile->errorString();
            m_currentFile->close();
            return false;
        }
        m_waitingForData -= written;
        qDebug() << "Wrote" << written << "bytes" << "Left" << m_waitingForData;
        b.remove(0, written);
    }

    setLabelText(tr("Receiving %1").arg(m_currentFile->fileName()));
    setValue((float)(m_currentSize-m_waitingForData)/m_currentSize * 100.0);
    if(m_waitingForData <= 0)
    {
        qDebug() << "Finished writing" << m_currentFile->fileName();
        m_currentFile->close();
    }
    return true;
}

bool RecvFileProgressDialog::makeDirectory(const QString& path)
{
    qDebug() << "makeDirectory" << path;
    if(m_dir != NULL)
    {
        delete m_dir;
        m_dir = NULL;
    }
    m_dir = new QDir(path);
    if(!m_dir->exists() && !m_dir->mkdir(path))
    {
        // TODO: convert to QMessageBox
        qWarning() << "Could not create directory" << m_dir->absolutePath() ;
        return false;
    }
    return true;
}

bool RecvFileProgressDialog::writeToDirectory(QByteArray& b)
{
    m_header += b;
    //qDebug() << "At the beginning of writeToDirectory" << m_header;
    QByteArray leftover;
    RecvFileInfo info = parseDirectoryHeader(m_header, &leftover);
    if(m_waitingForData != 0 && m_header.isEmpty())
    {
        qDebug() << "Need to flush";
        exit(5);
    }

    //info is filled, only leftover is valuable
    if(info.fileID >= 0)
    {
        m_inHeader = false;
        m_header = leftover;
        //qDebug() << "! Leftover" << leftover;

        if(info.type == QOM_FILE_REGULAR)
        {
            qDebug() << "Now writing new file" << info.fileName;
            if(!openFile(m_dir->absoluteFilePath(info.fileName)))
                return false;
            m_waitingForData = info.size;
            m_currentSize = info.size;

            QByteArray moreLeftover;

            // leftover can't immediately have another header
            // any headers it has now are in moreLeftover
            if(!writeToFile(leftover, &moreLeftover))
                return false;
            //qDebug() << "!moreLeftover" << moreLeftover;
            qDebug() << "line 306:" << m_waitingForData;
            if(m_waitingForData == 0)
            {
                m_inHeader = true;
                m_header = moreLeftover;
            }
            else
            {
                m_inHeader = false;
                m_header.clear();
            }
        }
        else if(info.type == QOM_FILE_DIR)
        {
            qDebug() << "Creating new directory" << info.fileName;
            bool made = false;
            if(m_dir == NULL)
                made = makeDirectory(m_saveDir + QDir::separator() + info.fileName);
            else
                made = makeDirectory(m_dir->absoluteFilePath(info.fileName));
            if(!made)
                return false;
            m_inHeader = true;
            //m_header.clear();
        }
        else if(info.type == QOM_FILE_RETPARENT)
        {
            qDebug() << "Going up";
            m_dir->cdUp();
            m_inHeader = true;
        }
        //qDebug() << "After all processing of header, in header?" << m_inHeader<<"\n";
    }

    if(!m_inHeader)
    {
        qDebug() << "> In file";
        QByteArray leftover;
        writeToFile(m_header, &leftover);
        qDebug() << "line 337:" << m_waitingForData ;
        if(m_waitingForData == 0)
        {
            m_header = leftover;
            m_inHeader = true;
        }
        else
        {
            m_header.clear();
            m_inHeader = false;
        }
    }
    qDebug() << " << Returning from writeToDirectory";
    return true;
}

/**
 * Tries to parse a header if it can
 * if it can, fills in RecvFileInfo correctly
 * Otherwise sets RecvFileInfo.fileID to -1, other fields are kept default
 * If any data is left over after parsing header, it is stored in remainder.
 */
RecvFileInfo RecvFileProgressDialog::parseDirectoryHeader(const QByteArray& a, QByteArray* remainder)
{
    bool bad = false;
    RecvFileInfo ret;

    if(a.contains(":"))
    {
        QList<QByteArray> tokens = a.split(':');
        int headerSize = tokens[0].toInt(0, 16);
        //qDebug() << "Header size:"<<headerSize;
        if( headerSize == 0 ) //bad data, perhaps binary
            bad = true;
        else if(a.size() < headerSize || tokens.size() < 4)
        {
            //qDebug() << "not enough data" << tokens << "expected" << tokens[0].size();
            bad = true;
        }
        else
        {
            //qDebug() << "Header list" << tokens;
            ret.fileID = 0;
            ret.fileName = tokens[1];
            ret.size = tokens[2].toLongLong(0, 16);
            ret.type = tokens[3].toInt(0, 16);

            *remainder = a.right(a.size() - headerSize);
        }
    }
    else
        bad = true;

    if(bad)
    {
        ret.fileID = -1;
    }

    return ret;
}<commit_msg>toLongLong to accomodate large files<commit_after>#include "recvfileprogressdialog.h"

#include <QStringList>
#include <QMessageBox>
#include <QFileDialog>
#include <QTimer>

#include "constants.h"

/**
 * Returns a list of requested files
 */
QList<RecvFileInfo> RecvFileProgressDialog::parsePayloadFileList(QByteArray payload)
{
    QList<RecvFileInfo> recvFiles;
    QList<QByteArray> headers = payload.split(QOM_FILELIST_SEPARATOR);
    foreach(QByteArray header, headers)
    {
        RecvFileInfo info;
        QList<QByteArray> tokens = header.split(':');
        qDebug() << "Tokens:" << tokens;
        if(tokens.size() < 5)
            continue;
        info.fileID = tokens[0].toInt();
        info.fileName = tokens[1];
        info.size = tokens[2].toLongLong(0, 16);
        info.mtime = tokens[3].toInt(0, 16);
        info.type = tokens[4].toInt(0, 16);
        qDebug() << "Parsed ID:"<<info.fileID<<"name:"<<info.fileName<<"size:"<<info.size<<"mtime"<<info.mtime<<"type:"<<info.type;

        //NOTE: additional tokens can be parsed here if required

        recvFiles << info ;
    }
    return recvFiles;
}

void RecvFileProgressDialog::error(QAbstractSocket::SocketError e)
{
    qDebug() << "Socket error:" << e << m_socket->errorString();
    reject();
}

void RecvFileProgressDialog::accept()
{
    qDebug() << "$$$$$ Socket disconnected";
    QProgressDialog::accept();
}

void RecvFileProgressDialog::startReceiving()
{
    m_socket = new QTcpSocket(this);
    m_socket->abort();
    m_socket->connectToHost(m_msg.sender()->addressString(), UDP_PORT);

    connect(m_socket, SIGNAL(readyRead()), this, SLOT(readRequest()));
    connect(m_socket, SIGNAL(connected()), this, SLOT(requestFiles()));
    connect(m_socket, SIGNAL(disconnected()), this, SLOT(accept()));
    connect(m_socket, SIGNAL(disconnected()), m_socket, SLOT(deleteLater()));
    connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
}

void RecvFileProgressDialog::requestFiles()
{
    while(!m_fileHeaders.isEmpty())
    {
        if(m_waitingForData > 0)
        {
            qDebug() << "Waiting for"<< m_waitingForData;
            QTimer::singleShot(1000, this, SLOT(requestFiles()));
            break;
        }
        startReceiving();
        RecvFileInfo info = m_fileHeaders.takeFirst();
        m_requestType = info.type;

        QByteArray payload = QByteArray::number(m_msg.packetNo(), 16);
        payload += ":";
        payload += QByteArray::number(info.fileID, 16);
        qDebug() << "Writing request for file data"<<info.fileName << "id" << info.fileID;

        QString path = m_saveDir + QDir::separator() + info.fileName;

        //TODO: fix offset call
        if(m_requestType == QOM_FILE_REGULAR)
        {
            m_currentSize = info.size;
            m_waitingForData = info.size;
            if(!openFile(path))
                continue;
            payload += ":0";
            writeBlock(messenger()->makeMessage(QOM_GETFILEDATA, payload).toAscii());
        }
        else if(m_requestType == QOM_FILE_DIR)
        {
            writeBlock(messenger()->makeMessage(QOM_GETDIRFILES, payload).toAscii());
        }
    }
}

void RecvFileProgressDialog::informUser()
{
    QString message(m_msg.sender()->name());
    message.append(tr(" has sent the following files\n\n"));

    qint64 totalSize = 0;

    foreach(RecvFileInfo info, m_fileHeaders)
    {
        message.append(info.fileName);
        message.append(" (");
        if(info.type == QOM_FILE_REGULAR)
        {
            message.append(QString::number(info.size));
            totalSize += info.size;
            message.append(" bytes)\n");
        }
        else if(info.type == QOM_FILE_DIR)
        {
            message.append("Directory, unknown size)\n");
        }
    }

    message.append(tr("\nTotal Size: "));
    if(totalSize > 1024*1024)
    {
        message.append(QString::number(totalSize / (1024*1024) ));
        message.append("Mb");
    }
    else if( totalSize > 1024 )
    {
        message.append(QString::number(totalSize / 1024));
        message.append("Kb");
    }
    else
    {
        message.append(QString::number(totalSize));
        message.append("bytes");
    }

    QString msg = m_msg.payload().left(m_msg.payload().indexOf("\a"));
    if(!msg.isEmpty())
        message.append("\n\nMessage: " + msg);

    message.append(tr("\n\nDo you want to accept the files?"));
    if(QMessageBox::No == QMessageBox::question(NULL,
                           tr("Receiving files"),
                           message,
                           QMessageBox::Yes | QMessageBox::No,
                           QMessageBox::Yes)) // last is default button
        reject();
    else
        m_saveDir = QFileDialog::getExistingDirectory(this, tr("Save To"));

    if(!m_saveDir.isEmpty())
    {
        qDebug() << m_saveDir;
    }
}

// TODO: share with sending dialog?
bool RecvFileProgressDialog::writeBlock(QByteArray b)
{
    int bytesToWrite = b.size();
    int bytesWritten = 0;
    do
    {
        qDebug() << "Writing" << b << "to socket";
        bytesWritten = m_socket->write(b);
        if(bytesWritten == -1)
            return false;
        b.remove(0, bytesWritten);
        bytesToWrite -= bytesWritten;
    } while(bytesToWrite > 0);
    return true;
}

/**
* Instead of actually reading the data
* it notifies seperate functions depending
* on what we are reading, which read as required
*/
void RecvFileProgressDialog::readRequest()
{
//     QByteArray data;
//     while(m_socket->bytesAvailable())
//     {
//         data += m_socket->read(1024);
//         //qDebug() << "Reading";
//     }
//     qDebug() << ":: readRequest data size" << data.size();
//     qDebug() << data << "\n\n";
    if(m_requestType == QOM_FILE_REGULAR)
        requestWriteToFile();
    else if(m_requestType == QOM_FILE_DIR)
        requestWriteToDirectory();
}

void RecvFileProgressDialog::requestWriteToFile()
{
    QByteArray data = m_socket->readAll();
    writeToFile(data);
}

/**
 * Attempts to read headers individually
 * and then read the file as required
 */
void RecvFileProgressDialog::requestWriteToDirectory()
{
    while(m_socket->bytesAvailable())
    {
        if(m_inHeader)
        {
            m_header += m_socket->read(1);
            //qDebug() << m_header;
            QByteArray drop; // TODO: ideally not required
            RecvFileInfo info = parseDirectoryHeader(m_header, &drop);

            if(info.fileID >= 0)
            {
                qDebug() << "# Header parsed successfully";
                qDebug() << info.fileName << info.size << info.type;
                // we got a valid header, process it
                m_inHeader = false;
                m_header.clear();

                if(info.type == QOM_FILE_REGULAR)
                {
                    qDebug() << "Now writing new file" << info.fileName;
                    if(!openFile(m_dir->absoluteFilePath(info.fileName)))
                        return ;
                    m_waitingForData = info.size;
                    m_currentSize = info.size;
                }
                else if(info.type == QOM_FILE_DIR)
                {
                    qDebug() << "Creating directory" << info.fileName;
                    bool made = false;
                    if(m_dir == NULL)
                        made = makeDirectory(m_saveDir + QDir::separator() + info.fileName);
                    else
                        made = makeDirectory(m_dir->absoluteFilePath(info.fileName));
                    if(!made)
                        return;
                    m_inHeader = true;
                    continue;
                }
                else if(info.type == QOM_FILE_RETPARENT)
                {
                    qDebug() << "Going up";
                    m_dir->cdUp();
                    if(m_dir->absolutePath() == m_saveDir)
                        accept();
                    m_inHeader = true;
                    continue;
                }
                else
                {
                    QMessageBox::critical(this, tr("Receive failed"), tr("Bad header! Could not understand file type %1").arg(info.type));
                    reject();
                }
            }
        }
        // write to file whatever we get
        if(!m_inHeader)
        {
            QByteArray fileData = m_socket->read(m_waitingForData);
            writeToFile(fileData);
            if(m_waitingForData == 0)
            {
                m_inHeader = true;
            }
        }
    }

}

bool RecvFileProgressDialog::openFile(const QString& fileName)
{
    m_currentFile = new QFile(fileName);
    if(m_currentFile->exists())
    {
        if(QMessageBox::No == QMessageBox::question(this, tr("Replace file"), tr("File %1 exists, overwrite?").arg(fileName),
                               QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes))
        {
            m_waitingForData = 0;
            return false;
        }
    }
    if(!m_currentFile->open(QIODevice::Truncate | QIODevice::WriteOnly))
    {
        qWarning() << "Cannot open" << fileName << "for writing";
        m_currentFile->close();
        m_waitingForData = 0;
        return false;
    }
    qDebug() << fileName << "Opened with truncate";
    return true;
}

/*
 * TODO: i know, the remainder thing is a bad hack.
 */
bool RecvFileProgressDialog::writeToFile(QByteArray& b, QByteArray* remainder)
{
    if(m_currentFile == NULL)
        return false;
    while(b.size() > 0)
    {
        if(m_waitingForData < b.size())
        {
            qDebug() << "Excess data, setting remainder";
            if(remainder != NULL)
            {
                *remainder = b.right(b.size() - m_waitingForData);
            }
            b = b.left(m_waitingForData);
        }
        int written = m_currentFile->write(b);
        //qDebug() << "Wrote" << written << "bytes to" << m_currentFile->fileName();
        if(written == -1)
        {
            qWarning() << "Write error" << m_currentFile->errorString();
            m_currentFile->close();
            return false;
        }
        m_waitingForData -= written;
        qDebug() << "Wrote" << written << "bytes" << "Left" << m_waitingForData;
        b.remove(0, written);
    }

    setLabelText(tr("Receiving %1").arg(m_currentFile->fileName()));
    setValue((float)(m_currentSize-m_waitingForData)/m_currentSize * 100.0);
    if(m_waitingForData <= 0)
    {
        qDebug() << "Finished writing" << m_currentFile->fileName();
        m_currentFile->close();
    }
    return true;
}

bool RecvFileProgressDialog::makeDirectory(const QString& path)
{
    qDebug() << "makeDirectory" << path;
    if(m_dir != NULL)
    {
        delete m_dir;
        m_dir = NULL;
    }
    m_dir = new QDir(path);
    if(!m_dir->exists() && !m_dir->mkdir(path))
    {
        // TODO: convert to QMessageBox
        qWarning() << "Could not create directory" << m_dir->absolutePath() ;
        return false;
    }
    return true;
}

bool RecvFileProgressDialog::writeToDirectory(QByteArray& b)
{
    m_header += b;
    //qDebug() << "At the beginning of writeToDirectory" << m_header;
    QByteArray leftover;
    RecvFileInfo info = parseDirectoryHeader(m_header, &leftover);
    if(m_waitingForData != 0 && m_header.isEmpty())
    {
        qDebug() << "Need to flush";
        exit(5);
    }

    //info is filled, only leftover is valuable
    if(info.fileID >= 0)
    {
        m_inHeader = false;
        m_header = leftover;
        //qDebug() << "! Leftover" << leftover;

        if(info.type == QOM_FILE_REGULAR)
        {
            qDebug() << "Now writing new file" << info.fileName;
            if(!openFile(m_dir->absoluteFilePath(info.fileName)))
                return false;
            m_waitingForData = info.size;
            m_currentSize = info.size;

            QByteArray moreLeftover;

            // leftover can't immediately have another header
            // any headers it has now are in moreLeftover
            if(!writeToFile(leftover, &moreLeftover))
                return false;
            //qDebug() << "!moreLeftover" << moreLeftover;
            qDebug() << "line 306:" << m_waitingForData;
            if(m_waitingForData == 0)
            {
                m_inHeader = true;
                m_header = moreLeftover;
            }
            else
            {
                m_inHeader = false;
                m_header.clear();
            }
        }
        else if(info.type == QOM_FILE_DIR)
        {
            qDebug() << "Creating new directory" << info.fileName;
            bool made = false;
            if(m_dir == NULL)
                made = makeDirectory(m_saveDir + QDir::separator() + info.fileName);
            else
                made = makeDirectory(m_dir->absoluteFilePath(info.fileName));
            if(!made)
                return false;
            m_inHeader = true;
            //m_header.clear();
        }
        else if(info.type == QOM_FILE_RETPARENT)
        {
            qDebug() << "Going up";
            m_dir->cdUp();
            m_inHeader = true;
        }
        //qDebug() << "After all processing of header, in header?" << m_inHeader<<"\n";
    }

    if(!m_inHeader)
    {
        qDebug() << "> In file";
        QByteArray leftover;
        writeToFile(m_header, &leftover);
        qDebug() << "line 337:" << m_waitingForData ;
        if(m_waitingForData == 0)
        {
            m_header = leftover;
            m_inHeader = true;
        }
        else
        {
            m_header.clear();
            m_inHeader = false;
        }
    }
    qDebug() << " << Returning from writeToDirectory";
    return true;
}

/**
 * Tries to parse a header if it can
 * if it can, fills in RecvFileInfo correctly
 * Otherwise sets RecvFileInfo.fileID to -1, other fields are kept default
 * If any data is left over after parsing header, it is stored in remainder.
 */
RecvFileInfo RecvFileProgressDialog::parseDirectoryHeader(const QByteArray& a, QByteArray* remainder)
{
    bool bad = false;
    RecvFileInfo ret;

    if(a.contains(":"))
    {
        QList<QByteArray> tokens = a.split(':');
        int headerSize = tokens[0].toInt(0, 16);
        //qDebug() << "Header size:"<<headerSize;
        if( headerSize == 0 ) //bad data, perhaps binary
            bad = true;
        else if(a.size() < headerSize || tokens.size() < 4)
        {
            //qDebug() << "not enough data" << tokens << "expected" << tokens[0].size();
            bad = true;
        }
        else
        {
            //qDebug() << "Header list" << tokens;
            ret.fileID = 0;
            ret.fileName = tokens[1];
            ret.size = tokens[2].toLongLong(0, 16);
            ret.type = tokens[3].toInt(0, 16);

            *remainder = a.right(a.size() - headerSize);
        }
    }
    else
        bad = true;

    if(bad)
    {
        ret.fileID = -1;
    }

    return ret;
}<|endoftext|>


<commit_before>/*  This file is part of the KDE project
    Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) version 3, or any
    later version accepted by the membership of KDE e.V. (or its
    successor approved by the membership of KDE e.V.), Nokia Corporation
    (or its successors, if any) and the KDE Free Qt Foundation, which shall
    act as a proxy defined in Section 6 of version 3 of the license.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "effectwidget.h"
#include "effectwidget_p.h"

#include <QtCore/QtAlgorithms>
#include <QtCore/QList>

#include "effect.h"
#include "effectparameter.h"
#include "phonondefs_p.h"
#include <QtGui/QBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QSpinBox>
#include <QtGui/QCheckBox>
#include <QtGui/QComboBox>
#include <QtGui/QSlider>
#include <limits>

#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
static const qreal DEFAULT_MIN = std::numeric_limits<qreal>::min();
static const qreal DEFAULT_MAX = std::numeric_limits<qreal>::max();
static const int DEFAULT_MIN_INT = std::numeric_limits<int>::min();
static const int DEFAULT_MAX_INT = std::numeric_limits<int>::max();
static const int SLIDER_RANGE = 8;
static const int TICKINTERVAL = 4;

QT_BEGIN_NAMESPACE

#ifndef QT_NO_PHONON_EFFECTWIDGET

namespace Phonon
{

EffectWidget::EffectWidget(Effect *effect, QWidget *parent)
    : QWidget(parent),
    k_ptr(new EffectWidgetPrivate(effect))
{
    K_D(EffectWidget);
    d->q_ptr = this;
    d->autogenerateUi();
}

EffectWidget::~EffectWidget()
{
    delete k_ptr;
}

/*
EffectWidget::EffectWidget(EffectWidgetPrivate &dd, QWidget *parent)
    : QWidget(parent)
    , k_ptr(&dd)
{
    K_D(EffectWidget);
    d->q_ptr = this;
    d->autogenerateUi();
}
*/

EffectWidgetPrivate::EffectWidgetPrivate(Effect *e)
    : effect(e)
{
    //TODO: look up whether there is a specialized widget for this effect. This
    //could be a DSO or a Designer ui file found via KTrader.
    //
    //if no specialized widget is available:
}

void EffectWidgetPrivate::autogenerateUi()
{
    Q_Q(EffectWidget);
    QVBoxLayout *mainLayout = new QVBoxLayout(q);
    mainLayout->setMargin(0);
    foreach (const EffectParameter &para, effect->parameters()) {
        QVariant value = effect->parameterValue(para);
        QHBoxLayout *pLayout = new QHBoxLayout;
        mainLayout->addLayout(pLayout);

        QLabel *label = new QLabel(q);
        pLayout->addWidget(label);
        label->setText(para.name());
#ifndef QT_NO_TOOLTIP
        label->setToolTip(para.description());
#endif

        QWidget *control = 0;
        switch (para.type()) {
        case QVariant::String:
            {
                QComboBox *cb = new QComboBox(q);
                control = cb;
                if (value.type() == QVariant::Int) {
                    //value just defines the item index
                    foreach (const QVariant &item, para.possibleValues()) {
                        cb->addItem(item.toString());
                    }
                    cb->setCurrentIndex(value.toInt());
                    QObject::connect(cb, SIGNAL(currentIndexChanged(int)), q, SLOT(_k_setIntParameter(int)));
                } else {
                    foreach (const QVariant &item, para.possibleValues()) {
                        cb->addItem(item.toString());
                        if (item == value) {
                            cb->setCurrentIndex(cb->count() - 1);
                        }
                    }
                    QObject::connect(cb, SIGNAL(currentIndexChanged(QString)), q, SLOT(_k_setStringParameter(QString)));
                }
            }
            break;
        case QVariant::Bool:
            {
                QCheckBox *cb = new QCheckBox(q);
                control = cb;
                cb->setChecked(value.toBool());
                QObject::connect(cb, SIGNAL(toggled(bool)), q, SLOT(_k_setToggleParameter(bool)));
            }
            break;
        case QVariant::Int:
            {
                QSpinBox *sb = new QSpinBox(q);
                control = sb;
                bool minValueOk = false;
                bool maxValueOk = false;
                const int minValue = para.minimumValue().toInt(&minValueOk);
                const int maxValue = para.minimumValue().toInt(&maxValueOk);

                sb->setRange(minValueOk ? minValue : DEFAULT_MIN_INT, maxValueOk ? maxValue : DEFAULT_MAX_INT);
                sb->setValue(value.toInt());
                QObject::connect(sb, SIGNAL(valueChanged(int)), q, SLOT(_k_setIntParameter(int)));
            }
            break;
        case QVariant::Double:
            {
                const double minValue = (para.minimumValue().type() == QVariant::Double ?
                    para.minimumValue().toDouble() : DEFAULT_MIN);
                const double maxValue = (para.maximumValue().type() == QVariant::Double ?
                    para.maximumValue().toDouble() : DEFAULT_MAX);

                if (minValue == -1. && maxValue == 1.) {
                    //Special case values between -1 and 1.0 to use a slider for improved usability
                    QSlider *slider = new QSlider(Qt::Horizontal, q);
                    slider->setRange(-SLIDER_RANGE, +SLIDER_RANGE);
                    slider->setValue(int(SLIDER_RANGE * value.toDouble()));
                    slider->setTickPosition(QSlider::TicksBelow);
                    slider->setTickInterval(TICKINTERVAL);
                    QObject::connect(slider, SIGNAL(valueChanged(int)), q, SLOT(_k_setSliderParameter(int)));
                } else {
                    double step = 0.1;
                    if (qAbs(maxValue - minValue) > 50)
                        step = 1.0;
                    QDoubleSpinBox *sb = new QDoubleSpinBox(q);
                    control = sb;
                    sb->setRange(minValue, maxValue);
                    sb->setValue(value.toDouble());
                    sb->setSingleStep(step);
                    QObject::connect(sb, SIGNAL(valueChanged(double)), q,
                        SLOT(_k_setDoubleParameter(double)));
                }
            }
            break;
        default:
            break;
        }

#ifndef QT_NO_TOOLTIP
        control->setToolTip(para.description());
#endif
        if (control) {
#ifndef QT_NO_SHORTCUT
            label->setBuddy(control);
#endif
            pLayout->addWidget(control);
            parameterForObject.insert(control, para);
        }
    }
}

void EffectWidgetPrivate::_k_setToggleParameter(bool checked)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], checked);
    }
}

void EffectWidgetPrivate::_k_setIntParameter(int value)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], value);
    }
}

void EffectWidgetPrivate::_k_setDoubleParameter(double value)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], value);
    }
}

void EffectWidgetPrivate::_k_setStringParameter(const QString &value)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], value);
    }
}

void EffectWidgetPrivate::_k_setSliderParameter(int value)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], double(value) / double(SLIDER_RANGE));
    }
}

} // namespace Phonon

#endif // QT_NO_PHONON_EFFECTWIDGET

QT_END_NAMESPACE

#include "moc_effectwidget.cpp"

// vim: sw=4 ts=4
<commit_msg>Fix a crash in Phonon::EffectWidget<commit_after>/*  This file is part of the KDE project
    Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) version 3, or any
    later version accepted by the membership of KDE e.V. (or its
    successor approved by the membership of KDE e.V.), Nokia Corporation
    (or its successors, if any) and the KDE Free Qt Foundation, which shall
    act as a proxy defined in Section 6 of version 3 of the license.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "effectwidget.h"
#include "effectwidget_p.h"

#include <QtCore/QtAlgorithms>
#include <QtCore/QList>

#include "effect.h"
#include "effectparameter.h"
#include "phonondefs_p.h"
#include <QtGui/QBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QSpinBox>
#include <QtGui/QCheckBox>
#include <QtGui/QComboBox>
#include <QtGui/QSlider>
#include <limits>

#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
static const qreal DEFAULT_MIN = std::numeric_limits<qreal>::min();
static const qreal DEFAULT_MAX = std::numeric_limits<qreal>::max();
static const int DEFAULT_MIN_INT = std::numeric_limits<int>::min();
static const int DEFAULT_MAX_INT = std::numeric_limits<int>::max();
static const int SLIDER_RANGE = 8;
static const int TICKINTERVAL = 4;

QT_BEGIN_NAMESPACE

#ifndef QT_NO_PHONON_EFFECTWIDGET

namespace Phonon
{

EffectWidget::EffectWidget(Effect *effect, QWidget *parent)
    : QWidget(parent),
    k_ptr(new EffectWidgetPrivate(effect))
{
    K_D(EffectWidget);
    d->q_ptr = this;
    d->autogenerateUi();
}

EffectWidget::~EffectWidget()
{
    delete k_ptr;
}

/*
EffectWidget::EffectWidget(EffectWidgetPrivate &dd, QWidget *parent)
    : QWidget(parent)
    , k_ptr(&dd)
{
    K_D(EffectWidget);
    d->q_ptr = this;
    d->autogenerateUi();
}
*/

EffectWidgetPrivate::EffectWidgetPrivate(Effect *e)
    : effect(e)
{
    //TODO: look up whether there is a specialized widget for this effect. This
    //could be a DSO or a Designer ui file found via KTrader.
    //
    //if no specialized widget is available:
}

void EffectWidgetPrivate::autogenerateUi()
{
    Q_Q(EffectWidget);
    QVBoxLayout *mainLayout = new QVBoxLayout(q);
    mainLayout->setMargin(0);
    foreach (const EffectParameter &para, effect->parameters()) {
        QVariant value = effect->parameterValue(para);
        QHBoxLayout *pLayout = new QHBoxLayout;
        mainLayout->addLayout(pLayout);

        QLabel *label = new QLabel(q);
        pLayout->addWidget(label);
        label->setText(para.name());
#ifndef QT_NO_TOOLTIP
        label->setToolTip(para.description());
#endif

        QWidget *control = 0;
        switch (para.type()) {
        case QVariant::String:
            {
                QComboBox *cb = new QComboBox(q);
                control = cb;
                if (value.type() == QVariant::Int) {
                    //value just defines the item index
                    foreach (const QVariant &item, para.possibleValues()) {
                        cb->addItem(item.toString());
                    }
                    cb->setCurrentIndex(value.toInt());
                    QObject::connect(cb, SIGNAL(currentIndexChanged(int)), q, SLOT(_k_setIntParameter(int)));
                } else {
                    foreach (const QVariant &item, para.possibleValues()) {
                        cb->addItem(item.toString());
                        if (item == value) {
                            cb->setCurrentIndex(cb->count() - 1);
                        }
                    }
                    QObject::connect(cb, SIGNAL(currentIndexChanged(QString)), q, SLOT(_k_setStringParameter(QString)));
                }
            }
            break;
        case QVariant::Bool:
            {
                QCheckBox *cb = new QCheckBox(q);
                control = cb;
                cb->setChecked(value.toBool());
                QObject::connect(cb, SIGNAL(toggled(bool)), q, SLOT(_k_setToggleParameter(bool)));
            }
            break;
        case QVariant::Int:
            {
                QSpinBox *sb = new QSpinBox(q);
                control = sb;
                bool minValueOk = false;
                bool maxValueOk = false;
                const int minValue = para.minimumValue().toInt(&minValueOk);
                const int maxValue = para.minimumValue().toInt(&maxValueOk);

                sb->setRange(minValueOk ? minValue : DEFAULT_MIN_INT, maxValueOk ? maxValue : DEFAULT_MAX_INT);
                sb->setValue(value.toInt());
                QObject::connect(sb, SIGNAL(valueChanged(int)), q, SLOT(_k_setIntParameter(int)));
            }
            break;
        case QVariant::Double:
            {
                const double minValue = (para.minimumValue().type() == QVariant::Double ?
                    para.minimumValue().toDouble() : DEFAULT_MIN);
                const double maxValue = (para.maximumValue().type() == QVariant::Double ?
                    para.maximumValue().toDouble() : DEFAULT_MAX);

                if (minValue == -1. && maxValue == 1.) {
                    //Special case values between -1 and 1.0 to use a slider for improved usability
                    QSlider *slider = new QSlider(Qt::Horizontal, q);
                    control = slider;
                    slider->setRange(-SLIDER_RANGE, +SLIDER_RANGE);
                    slider->setValue(int(SLIDER_RANGE * value.toDouble()));
                    slider->setTickPosition(QSlider::TicksBelow);
                    slider->setTickInterval(TICKINTERVAL);
                    QObject::connect(slider, SIGNAL(valueChanged(int)), q, SLOT(_k_setSliderParameter(int)));
                } else {
                    double step = 0.1;
                    if (qAbs(maxValue - minValue) > 50)
                        step = 1.0;
                    QDoubleSpinBox *sb = new QDoubleSpinBox(q);
                    control = sb;
                    sb->setRange(minValue, maxValue);
                    sb->setValue(value.toDouble());
                    sb->setSingleStep(step);
                    QObject::connect(sb, SIGNAL(valueChanged(double)), q,
                        SLOT(_k_setDoubleParameter(double)));
                }
            }
            break;
        default:
            break;
        }

        if (control) {
#ifndef QT_NO_TOOLTIP
        control->setToolTip(para.description());
#endif
#ifndef QT_NO_SHORTCUT
            label->setBuddy(control);
#endif
            pLayout->addWidget(control);
            parameterForObject.insert(control, para);
        }
    }
}

void EffectWidgetPrivate::_k_setToggleParameter(bool checked)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], checked);
    }
}

void EffectWidgetPrivate::_k_setIntParameter(int value)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], value);
    }
}

void EffectWidgetPrivate::_k_setDoubleParameter(double value)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], value);
    }
}

void EffectWidgetPrivate::_k_setStringParameter(const QString &value)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], value);
    }
}

void EffectWidgetPrivate::_k_setSliderParameter(int value)
{
    Q_Q(EffectWidget);
    if (parameterForObject.contains(q->sender())) {
        effect->setParameterValue(parameterForObject[q->sender()], double(value) / double(SLIDER_RANGE));
    }
}

} // namespace Phonon

#endif // QT_NO_PHONON_EFFECTWIDGET

QT_END_NAMESPACE

#include "moc_effectwidget.cpp"

// vim: sw=4 ts=4
<|endoftext|>


<commit_before>#pragma once

#include "SuperComponent.hpp"
#include <glm\glm.hpp>

namespace Component {
    /// %Component handling a camera lens.
    class Lens : public SuperComponent {
        public:
            /// Create new lens.
            /**
             * @param entity Pointer to which entity this component corresponds.
             */
            Lens(Entity* entity);

            /// Save the component.
            /**
             * @return JSON value to be stored on disk.
             */
            Json::Value Save() const override;

            /// Load component from JSON node.
            /**
             * @param node JSON node to load from.
             */
            void Load(const Json::Value& node) override;

            /// Get the projection matrix.
            /**
             * @param screenSize Screen size in pixels.
             * @return The projection matrix.
             */
            glm::mat4 GetProjection(const glm::vec2& screenSize) const;

            /// Field of view, in degrees.
            /**
             * Default: 45.0
             */
            float fieldOfView = 45.f;

            /// Near plane.
            /**
             * Default: 0.5
             */
            float zNear = 0.5f;

            /// Far plane.
            /**
             * Default: 100.0
             */
            float zFar = 100.f;
    };
}
<commit_msg>Fix GLM include<commit_after>#pragma once

#include "SuperComponent.hpp"
#include <glm/glm.hpp>

namespace Component {
    /// %Component handling a camera lens.
    class Lens : public SuperComponent {
        public:
            /// Create new lens.
            /**
             * @param entity Pointer to which entity this component corresponds.
             */
            Lens(Entity* entity);

            /// Save the component.
            /**
             * @return JSON value to be stored on disk.
             */
            Json::Value Save() const override;

            /// Load component from JSON node.
            /**
             * @param node JSON node to load from.
             */
            void Load(const Json::Value& node) override;

            /// Get the projection matrix.
            /**
             * @param screenSize Screen size in pixels.
             * @return The projection matrix.
             */
            glm::mat4 GetProjection(const glm::vec2& screenSize) const;

            /// Field of view, in degrees.
            /**
             * Default: 45.0
             */
            float fieldOfView = 45.f;

            /// Near plane.
            /**
             * Default: 0.5
             */
            float zNear = 0.5f;

            /// Far plane.
            /**
             * Default: 100.0
             */
            float zFar = 100.f;
    };
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2014, Intel Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include <limits>
#include <sstream>
#include <string>
#include <stdint.h>
#include <cmath>

/* details namespace is here to hide implementation details to header end user. It
 * is NOT intended to be used outside. */
namespace details
{

/** Helper class to limit instantiation of templates */
template<typename T>
struct ConvertionAllowed;

/* List of allowed types for conversion */
template<> struct ConvertionAllowed<bool> {};
template<> struct ConvertionAllowed<uint64_t> {};
template<> struct ConvertionAllowed<int64_t> {};
template<> struct ConvertionAllowed<uint32_t> {};
template<> struct ConvertionAllowed<int32_t> {};
template<> struct ConvertionAllowed<uint16_t> {};
template<> struct ConvertionAllowed<int16_t> {};
template<> struct ConvertionAllowed<float> {};
template<> struct ConvertionAllowed<double> {};

template<typename T>
static inline bool convertTo(const std::string &str, T &result)
{
    /* Check that conversion to that type is allowed.
     * If this fails, this means that this template was not intended to be used
     * with this type, thus that the result is undefined. */
    ConvertionAllowed<T>();

    if (str.find_first_of(std::string("\r\n\t\v ")) != std::string::npos) {
        return false;
    }

    /* Check for a '-' in string. If type is unsigned and a - is found, the
     * parsing fails. This is made necessary because "-1" is read as 65535 for
     * uint16_t, for example */
    if (str.find("-") != std::string::npos
        && !std::numeric_limits<T>::is_signed) {
        return false;
    }

    std::stringstream ss(str);

    /* Sadly, the stream conversion does not handle hexadecimal format, thus
     * check is done manually */
    if (str.substr(0, 2) == "0x") {
        if (std::numeric_limits<T>::is_integer) {
            ss >> std::hex >> result;
        }
        else {
            /* Conversion undefined for non integers */
            return false;
        }
    } else {
        ss >> result;
    }

    return ss.eof() && !ss.fail() && !ss.bad();
}
} // namespace details

/**
 * Convert a string to a given type.
 *
 * This template function read the value of the type T in the given string.
 * The function does not allow to have white spaces around the value to parse
 * and tries to parse the whole string, which means that if some bytes were not
 * read in the string, the function fails.
 * Hexadecimal representation (ie numbers starting with 0x) is supported only
 * for integral types conversions.
 * Result may be modified, even in case of failure.
 *
 * @param[in]  str    the string to parse.
 * @param[out] result reference to object where to store the result.
 *
 * @return true if conversion was successful, false otherwise.
 */
template<typename T>
static inline bool convertTo(const std::string &str, T &result)
{
    return details::convertTo<T>(str, result);
}

/**
 * Specialization for float of convertTo template function.
 *
 * This function follows the same paradigm than it's generic version and is
 * based on it but makes furthers checks on the returned value.
 *
 * The specific implementation is made necessary because the stlport conversion
 * from string to float behaves differently than GNU STL: overflow produce
 * +/-Infinity rather than an error.
 *
 * @param[in]  str    the string to parse.
 * @param[out] result reference to object where to store the result.
 *
 * @return true if conversion was successful, false otherwise.
 */
template<>
inline bool convertTo<float>(const std::string &str, float &result)
{
    if (!details::convertTo(str, result)) {
        return false;
    }

    if (!std::isfinite(result)) {
        return false;
    }

    return true;
}

/**
 * Specialization for double of convertTo template function.
 *
 * This function follows the same paradigm than it's generic version and is
 * based on it but makes furthers checks on the returned value.
 *
 * The specific implementation is made necessary because the stlport conversion
 * from string to double behaves differently than GNU STL: overflow produce
 * +/-Infinity rather than an error.
 *
 * @param[in]  str    the string to parse.
 * @param[out] result reference to object where to store the result.
 *
 * @return true if conversion was successful, false otherwise.
 */
template<>
inline bool convertTo<double>(const std::string &str, double &result)
{
    if (!details::convertTo(str, result)) {
        return false;
    }

    if (!std::isfinite(result)) {
        return false;
    }

    return true;
}

/**
 * Specialization for boolean of convertTo template function.
 *
 * This function follows the same paradigm than it's generic version.
 * This function accepts to parse boolean as "0/1" or "false/true" or
 * "FALSE/TRUE".
 * The specific implementation is made necessary because the behaviour of
 * string streams when parsing boolean values is not sufficient to fit our
 * requirements. Indeed, parsing "true" will correctly parse the value, but the
 * end of stream is not reached which makes the ss.eof() fails in the generic
 * implementation.
 *
 * @param[in]  str    the string to parse.
 * @param[out] result reference to object where to store the result.
 *
 * @return true if conversion was successful, false otherwise.
 */
template<>
inline bool convertTo<bool>(const std::string &str, bool &result)
{
    if (str == "0" || str == "FALSE" || str == "false") {
        result = false;
        return true;
    }

    if (str == "1" || str == "TRUE" || str == "true") {
        result = true;
        return true;
    }

    return false;
}
<commit_msg>Define special case for convertTo<uint8_t>() and convertTo<int8_t>()<commit_after>/*
 * Copyright (c) 2011-2014, Intel Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#pragma once

#include <limits>
#include <sstream>
#include <string>
#include <stdint.h>
#include <cmath>

/* details namespace is here to hide implementation details to header end user. It
 * is NOT intended to be used outside. */
namespace details
{

/** Helper class to limit instantiation of templates */
template<typename T>
struct ConvertionAllowed;
template<typename T, typename Via>
struct ConvertionAllowedVia;

/* List of allowed types for conversion */
template<> struct ConvertionAllowed<bool> {};
template<> struct ConvertionAllowed<uint64_t> {};
template<> struct ConvertionAllowed<int64_t> {};
template<> struct ConvertionAllowed<uint32_t> {};
template<> struct ConvertionAllowed<int32_t> {};
template<> struct ConvertionAllowed<uint16_t> {};
template<> struct ConvertionAllowed<int16_t> {};
template<> struct ConvertionAllowed<int8_t> {};
template<> struct ConvertionAllowed<uint8_t> {};
template<> struct ConvertionAllowed<float> {};
template<> struct ConvertionAllowed<double> {};

/* Allow chars and unsigned chars to be converted via integers */
template<> struct ConvertionAllowedVia<uint8_t, uint32_t> {};
template<> struct ConvertionAllowedVia<int8_t, int32_t> {};

template<typename T>
static inline bool convertTo(const std::string &str, T &result)
{
    /* Check that conversion to that type is allowed.
     * If this fails, this means that this template was not intended to be used
     * with this type, thus that the result is undefined. */
    ConvertionAllowed<T>();

    if (str.find_first_of(std::string("\r\n\t\v ")) != std::string::npos) {
        return false;
    }

    /* Check for a '-' in string. If type is unsigned and a - is found, the
     * parsing fails. This is made necessary because "-1" is read as 65535 for
     * uint16_t, for example */
    if (str.find("-") != std::string::npos
        && !std::numeric_limits<T>::is_signed) {
        return false;
    }

    std::stringstream ss(str);

    /* Sadly, the stream conversion does not handle hexadecimal format, thus
     * check is done manually */
    if (str.substr(0, 2) == "0x") {
        if (std::numeric_limits<T>::is_integer) {
            ss >> std::hex >> result;
        }
        else {
            /* Conversion undefined for non integers */
            return false;
        }
    } else {
        ss >> result;
    }

    return ss.eof() && !ss.fail() && !ss.bad();
}

template<typename T, typename Via>
static inline bool convertToVia(const std::string &str, T &result)
{
    /* Check that conversion to that type is allowed.
     * If this fails, this means that this template was not intended to be used
     * with this type, thus that the result is undefined. */
    ConvertionAllowedVia<T, Via>();

    /* We want to override the behaviour of convertTo<T> with that of
     * convertTo<Via> and then safely cast the result into a T. */
    Via res;

    if (!convertTo<Via>(str, res)) {
        return false;
    }

    if ((res > std::numeric_limits<T>::max())
        or (res < std::numeric_limits<T>::min())) {
        return false;
    }

    result = static_cast<T>(res);
    return true;
}
} // namespace details

/**
 * Convert a string to a given type.
 *
 * This template function read the value of the type T in the given string.
 * The function does not allow to have white spaces around the value to parse
 * and tries to parse the whole string, which means that if some bytes were not
 * read in the string, the function fails.
 * Hexadecimal representation (ie numbers starting with 0x) is supported only
 * for integral types conversions.
 * Result may be modified, even in case of failure.
 *
 * @param[in]  str    the string to parse.
 * @param[out] result reference to object where to store the result.
 *
 * @return true if conversion was successful, false otherwise.
 */
template<typename T>
static inline bool convertTo(const std::string &str, T &result)
{
    return details::convertTo<T>(str, result);
}

/** Specialization for uint8_t of convertTo template function.
 *
 * This function follows the same paradigm than it's generic version.
 *
 * The generic version was converting int8 as it was a character
 * (uint8_t is an alias to unsigned char on most compiler).
 * Thus converting "1" would return 49 ie '1'.
 * As convertTo is thought as an _numerical_ convertion tool
 * (contrary to boost::lexical_cast for example),
 * forbid considering the input as a character and consider uint8_t
 * (aka unsigned char) as a number exclusively.
 *
 * @param[in]  str    the string to parse.
 * @param[out] result reference to object where to store the result.
 *
 * @return true if conversion was successful, false otherwise.
 */
template<>
inline bool convertTo<uint8_t>(const std::string &str, uint8_t &result)
{
    return details::convertToVia<uint8_t, uint32_t>(str, result);
}

/** Specialization for int8_t of convertTo template function.
 *
 * @see convertTo<uint8_t>
 */
template<>
inline bool convertTo<int8_t>(const std::string &str, int8_t &result)
{
    return details::convertToVia<int8_t, int32_t>(str, result);
}

/**
 * Specialization for float of convertTo template function.
 *
 * This function follows the same paradigm than it's generic version and is
 * based on it but makes furthers checks on the returned value.
 *
 * The specific implementation is made necessary because the stlport conversion
 * from string to float behaves differently than GNU STL: overflow produce
 * +/-Infinity rather than an error.
 *
 * @param[in]  str    the string to parse.
 * @param[out] result reference to object where to store the result.
 *
 * @return true if conversion was successful, false otherwise.
 */
template<>
inline bool convertTo<float>(const std::string &str, float &result)
{
    if (!details::convertTo(str, result)) {
        return false;
    }

    if (!std::isfinite(result)) {
        return false;
    }

    return true;
}

/**
 * Specialization for double of convertTo template function.
 *
 * This function follows the same paradigm than it's generic version and is
 * based on it but makes furthers checks on the returned value.
 *
 * The specific implementation is made necessary because the stlport conversion
 * from string to double behaves differently than GNU STL: overflow produce
 * +/-Infinity rather than an error.
 *
 * @param[in]  str    the string to parse.
 * @param[out] result reference to object where to store the result.
 *
 * @return true if conversion was successful, false otherwise.
 */
template<>
inline bool convertTo<double>(const std::string &str, double &result)
{
    if (!details::convertTo(str, result)) {
        return false;
    }

    if (!std::isfinite(result)) {
        return false;
    }

    return true;
}

/**
 * Specialization for boolean of convertTo template function.
 *
 * This function follows the same paradigm than it's generic version.
 * This function accepts to parse boolean as "0/1" or "false/true" or
 * "FALSE/TRUE".
 * The specific implementation is made necessary because the behaviour of
 * string streams when parsing boolean values is not sufficient to fit our
 * requirements. Indeed, parsing "true" will correctly parse the value, but the
 * end of stream is not reached which makes the ss.eof() fails in the generic
 * implementation.
 *
 * @param[in]  str    the string to parse.
 * @param[out] result reference to object where to store the result.
 *
 * @return true if conversion was successful, false otherwise.
 */
template<>
inline bool convertTo<bool>(const std::string &str, bool &result)
{
    if (str == "0" || str == "FALSE" || str == "false") {
        result = false;
        return true;
    }

    if (str == "1" || str == "TRUE" || str == "true") {
        result = true;
        return true;
    }

    return false;
}
<|endoftext|>


<commit_before>
#pragma once

#include "numerics/frequency_analysis.hpp"

#include <algorithm>
#include <functional>
#include <vector>

#include "base/tags.hpp"
#include "geometry/grassmann.hpp"
#include "geometry/hilbert.hpp"
#include "numerics/poisson_series_basis.hpp"
#include "numerics/root_finders.hpp"
#include "numerics/unbounded_arrays.hpp"
#include "quantities/elementary_functions.hpp"

namespace principia {
namespace numerics {
namespace frequency_analysis {
namespace internal_frequency_analysis {

using base::uninitialized;
using geometry::Hilbert;
using geometry::Vector;
using quantities::Inverse;
using quantities::IsFinite;
using quantities::Sqrt;
using quantities::Square;
using quantities::SquareRoot;

template<typename Function,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
AngularFrequency PreciseMode(
    Interval<AngularFrequency> const& fft_mode,
    Function const& function,
    PoissonSeries<double,
                  aperiodic_wdegree, periodic_wdegree,
                  Evaluator> const& weight) {
  auto const weighted_function = weight * function;
  auto const weighted_function_spectrum = weighted_function.FourierTransform();

  auto power =
      [&weighted_function_spectrum](AngularFrequency const& ω) {
        return weighted_function_spectrum(ω).Norm²();
      };

  return Brent(power,
               fft_mode.min,
               fft_mode.max,
               std::greater<>());
}

template<int aperiodic_degree, int periodic_degree,
         typename Function,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
PoissonSeries<std::invoke_result_t<Function, Instant>,
              aperiodic_degree, periodic_degree,
              Evaluator>
Projection(Function const& function,
           AngularFrequency const& ω,
           PoissonSeries<double,
                         aperiodic_wdegree, periodic_wdegree,
                         Evaluator> const& weight,
           Instant const& t_min,
           Instant const& t_max) {
  std::optional<AngularFrequency> optional_ω = ω;

  // A calculator that returns optional_ω once and then stops.
  auto angular_frequency_calculator = [&optional_ω](auto const& residual) {
    auto const result = optional_ω;
    optional_ω = std::nullopt;
    return result;
  };

  return IncrementalProjection<aperiodic_degree, periodic_degree>(
      function,
      angular_frequency_calculator,
      weight,
      t_min, t_max);
}

#define DO_THE_LOGGING 0
#define USE_CGS 1
#define USE_INTEGRATE 0

template<int aperiodic_degree, int periodic_degree,
         typename Function,
         typename AngularFrequencyCalculator,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
PoissonSeries<std::invoke_result_t<Function, Instant>,
              aperiodic_degree, periodic_degree,
              Evaluator>
IncrementalProjection(Function const& function,
                      AngularFrequencyCalculator const& calculator,
                      PoissonSeries<double,
                                    aperiodic_wdegree, periodic_wdegree,
                                    Evaluator> const& weight,
                      Instant const& t_min,
                      Instant const& t_max) {
  using Value = std::invoke_result_t<Function, Instant>;
  using Norm = typename Hilbert<Value>::NormType;
  using Normalized = typename Hilbert<Value>::NormalizedType;
  using Series = PoissonSeries<Value,
                               aperiodic_degree, periodic_degree,
                               Evaluator>;

  // This code follows [Kud07], section 2.  Our indices start at 0, unlike those
  // of Кудрявцев which start at 1.

  Instant const& t0 = weight.origin();

  std::optional<AngularFrequency> ω = calculator(function);
  CHECK(ω.has_value());

  std::vector<Series> basis;
  // The Poisson series basis[k] belongs to the subspace basis_subspaces[k];
  // this remains true after orthonormalization, i.e., q[k] belongs to the
  // subspace basis_subspaces[k] below.
  std::vector<PoissonSeriesSubspace> basis_subspaces;

  int basis_size;
  // TODO(phl): This is replicated below.
  if (ω.value() == AngularFrequency{}) {
    auto const ω_basis =
        PoissonSeriesBasisGenerator<Series,
                                    aperiodic_degree>::Basis(t0);
    auto const ω_basis_subspaces =
        PoissonSeriesBasisGenerator<Series, aperiodic_degree>::Subspaces(t0);
    basis_size = std::tuple_size_v<decltype(ω_basis)>;
    std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
    std::move(ω_basis_subspaces.begin(),
              ω_basis_subspaces.end(),
              std::back_inserter(basis_subspaces));
  } else {
    auto const ω_basis =
        PoissonSeriesBasisGenerator<Series,
                                    periodic_degree>::Basis(ω.value(), t0);
    auto const ω_basis_subspaces =
        PoissonSeriesBasisGenerator<Series, periodic_degree>::Subspaces(
            ω.value(), t0);
    basis_size = std::tuple_size_v<decltype(ω_basis)>;
    std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
    std::move(ω_basis_subspaces.begin(),
              ω_basis_subspaces.end(),
              std::back_inserter(basis_subspaces));
  }

  // This is logically Q in the QR decomposition of basis.
  std::vector<PoissonSeries<Normalized,
                            aperiodic_degree, periodic_degree,
                            Evaluator>> q;

  auto const& a₀ = basis[0];
  auto const r₀₀ = a₀.Norm(weight, t_min, t_max);
  CHECK(IsFinite(r₀₀)) << a₀;
  q.push_back(a₀ / r₀₀);

  auto const A₀ = InnerProduct(function, q[0], weight, t_min, t_max);

  Series F = A₀ * q[0];
  auto f = function - F;

  int m_begin = 1;
  for (;;) {
    for (int m = m_begin; m < basis_size; ++m) {
      auto const& aₘ = basis[m];

      // k -> m
      std::optional<Series> previous_q̂ₘ;
      Series q̂ₘ = aₘ;
      std::vector<Norm> rₘ(m);
      // Loop on p.
      do {
        std::vector<Norm> sᵖₘ;
        for (int i = 0; i < m; ++i) {
          sᵖₘ.push_back(InnerProduct(q[i], q̂ₘ, weight, t_min, t_max));
        }
        previous_q̂ₘ = q̂ₘ;
        for (int i = 0; i < m; ++i) {
          q̂ₘ -= sᵖₘ[i] * q[i];
        }
        for (int i = 0; i < m; ++i) {
          rₘ[i] += sᵖₘ[i];
        }
        //LOG(ERROR) << "m: " << m << " q̂ₘ: " << q̂ₘ.Norm(weight, t_min, t_max)
        //           << " previous q̂ₘ: "
        //           << previous_q̂ₘ.value().Norm(
        //                  weight, t_min, t_max);
      } while (q̂ₘ.Norm(weight, t_min, t_max) <
               0.5 * previous_q̂ₘ.value().Norm(weight, t_min, t_max));
      auto const rₘₘ = q̂ₘ.Norm(weight, t_min, t_max);
      q.push_back(q̂ₘ / rₘₘ);

#if 0
      auto const r₀ₘ = InnerProduct(q[0], aₘ, weight, t_min, t_max);
      Series Σrᵢₘqᵢ = r₀ₘ * q[0];
      for (int i = 1; i < m; ++i) {
        auto const rᵢₘ = InnerProduct(q[i], aₘ, weight, t_min, t_max);
        Σrᵢₘqᵢ += rᵢₘ * q[i];
      }

      auto const qʹₘ = aₘ - Σrᵢₘqᵢ;
      auto rₘₘ = qʹₘ.Norm(weight, t_min, t_max);
      q.push_back(qʹₘ / rₘₘ);
#endif
      DCHECK_EQ(m + 1, q.size());

      Norm const Aₘ = InnerProduct(f, q[m], weight, t_min, t_max);

      auto const Aₘqₘ = Aₘ * q[m];
      f -= Aₘqₘ;
      F += Aₘqₘ;
    }

    ω = calculator(f);
    if (!ω.has_value()) {
      return F;
    }

    int ω_basis_size;
    if (ω.value() == AngularFrequency{}) {
      auto const ω_basis =
          PoissonSeriesBasisGenerator<Series,
                                      aperiodic_degree>::Basis(t0);
      auto const ω_basis_subspaces =
          PoissonSeriesBasisGenerator<Series, aperiodic_degree>::Subspaces(t0);
      ω_basis_size = std::tuple_size_v<decltype(ω_basis)>;
      std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
      std::move(ω_basis_subspaces.begin(),
                ω_basis_subspaces.end(),
                std::back_inserter(basis_subspaces));
    } else {
      auto const ω_basis =
          PoissonSeriesBasisGenerator<Series,
                                      periodic_degree>::Basis(ω.value(), t0);
      auto const ω_basis_subspaces =
          PoissonSeriesBasisGenerator<Series, periodic_degree>::Subspaces(
              ω.value(), t0);
      ω_basis_size = std::tuple_size_v<decltype(ω_basis)>;
      std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
      std::move(ω_basis_subspaces.begin(),
                ω_basis_subspaces.end(),
                std::back_inserter(basis_subspaces));
    }
    m_begin = basis_size;
    basis_size += ω_basis_size;
  }
}

}  // namespace internal_frequency_analysis
}  // namespace frequency_analysis
}  // namespace numerics
}  // namespace principia
<commit_msg>Some code simplification.<commit_after>
#pragma once

#include "numerics/frequency_analysis.hpp"

#include <algorithm>
#include <functional>
#include <vector>

#include "base/tags.hpp"
#include "geometry/grassmann.hpp"
#include "geometry/hilbert.hpp"
#include "numerics/poisson_series_basis.hpp"
#include "numerics/root_finders.hpp"
#include "numerics/unbounded_arrays.hpp"
#include "quantities/elementary_functions.hpp"

namespace principia {
namespace numerics {
namespace frequency_analysis {
namespace internal_frequency_analysis {

using base::uninitialized;
using geometry::Hilbert;
using geometry::Vector;
using quantities::Inverse;
using quantities::IsFinite;
using quantities::Sqrt;
using quantities::Square;
using quantities::SquareRoot;

template<typename Function,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
AngularFrequency PreciseMode(
    Interval<AngularFrequency> const& fft_mode,
    Function const& function,
    PoissonSeries<double,
                  aperiodic_wdegree, periodic_wdegree,
                  Evaluator> const& weight) {
  auto const weighted_function = weight * function;
  auto const weighted_function_spectrum = weighted_function.FourierTransform();

  auto power =
      [&weighted_function_spectrum](AngularFrequency const& ω) {
        return weighted_function_spectrum(ω).Norm²();
      };

  return Brent(power,
               fft_mode.min,
               fft_mode.max,
               std::greater<>());
}

template<int aperiodic_degree, int periodic_degree,
         typename Function,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
PoissonSeries<std::invoke_result_t<Function, Instant>,
              aperiodic_degree, periodic_degree,
              Evaluator>
Projection(Function const& function,
           AngularFrequency const& ω,
           PoissonSeries<double,
                         aperiodic_wdegree, periodic_wdegree,
                         Evaluator> const& weight,
           Instant const& t_min,
           Instant const& t_max) {
  std::optional<AngularFrequency> optional_ω = ω;

  // A calculator that returns optional_ω once and then stops.
  auto angular_frequency_calculator = [&optional_ω](auto const& residual) {
    auto const result = optional_ω;
    optional_ω = std::nullopt;
    return result;
  };

  return IncrementalProjection<aperiodic_degree, periodic_degree>(
      function,
      angular_frequency_calculator,
      weight,
      t_min, t_max);
}

#define DO_THE_LOGGING 0
#define USE_CGS 1
#define USE_INTEGRATE 0

template<int aperiodic_degree, int periodic_degree,
         typename Function,
         typename AngularFrequencyCalculator,
         int aperiodic_wdegree, int periodic_wdegree,
         template<typename, typename, int> class Evaluator>
PoissonSeries<std::invoke_result_t<Function, Instant>,
              aperiodic_degree, periodic_degree,
              Evaluator>
IncrementalProjection(Function const& function,
                      AngularFrequencyCalculator const& calculator,
                      PoissonSeries<double,
                                    aperiodic_wdegree, periodic_wdegree,
                                    Evaluator> const& weight,
                      Instant const& t_min,
                      Instant const& t_max) {
  using Value = std::invoke_result_t<Function, Instant>;
  using Norm = typename Hilbert<Value>::NormType;
  using Normalized = typename Hilbert<Value>::NormalizedType;
  using Series = PoissonSeries<Value,
                               aperiodic_degree, periodic_degree,
                               Evaluator>;

  // This code follows [Kud07], section 2.  Our indices start at 0, unlike those
  // of Кудрявцев which start at 1.

  Instant const& t0 = weight.origin();

  std::optional<AngularFrequency> ω = calculator(function);
  CHECK(ω.has_value());

  std::vector<Series> basis;
  // The Poisson series basis[k] belongs to the subspace basis_subspaces[k];
  // this remains true after orthonormalization, i.e., q[k] belongs to the
  // subspace basis_subspaces[k] below.
  std::vector<PoissonSeriesSubspace> basis_subspaces;

  int basis_size;
  // TODO(phl): This is replicated below.
  if (ω.value() == AngularFrequency{}) {
    auto const ω_basis =
        PoissonSeriesBasisGenerator<Series,
                                    aperiodic_degree>::Basis(t0);
    auto const ω_basis_subspaces =
        PoissonSeriesBasisGenerator<Series, aperiodic_degree>::Subspaces(t0);
    basis_size = std::tuple_size_v<decltype(ω_basis)>;
    std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
    std::move(ω_basis_subspaces.begin(),
              ω_basis_subspaces.end(),
              std::back_inserter(basis_subspaces));
  } else {
    auto const ω_basis =
        PoissonSeriesBasisGenerator<Series,
                                    periodic_degree>::Basis(ω.value(), t0);
    auto const ω_basis_subspaces =
        PoissonSeriesBasisGenerator<Series, periodic_degree>::Subspaces(
            ω.value(), t0);
    basis_size = std::tuple_size_v<decltype(ω_basis)>;
    std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
    std::move(ω_basis_subspaces.begin(),
              ω_basis_subspaces.end(),
              std::back_inserter(basis_subspaces));
  }

  // This is logically Q in the QR decomposition of basis.
  std::vector<PoissonSeries<Normalized,
                            aperiodic_degree, periodic_degree,
                            Evaluator>> q;

  auto const& a₀ = basis[0];
  auto const r₀₀ = a₀.Norm(weight, t_min, t_max);
  CHECK(IsFinite(r₀₀)) << a₀;
  q.push_back(a₀ / r₀₀);

  auto const A₀ = InnerProduct(function, q[0], weight, t_min, t_max);

  Series F = A₀ * q[0];
  auto f = function - F;

  int m_begin = 1;
  for (;;) {
    for (int m = m_begin; m < basis_size; ++m) {
      auto const& aₘ = basis[m];

      // k -> m
      Series q̂ₘ = aₘ;
      Series previous_q̂ₘ = q̂ₘ;
      std::vector<Norm> rₘ(m);
      // Loop on p.
      do {
        previous_q̂ₘ = q̂ₘ;
        std::vector<Norm> sᵖₘ;
        for (int i = 0; i < m; ++i) {
          if (!PoissonSeriesSubspace::orthogonal(basis_subspaces[i],
                                                 basis_subspaces[m])) {
            auto const sᵖₘ =
                InnerProduct(q[i], previous_q̂ₘ, weight, t_min, t_max);
            q̂ₘ -= sᵖₘ * q[i];
          }
        }
        //LOG(ERROR) << "m: " << m << " q̂ₘ: " << q̂ₘ.Norm(weight, t_min, t_max)
        //           << " previous q̂ₘ: "
        //           << previous_q̂ₘ.value().Norm(
        //                  weight, t_min, t_max);
      } while (q̂ₘ.Norm(weight, t_min, t_max) <
               0.5 * previous_q̂ₘ.Norm(weight, t_min, t_max));
      auto const rₘₘ = q̂ₘ.Norm(weight, t_min, t_max);
      q.push_back(q̂ₘ / rₘₘ);

#if 0
      auto const r₀ₘ = InnerProduct(q[0], aₘ, weight, t_min, t_max);
      Series Σrᵢₘqᵢ = r₀ₘ * q[0];
      for (int i = 1; i < m; ++i) {
        auto const rᵢₘ = InnerProduct(q[i], aₘ, weight, t_min, t_max);
        Σrᵢₘqᵢ += rᵢₘ * q[i];
      }

      auto const qʹₘ = aₘ - Σrᵢₘqᵢ;
      auto rₘₘ = qʹₘ.Norm(weight, t_min, t_max);
      q.push_back(qʹₘ / rₘₘ);
#endif
      DCHECK_EQ(m + 1, q.size());

      Norm const Aₘ = InnerProduct(f, q[m], weight, t_min, t_max);

      auto const Aₘqₘ = Aₘ * q[m];
      f -= Aₘqₘ;
      F += Aₘqₘ;
    }

    ω = calculator(f);
    if (!ω.has_value()) {
      return F;
    }

    int ω_basis_size;
    if (ω.value() == AngularFrequency{}) {
      auto const ω_basis =
          PoissonSeriesBasisGenerator<Series,
                                      aperiodic_degree>::Basis(t0);
      auto const ω_basis_subspaces =
          PoissonSeriesBasisGenerator<Series, aperiodic_degree>::Subspaces(t0);
      ω_basis_size = std::tuple_size_v<decltype(ω_basis)>;
      std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
      std::move(ω_basis_subspaces.begin(),
                ω_basis_subspaces.end(),
                std::back_inserter(basis_subspaces));
    } else {
      auto const ω_basis =
          PoissonSeriesBasisGenerator<Series,
                                      periodic_degree>::Basis(ω.value(), t0);
      auto const ω_basis_subspaces =
          PoissonSeriesBasisGenerator<Series, periodic_degree>::Subspaces(
              ω.value(), t0);
      ω_basis_size = std::tuple_size_v<decltype(ω_basis)>;
      std::move(ω_basis.begin(), ω_basis.end(), std::back_inserter(basis));
      std::move(ω_basis_subspaces.begin(),
                ω_basis_subspaces.end(),
                std::back_inserter(basis_subspaces));
    }
    m_begin = basis_size;
    basis_size += ω_basis_size;
  }
}

}  // namespace internal_frequency_analysis
}  // namespace frequency_analysis
}  // namespace numerics
}  // namespace principia
<|endoftext|>