<commit_before>/******************************************************************************
 * This file is part of the Mula project
 * Copyright (c) 2011 Laszlo Papp <lpapp@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) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "offsetcachefile.h"

#include "file.h"
#include "wordentry.h"

#include <QtCore/QVector>
#include <QtCore/QFile>
#include <QtCore/QtGlobal>
#include <QtCore/QDir>
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QPair>
#include <QtGui/QDesktopServices>

#include <arpa/inet.h>

using namespace MulaPluginStarDict;

class OffsetCacheFile::Private
{
    public:
        Private()
            : wordCount(0)
            , pageIndex(-1)
            , cacheMagicString("StarDict's Cache, Version: 0.1")
            , mappedData(0)
        {
        }

        ~Private()
        {
        }

        // The length of "word_str" should be less than 256, and then offset, size
        static const int wordEntrySize = 256 + sizeof(quint32)*2;

        static const int pageEntryNumber = 32;

        QVector<quint32> pageOffsetList;
        QFile indexFile;
        ulong wordCount;

        // index/date based key and value pair
        QPair<int, QByteArray> first;
        QPair<int, QByteArray> last;
        QPair<int, QByteArray> middle;
        QPair<int, QByteArray> realLast;

        long pageIndex;
        QList<WordEntry> wordEntryList;

        QByteArray cacheMagicString;
        QFile mapFile;
        uchar *mappedData;
};

OffsetCacheFile::OffsetCacheFile()
    : d(new Private)
{
}

OffsetCacheFile::~OffsetCacheFile()
{
}

QByteArray
OffsetCacheFile::readFirstWordDataOnPage(long pageIndex)
{
    int pageSize = d->pageOffsetList.at(pageIndex + 1) - d->pageOffsetList.at(pageIndex);
    int wordEntrySize = d->wordEntrySize;

    d->indexFile.seek(d->pageOffsetList.at(pageIndex));
    return d->indexFile.read(qMin(wordEntrySize, pageSize)); //TODO: check returned values, deal with word entry that strlen>255.
}

QByteArray
OffsetCacheFile::firstWordDataOnPage(long pageIndex)
{
    if (pageIndex < d->middle.first)
    {
        if (pageIndex == d->first.first)
            return d->first.second;

        return readFirstWordDataOnPage(pageIndex);
    }
    else if (pageIndex > d->middle.first)
    {
        if (pageIndex == d->last.first)
            return d->last.second;

        return readFirstWordDataOnPage(pageIndex);
    }
    else
    {
        return d->middle.second;
    }
}

QStringList
OffsetCacheFile::cacheLocations(const QString& completeFilePath)
{
    QStringList result;
    result.append(completeFilePath + ".oft");

    QString cacheLocation = QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
    QFileInfo cacheLocationFileInfo(cacheLocation);
    QDir cacheLocationDir;

    if (!cacheLocationFileInfo.exists() && cacheLocationDir.mkdir(cacheLocation) == false)
        return result;

    if (!cacheLocationFileInfo.isDir())
        return result;

    result.append(cacheLocation + QDir::separator() + "sdcv" + QDir::separator() + QFileInfo(completeFilePath).fileName() + ".oft");
    return result;
}

bool
OffsetCacheFile::loadCache(const QString& completeFilePath)
{
    foreach (const QString& cacheLocation, cacheLocations(completeFilePath))
    {
        QFileInfo fileInfoIndex(completeFilePath);
        QFileInfo fileInfoCache(cacheLocation);

        if (fileInfoCache.lastModified() < fileInfoIndex.lastModified())
            continue;

        d->mapFile.unmap(d->mappedData);
        d->mapFile.setFileName(cacheLocation);
        if( !d->mapFile.open( QIODevice::ReadOnly ) )
        {
            qDebug() << "Failed to open file:" << cacheLocation;
            return false;
        }

        d->mappedData = d->mapFile.map(0, d->mapFile.size());
        if (d->mappedData == NULL)
        {
            qDebug() << Q_FUNC_INFO << QString("Mapping the file %1 failed!").arg(cacheLocation);
            return false;
        }

        if (d->cacheMagicString != d->mappedData)
            continue;

        memcpy(d->pageOffsetList.data(), d->mappedData + d->cacheMagicString.size(), d->pageOffsetList.size()*sizeof(d->pageOffsetList.at(0)));
        return true;
    }

    return false;
}

bool
OffsetCacheFile::saveCache(const QString& completeFilePath)
{
    foreach (const QString& cacheLocation, cacheLocations(completeFilePath))
    {
        QFile file(cacheLocation);
        if( !file.open( QIODevice::WriteOnly ) )
        {
            qDebug() << "Failed to open file for writing:" << cacheLocation;
            return false;
        }

        d->mapFile.unmap(d->mappedData);
        d->mapFile.setFileName(cacheLocation);
        if( !d->mapFile.open( QIODevice::ReadOnly ) )
        {
            qDebug() << "Failed to open file:" << cacheLocation;
            return false;
        }

        d->mappedData = d->mapFile.map(0, d->mapFile.size());
        if (d->mappedData == NULL)
        {
            qDebug() << Q_FUNC_INFO << QString("Mapping the file %1 failed!").arg(cacheLocation);
            return false;
        }

        if (file.write(d->cacheMagicString) != d->cacheMagicString.size())
            continue;

        if (file.write(reinterpret_cast<const char*>(d->pageOffsetList.data()), sizeof(d->pageOffsetList.at(0))*d->pageOffsetList.size())
                != d->pageOffsetList.at(0)*d->pageOffsetList.size())
            continue;

        file.close();

        qDebug() << "Save to cache" << completeFilePath;

        return true;
    }

    return false;
}

int
OffsetCacheFile::loadPage(int pageIndex)
{
    int wordEntryCountOnPage;

    if (pageIndex == (d->pageOffsetList.size() - 2) && (d->wordCount % d->pageEntryNumber) != 0)
        wordEntryCountOnPage = d->wordCount % d->pageEntryNumber;
    else
        wordEntryCountOnPage = d->pageEntryNumber;

    if (pageIndex != d->pageIndex)
    {
        d->pageIndex = pageIndex;
        d->indexFile.seek(d->pageOffsetList.at(pageIndex));
        QByteArray pageData = d->indexFile.read(d->pageOffsetList.at(pageIndex + 1) - d->pageOffsetList.at(pageIndex));

        ulong position = 0;
        d->wordEntryList.clear();
        d->wordEntryList.reserve(wordEntryCountOnPage);
        for (int i = 0; i < wordEntryCountOnPage; ++i)
        {
            WordEntry wordEntry;
            wordEntry.setData(pageData.mid(position));
            position = qstrlen(pageData.mid(position)) + 1;
            wordEntry.setDataOffset(ntohl(*reinterpret_cast<quint32 *>(pageData.mid(position).data())));
            position += sizeof(quint32);
            wordEntry.setDataSize(ntohl(*reinterpret_cast<quint32 *>(pageData.mid(position).data())));
            position += sizeof(quint32);

            d->wordEntryList.append(wordEntry);
        }
    }

    return wordEntryCountOnPage;
}

QByteArray
OffsetCacheFile::key(long index)
{
    loadPage(index / d->pageEntryNumber);
    ulong indexInPage = index % d->pageEntryNumber;
    setWordEntryOffset(d->wordEntryList.at(indexInPage).dataOffset());
    setWordEntrySize(d->wordEntryList.at(indexInPage).dataSize());

    return d->wordEntryList.at(indexInPage).data();
}

bool
OffsetCacheFile::load(const QString& completeFilePath, qulonglong fileSize, int wordCount)
{
    d->wordCount = wordCount;

    if (!loadCache(completeFilePath))
    { //map file will close after finish of block
        d->mapFile.unmap(d->mappedData);
        d->mapFile.setFileName(completeFilePath);
        if (!d->mapFile.open(QIODevice::ReadOnly))
        {
            qDebug() << "Failed to open file:" << completeFilePath;
            return -1;
        }

        d->mappedData = d->mapFile.map(0, d->mapFile.size());
        if (d->mappedData == NULL)
        {
            qDebug() << Q_FUNC_INFO << QString("Mapping the file %1 failed!").arg(completeFilePath);
            return false;
        }

        QByteArray byteArray = QByteArray::fromRawData(reinterpret_cast<const char*>(d->mappedData), fileSize);

        int position = 0;
        d->pageOffsetList.clear();
        int wordTerminatorOffsetSizeLength = 1 + 2 * sizeof(quint32);
        for (int i = 0; i < wordCount; ++i)
        {
            if (i % d->pageEntryNumber == 0)
                d->pageOffsetList.append(position);

            position += qstrlen(byteArray.mid(position)) + wordTerminatorOffsetSizeLength;
        }

        d->pageOffsetList.append(position);

        if (!saveCache(completeFilePath))
            qDebug() << "Cache update failed";
    }

    d->indexFile.setFileName(completeFilePath);
    if (!d->indexFile.open(QIODevice::ReadOnly))
    {
        qDebug() << "Failed to open file:" << completeFilePath;
        d->pageOffsetList.clear();
        return false;
    }

    d->first = qMakePair(0, readFirstWordDataOnPage(0));
    d->last = qMakePair(d->pageOffsetList.size() - 2, readFirstWordDataOnPage(d->pageOffsetList.size() - 2));
    d->middle = qMakePair((d->pageOffsetList.size() - 2) / 2, readFirstWordDataOnPage((d->pageOffsetList.size() - 2) / 2));
    d->realLast = qMakePair(wordCount - 1, key(wordCount - 1));

    return true;
}

bool
OffsetCacheFile::lookupPage(const QByteArray& word, int& pageIndex)
{
    bool found = false;

    if (stardictStringCompare(word, d->first.second) < 0)
    {
        pageIndex = invalidIndex;
        return false;
    }
    else if (stardictStringCompare(word, d->realLast.second) > 0)
    {
        pageIndex = invalidIndex;
        return false;
    }
    else
    {
        int indexTo = d->pageOffsetList.size() - 2;
        int indexFrom = 0;
        int indexThisIndex = 0;
        int cmpint;
        while (indexFrom <= indexTo)
        {
            indexThisIndex = (indexFrom + indexTo) / 2;
            cmpint = stardictStringCompare(word, firstWordDataOnPage(indexThisIndex));
            if (cmpint > 0)
                indexFrom = indexThisIndex + 1;
            else if (cmpint < 0)
                indexTo = indexThisIndex - 1;
            else
            {
                found = true;
                break;
            }
        }

        if (!found)
            pageIndex = indexTo;    //prev
        else
            pageIndex = indexThisIndex;
    }

    return found;
}

bool
OffsetCacheFile::lookup(const QByteArray& word, int &index)
{
    bool found = false;
    int indexFrom;
    int cmpint;
    int indexThisIndex;

    int pageIndex;

    found = lookupPage(word, pageIndex);

    if (!found)
    {
        ulong netr = loadPage(index);
        indexFrom = 1; // Needn't search the first word anymore.
        int indexTo = netr - 1;
        indexThisIndex = 0;
        while (indexFrom <= indexTo)
        {
            indexThisIndex = (indexFrom + indexTo) / 2;
            cmpint = stardictStringCompare(word, d->wordEntryList.at(indexThisIndex).data());
            if (cmpint > 0)
                indexFrom = indexThisIndex + 1;
            else if (cmpint < 0)
                indexTo = indexThisIndex - 1;
            else
            {
                found = true;
                break;
            }
        }

        index *= d->pageEntryNumber;
        if (!found)
            index += indexFrom;    //next
        else
            index += indexThisIndex;
    }
    else
    {
        index *= d->pageEntryNumber;
    }

    return found;
}

quint32
OffsetCacheFile::wordEntryOffset() const
{
    return AbstractIndexFile::wordEntryOffset();
}

void
OffsetCacheFile::setWordEntryOffset(quint32 wordEntryOffset)
{
    AbstractIndexFile::setWordEntryOffset(wordEntryOffset);
}

quint32
OffsetCacheFile::wordEntrySize() const
{
    return AbstractIndexFile::wordEntrySize();
}

void
OffsetCacheFile::setWordEntrySize(quint32 wordEntrySize)
{
    AbstractIndexFile::setWordEntrySize(wordEntrySize);
}
<commit_msg>Just use one index instead of mixing up the situation by using two separate ones<commit_after>/******************************************************************************
 * This file is part of the Mula project
 * Copyright (c) 2011 Laszlo Papp <lpapp@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) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "offsetcachefile.h"

#include "file.h"
#include "wordentry.h"

#include <QtCore/QVector>
#include <QtCore/QFile>
#include <QtCore/QtGlobal>
#include <QtCore/QDir>
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QPair>
#include <QtGui/QDesktopServices>

#include <arpa/inet.h>

using namespace MulaPluginStarDict;

class OffsetCacheFile::Private
{
    public:
        Private()
            : wordCount(0)
            , pageIndex(-1)
            , cacheMagicString("StarDict's Cache, Version: 0.1")
            , mappedData(0)
        {
        }

        ~Private()
        {
        }

        // The length of "word_str" should be less than 256, and then offset, size
        static const int wordEntrySize = 256 + sizeof(quint32)*2;

        static const int pageEntryNumber = 32;

        QVector<quint32> pageOffsetList;
        QFile indexFile;
        ulong wordCount;

        // index/date based key and value pair
        QPair<int, QByteArray> first;
        QPair<int, QByteArray> last;
        QPair<int, QByteArray> middle;
        QPair<int, QByteArray> realLast;

        long pageIndex;
        QList<WordEntry> wordEntryList;

        QByteArray cacheMagicString;
        QFile mapFile;
        uchar *mappedData;
};

OffsetCacheFile::OffsetCacheFile()
    : d(new Private)
{
}

OffsetCacheFile::~OffsetCacheFile()
{
}

QByteArray
OffsetCacheFile::readFirstWordDataOnPage(long pageIndex)
{
    int pageSize = d->pageOffsetList.at(pageIndex + 1) - d->pageOffsetList.at(pageIndex);
    int wordEntrySize = d->wordEntrySize;

    d->indexFile.seek(d->pageOffsetList.at(pageIndex));
    return d->indexFile.read(qMin(wordEntrySize, pageSize)); //TODO: check returned values, deal with word entry that strlen>255.
}

QByteArray
OffsetCacheFile::firstWordDataOnPage(long pageIndex)
{
    if (pageIndex < d->middle.first)
    {
        if (pageIndex == d->first.first)
            return d->first.second;

        return readFirstWordDataOnPage(pageIndex);
    }
    else if (pageIndex > d->middle.first)
    {
        if (pageIndex == d->last.first)
            return d->last.second;

        return readFirstWordDataOnPage(pageIndex);
    }
    else
    {
        return d->middle.second;
    }
}

QStringList
OffsetCacheFile::cacheLocations(const QString& completeFilePath)
{
    QStringList result;
    result.append(completeFilePath + ".oft");

    QString cacheLocation = QDesktopServices::storageLocation(QDesktopServices::CacheLocation);
    QFileInfo cacheLocationFileInfo(cacheLocation);
    QDir cacheLocationDir;

    if (!cacheLocationFileInfo.exists() && cacheLocationDir.mkdir(cacheLocation) == false)
        return result;

    if (!cacheLocationFileInfo.isDir())
        return result;

    result.append(cacheLocation + QDir::separator() + "sdcv" + QDir::separator() + QFileInfo(completeFilePath).fileName() + ".oft");
    return result;
}

bool
OffsetCacheFile::loadCache(const QString& completeFilePath)
{
    foreach (const QString& cacheLocation, cacheLocations(completeFilePath))
    {
        QFileInfo fileInfoIndex(completeFilePath);
        QFileInfo fileInfoCache(cacheLocation);

        if (fileInfoCache.lastModified() < fileInfoIndex.lastModified())
            continue;

        d->mapFile.unmap(d->mappedData);
        d->mapFile.setFileName(cacheLocation);
        if( !d->mapFile.open( QIODevice::ReadOnly ) )
        {
            qDebug() << "Failed to open file:" << cacheLocation;
            return false;
        }

        d->mappedData = d->mapFile.map(0, d->mapFile.size());
        if (d->mappedData == NULL)
        {
            qDebug() << Q_FUNC_INFO << QString("Mapping the file %1 failed!").arg(cacheLocation);
            return false;
        }

        if (d->cacheMagicString != d->mappedData)
            continue;

        memcpy(d->pageOffsetList.data(), d->mappedData + d->cacheMagicString.size(), d->pageOffsetList.size()*sizeof(d->pageOffsetList.at(0)));
        return true;
    }

    return false;
}

bool
OffsetCacheFile::saveCache(const QString& completeFilePath)
{
    foreach (const QString& cacheLocation, cacheLocations(completeFilePath))
    {
        QFile file(cacheLocation);
        if( !file.open( QIODevice::WriteOnly ) )
        {
            qDebug() << "Failed to open file for writing:" << cacheLocation;
            return false;
        }

        d->mapFile.unmap(d->mappedData);
        d->mapFile.setFileName(cacheLocation);
        if( !d->mapFile.open( QIODevice::ReadOnly ) )
        {
            qDebug() << "Failed to open file:" << cacheLocation;
            return false;
        }

        d->mappedData = d->mapFile.map(0, d->mapFile.size());
        if (d->mappedData == NULL)
        {
            qDebug() << Q_FUNC_INFO << QString("Mapping the file %1 failed!").arg(cacheLocation);
            return false;
        }

        if (file.write(d->cacheMagicString) != d->cacheMagicString.size())
            continue;

        if (file.write(reinterpret_cast<const char*>(d->pageOffsetList.data()), sizeof(d->pageOffsetList.at(0))*d->pageOffsetList.size())
                != d->pageOffsetList.at(0)*d->pageOffsetList.size())
            continue;

        file.close();

        qDebug() << "Save to cache" << completeFilePath;

        return true;
    }

    return false;
}

int
OffsetCacheFile::loadPage(int pageIndex)
{
    int wordEntryCountOnPage;

    if (pageIndex == (d->pageOffsetList.size() - 2) && (d->wordCount % d->pageEntryNumber) != 0)
        wordEntryCountOnPage = d->wordCount % d->pageEntryNumber;
    else
        wordEntryCountOnPage = d->pageEntryNumber;

    if (pageIndex != d->pageIndex)
    {
        d->pageIndex = pageIndex;
        d->indexFile.seek(d->pageOffsetList.at(pageIndex));
        QByteArray pageData = d->indexFile.read(d->pageOffsetList.at(pageIndex + 1) - d->pageOffsetList.at(pageIndex));

        ulong position = 0;
        d->wordEntryList.clear();
        d->wordEntryList.reserve(wordEntryCountOnPage);
        for (int i = 0; i < wordEntryCountOnPage; ++i)
        {
            WordEntry wordEntry;
            wordEntry.setData(pageData.mid(position));
            position = qstrlen(pageData.mid(position)) + 1;
            wordEntry.setDataOffset(ntohl(*reinterpret_cast<quint32 *>(pageData.mid(position).data())));
            position += sizeof(quint32);
            wordEntry.setDataSize(ntohl(*reinterpret_cast<quint32 *>(pageData.mid(position).data())));
            position += sizeof(quint32);

            d->wordEntryList.append(wordEntry);
        }
    }

    return wordEntryCountOnPage;
}

QByteArray
OffsetCacheFile::key(long index)
{
    loadPage(index / d->pageEntryNumber);
    ulong indexInPage = index % d->pageEntryNumber;
    setWordEntryOffset(d->wordEntryList.at(indexInPage).dataOffset());
    setWordEntrySize(d->wordEntryList.at(indexInPage).dataSize());

    return d->wordEntryList.at(indexInPage).data();
}

bool
OffsetCacheFile::load(const QString& completeFilePath, qulonglong fileSize, int wordCount)
{
    d->wordCount = wordCount;

    if (!loadCache(completeFilePath))
    { //map file will close after finish of block
        d->mapFile.unmap(d->mappedData);
        d->mapFile.setFileName(completeFilePath);
        if (!d->mapFile.open(QIODevice::ReadOnly))
        {
            qDebug() << "Failed to open file:" << completeFilePath;
            return -1;
        }

        d->mappedData = d->mapFile.map(0, d->mapFile.size());
        if (d->mappedData == NULL)
        {
            qDebug() << Q_FUNC_INFO << QString("Mapping the file %1 failed!").arg(completeFilePath);
            return false;
        }

        QByteArray byteArray = QByteArray::fromRawData(reinterpret_cast<const char*>(d->mappedData), fileSize);

        int position = 0;
        d->pageOffsetList.clear();
        int wordTerminatorOffsetSizeLength = 1 + 2 * sizeof(quint32);
        for (int i = 0; i < wordCount; ++i)
        {
            if (i % d->pageEntryNumber == 0)
                d->pageOffsetList.append(position);

            position += qstrlen(byteArray.mid(position)) + wordTerminatorOffsetSizeLength;
        }

        d->pageOffsetList.append(position);

        if (!saveCache(completeFilePath))
            qDebug() << "Cache update failed";
    }

    d->indexFile.setFileName(completeFilePath);
    if (!d->indexFile.open(QIODevice::ReadOnly))
    {
        qDebug() << "Failed to open file:" << completeFilePath;
        d->pageOffsetList.clear();
        return false;
    }

    d->first = qMakePair(0, readFirstWordDataOnPage(0));
    d->last = qMakePair(d->pageOffsetList.size() - 2, readFirstWordDataOnPage(d->pageOffsetList.size() - 2));
    d->middle = qMakePair((d->pageOffsetList.size() - 2) / 2, readFirstWordDataOnPage((d->pageOffsetList.size() - 2) / 2));
    d->realLast = qMakePair(wordCount - 1, key(wordCount - 1));

    return true;
}

bool
OffsetCacheFile::lookupPage(const QByteArray& word, int& pageIndex)
{
    bool found = false;

    if (stardictStringCompare(word, d->first.second) < 0)
    {
        pageIndex = invalidIndex;
        return false;
    }
    else if (stardictStringCompare(word, d->realLast.second) > 0)
    {
        pageIndex = invalidIndex;
        return false;
    }
    else
    {
        int indexTo = d->pageOffsetList.size() - 2;
        int indexFrom = 0;
        int indexThisIndex = 0;
        int cmpint;
        while (indexFrom <= indexTo)
        {
            indexThisIndex = (indexFrom + indexTo) / 2;
            cmpint = stardictStringCompare(word, firstWordDataOnPage(indexThisIndex));
            if (cmpint > 0)
                indexFrom = indexThisIndex + 1;
            else if (cmpint < 0)
                indexTo = indexThisIndex - 1;
            else
            {
                found = true;
                break;
            }
        }

        if (!found)
            pageIndex = indexTo;    //prev
        else
            pageIndex = indexThisIndex;
    }

    return found;
}

bool
OffsetCacheFile::lookup(const QByteArray& word, int &index)
{
    bool found = false;
    int indexFrom;
    int cmpint;
    int indexThisIndex;

    found = lookupPage(word, index);

    if (!found)
    {
        ulong netr = loadPage(index);
        indexFrom = 1; // Needn't search the first word anymore.
        int indexTo = netr - 1;
        indexThisIndex = 0;
        while (indexFrom <= indexTo)
        {
            indexThisIndex = (indexFrom + indexTo) / 2;
            cmpint = stardictStringCompare(word, d->wordEntryList.at(indexThisIndex).data());
            if (cmpint > 0)
                indexFrom = indexThisIndex + 1;
            else if (cmpint < 0)
                indexTo = indexThisIndex - 1;
            else
            {
                found = true;
                break;
            }
        }

        index *= d->pageEntryNumber;
        if (!found)
            index += indexFrom;    //next
        else
            index += indexThisIndex;
    }
    else
    {
        index *= d->pageEntryNumber;
    }

    return found;
}

quint32
OffsetCacheFile::wordEntryOffset() const
{
    return AbstractIndexFile::wordEntryOffset();
}

void
OffsetCacheFile::setWordEntryOffset(quint32 wordEntryOffset)
{
    AbstractIndexFile::setWordEntryOffset(wordEntryOffset);
}

quint32
OffsetCacheFile::wordEntrySize() const
{
    return AbstractIndexFile::wordEntrySize();
}

void
OffsetCacheFile::setWordEntrySize(quint32 wordEntrySize)
{
    AbstractIndexFile::setWordEntrySize(wordEntrySize);
}
<|endoftext|>


<commit_before>#ifdef PARTICLES

#include<stdio.h>
#include<stdlib.h>
#include"math.h"
#include <iostream>
#include"../global.h"
#include"../grid3D.h"
#include"particles_3D.h"

#ifdef PARALLEL_OMP
#include"../parallel_omp.h"
#endif

Real Grid3D::Calc_Particles_dt( ){

  Real dt_particles;
  #ifndef PARALLEL_OMP
  dt_particles = Calc_Particles_dt_function( 0, Particles.n_local );
  #else
  dt_particles = 1e100;
  Real dt_particles_all[N_OMP_THREADS];
  #pragma omp parallel num_threads( N_OMP_THREADS )
  {
    int omp_id, n_omp_procs;
    part_int_t p_start, p_end;
    omp_id = omp_get_thread_num();
    n_omp_procs = omp_get_num_threads();
    Get_OMP_Particles_Indxs( Particles.n_local, N_OMP_THREADS, omp_id,  &p_start, &p_end );
    dt_particles_all[omp_id] = Calc_Particles_dt_function( p_start, p_end );
  }

  for ( int i=0; i<N_OMP_THREADS; i++ ){
    dt_particles = fmin( dt_particles, dt_particles_all[i]);
  }
  #endif

  Real dt_particles_global;
  #ifdef MPI_CHOLLA
  dt_particles_global = ReduceRealMin( dt_particles );
  #else
  dt_particles_global = dt_particles;
  #endif

  return dt_particles_global;
}

Real Grid3D::Calc_Particles_dt_function( part_int_t p_start, part_int_t p_end ){
  part_int_t pID;
  Real dt, dt_min, vel;
  dt_min = 1e100;

  for ( pID=p_start; pID<p_end; pID++ ){
    vel = fabs(Particles.vel_x[pID]);
    if ( vel > 0){
      dt = Particles.G.dx / vel;
      dt_min = std::min( dt_min, dt);
    }
    vel = fabs(Particles.vel_y[pID]);
    if ( vel > 0){
      dt = Particles.G.dy / vel;
      dt_min = std::min( dt_min, dt);
    }
    vel = fabs(Particles.vel_z[pID]);
    if ( vel > 0){
      dt = Particles.G.dz / vel;
      dt_min = std::min( dt_min, dt);
    }
  }
  return Particles.C_cfl * dt_min;
}

void Grid3D::Advance_Particles( int N_KDK_step ){

  #ifdef CPU_TIME
  Timer.Start_Timer();
  #endif

  if ( N_KDK_step == 1 ) Advance_Particles_KDK_Step1();

  if ( N_KDK_step == 2 ){
    Get_Particles_Accelration();
    Advance_Particles_KDK_Step2();
  }

  #ifdef CPU_TIME
  if ( N_KDK_step == 1) Timer.End_and_Record_Time(6);
  if ( N_KDK_step == 2) Timer.End_and_Record_Time(7);
  #endif

}

void Grid3D::Get_Particles_Accelration(){
  // Get the accteleration for all the particles
  Get_Gravity_Field_Particles();
  Get_Gravity_CIC();
}

void Grid3D::Advance_Particles_KDK_Step1( ){

  #ifndef PARALLEL_OMP
  #ifdef COSMOLOGY
  Advance_Particles_KDK_Cosmo_Step1_function( 0, Particles.n_local );
  #else
  Advance_Particles_KDK_Step1_function( 0, Particles.n_local );
  #endif//COSMOLOGY
  #else
  #pragma omp parallel num_threads( N_OMP_THREADS )
  {
    int omp_id, n_omp_procs;
    part_int_t p_start, p_end;
    omp_id = omp_get_thread_num();
    n_omp_procs = omp_get_num_threads();
    Get_OMP_Particles_Indxs( Particles.n_local, N_OMP_THREADS, omp_id,  &p_start, &p_end );
    #ifdef COSMOLOGY
    Advance_Particles_KDK_Cosmo_Step1_function( p_start, p_end );
    #else
    Advance_Particles_KDK_Step1_function( p_start, p_end );
    #endif//COSMOLOGY
  }
  #endif
}

void Grid3D::Advance_Particles_KDK_Step2( ){

  #ifndef PARALLEL_OMP
  #ifdef COSMOLOGY
  Advance_Particles_KDK_Cosmo_Step2_function( 0, Particles.n_local );
  #else
  Advance_Particles_KDK_Step2_function( 0, Particles.n_local );
  #endif//COSMOLOGY
  #else
  #pragma omp parallel num_threads( N_OMP_THREADS )
  {
    int omp_id, n_omp_procs;
    part_int_t p_start, p_end;
    omp_id = omp_get_thread_num();
    n_omp_procs = omp_get_num_threads();
    Get_OMP_Particles_Indxs( Particles.n_local, N_OMP_THREADS, omp_id,  &p_start, &p_end );
    #ifdef COSMOLOGY
    Advance_Particles_KDK_Cosmo_Step2_function( p_start, p_end );
    #else
    Advance_Particles_KDK_Step2_function( p_start, p_end );
    #endif//COSMOLOGY
  }
  #endif
}

void Grid3D::Advance_Particles_KDK_Step1_function( part_int_t p_start, part_int_t p_end ){

  part_int_t pID;
  Real dt = Particles.dt;
  // Advance velocities by half a step
  for ( pID=p_start; pID<p_end; pID++ ){
    Particles.vel_x[pID] += 0.5 * dt * Particles.grav_x[pID];
    Particles.vel_y[pID] += 0.5 * dt * Particles.grav_y[pID];
    Particles.vel_z[pID] += 0.5 * dt * Particles.grav_z[pID];
  }

  //Advance Posiotions using advanced velocities
  for ( pID=p_start; pID<p_end; pID++ ){
    Particles.pos_x[pID] += dt * Particles.vel_x[pID];
    Particles.pos_y[pID] += dt * Particles.vel_y[pID];
    Particles.pos_z[pID] += dt * Particles.vel_z[pID];
  }
}

void Grid3D::Advance_Particles_KDK_Step2_function( part_int_t p_start, part_int_t p_end ){

  part_int_t pID;
  Real dt = Particles.dt;
  // Advance velocities by the second half a step
  for ( pID=p_start; pID<p_end; pID++ ){
    Particles.vel_x[pID] += 0.5 * dt * Particles.grav_x[pID];
    Particles.vel_y[pID] += 0.5 * dt * Particles.grav_y[pID];
    Particles.vel_z[pID] += 0.5 * dt * Particles.grav_z[pID];
  }
}

#ifdef COSMOLOGY

Real Grid3D::Calc_Particles_dt_Cosmo(){

  Real dt_particles;
  #ifndef PARALLEL_OMP
  dt_particles = Calc_Particles_dt_Cosmo_function( 0, Particles.n_local );
  #else
  dt_particles = 1e100;
  Real dt_particles_all[N_OMP_THREADS];
  #pragma omp parallel num_threads( N_OMP_THREADS )
  {
    int omp_id, n_omp_procs;
    part_int_t p_start, p_end;
    omp_id = omp_get_thread_num();
    n_omp_procs = omp_get_num_threads();
    Get_OMP_Particles_Indxs( Particles.n_local, N_OMP_THREADS, omp_id,  &p_start, &p_end );
    dt_particles_all[omp_id] = Calc_Particles_dt_Cosmo_function( p_start, p_end );
  }

  for ( int i=0; i<N_OMP_THREADS; i++ ){
    dt_particles = fmin( dt_particles, dt_particles_all[i]);
  }
  #endif

  Real dt_particles_global;
  #ifdef MPI_CHOLLA
  dt_particles_global = ReduceRealMin( dt_particles );
  #else
  dt_particles_global = dt_particles;
  #endif

  return dt_particles_global;

}

Real Grid3D::Calc_Particles_dt_Cosmo_function( part_int_t p_start, part_int_t p_end ){

  part_int_t pID;
  Real da, da_min, vel, dt_min;
  da_min = 1e100;
  Real scale_factor = Cosmo.Scale_Function( Cosmo.current_a , Cosmo.Omega_M, Cosmo.Omega_L, Cosmo.Omega_K  ) / Cosmo.H0 * Cosmo.cosmo_h;
  Real a2 = ( Cosmo.current_a )*( Cosmo.current_a  );
  Real vel_factor = a2 / scale_factor;

  for ( pID=0; pID<Particles.n_local; pID++ ){
    vel = fabs(Particles.vel_x[pID]);
    if ( vel > 0){
      da = Particles.G.dx * vel_factor / vel;
      da_min = std::min( da_min, da);
    }
    vel = fabs(Particles.vel_y[pID]);
    if ( vel > 0){
      da = Particles.G.dy * vel_factor / vel;
      da_min = std::min( da_min, da);
    }
    vel = fabs(Particles.vel_z[pID]);
    if ( vel > 0){
      da = Particles.G.dz * vel_factor / vel;
      da_min = std::min( da_min, da);
    }
  }
  dt_min = Cosmo.Get_dt_from_da( da_min );
  return Particles.C_cfl * dt_min;
}

void Grid3D::Advance_Particles_KDK_Cosmo_Step1_function( part_int_t p_start, part_int_t p_end ){

  part_int_t pIndx;
  Real da = Cosmo.delta_a;
  Real current_a = Cosmo.current_a;

  Real scale_factor = Cosmo.Scale_Function( current_a, Cosmo.Omega_M, Cosmo.Omega_L, Cosmo.Omega_K ) / Cosmo.H0 * Cosmo.cosmo_h;
  Real scale_factor_1 = Cosmo.Scale_Function( current_a + 0.5*da, Cosmo.Omega_M, Cosmo.Omega_L, Cosmo.Omega_K  ) / Cosmo.H0 * Cosmo.cosmo_h;
  Real a2_inv = 1./( ( current_a + 0.5*da )*( current_a + 0.5*da ));
  // Advance velocities by half a step
  Real pos_x, vel_x, grav_x;
  Real pos_y, vel_y, grav_y;
  Real pos_z, vel_z, grav_z;
  for ( pIndx=p_start; pIndx<p_end; pIndx++ ){
    pos_x = Particles.pos_x[pIndx];
    pos_y = Particles.pos_y[pIndx];
    pos_z = Particles.pos_z[pIndx];
    vel_x = Particles.vel_x[pIndx];
    vel_y = Particles.vel_y[pIndx];
    vel_z = Particles.vel_z[pIndx];
    grav_x = Particles.grav_x[pIndx];
    grav_y = Particles.grav_y[pIndx];
    grav_z = Particles.grav_z[pIndx];

    vel_x += scale_factor * 0.5 * da * grav_x;
    vel_y += scale_factor * 0.5 * da * grav_y;
    vel_z += scale_factor * 0.5 * da * grav_z;

    pos_x += a2_inv * scale_factor_1 * da * vel_x;
    pos_y += a2_inv * scale_factor_1 * da * vel_y;
    pos_z += a2_inv * scale_factor_1 * da * vel_z;

    Particles.pos_x[pIndx] = pos_x;
    Particles.pos_y[pIndx] = pos_y;
    Particles.pos_z[pIndx] = pos_z;

    Particles.vel_x[pIndx] = vel_x;
    Particles.vel_y[pIndx] = vel_y;
    Particles.vel_z[pIndx] = vel_z;
  }
}

void Grid3D::Advance_Particles_KDK_Cosmo_Step2_function( part_int_t p_start, part_int_t p_end ){
  part_int_t pIndx;
  Real da = Cosmo.delta_a;
  Real current_a = Cosmo.current_a;

  Real scale_factor = Cosmo.Scale_Function( current_a , Cosmo.Omega_M, Cosmo.Omega_L, Cosmo.Omega_K ) / Cosmo.H0 * Cosmo.cosmo_h;
  // Advance velocities by half a step
  Real grav_x;
  Real grav_y;
  Real grav_z;
  for ( pIndx=p_start; pIndx<p_end; pIndx++ ){
    grav_x = Particles.grav_x[pIndx];
    grav_y = Particles.grav_y[pIndx];
    grav_z = Particles.grav_z[pIndx];
    Particles.vel_x[pIndx] += scale_factor * 0.5 * da * grav_x;
    Particles.vel_y[pIndx] += scale_factor * 0.5 * da * grav_y;
    Particles.vel_z[pIndx] += scale_factor * 0.5 * da * grav_z;
  }
}

#endif

#endif//PARTICLES<commit_msg>optimized dt_particles<commit_after>#ifdef PARTICLES

#include<stdio.h>
#include<stdlib.h>
#include"math.h"
#include <iostream>
#include"../global.h"
#include"../grid3D.h"
#include"particles_3D.h"

#ifdef PARALLEL_OMP
#include"../parallel_omp.h"
#endif

Real Grid3D::Calc_Particles_dt( ){

  Real dt_particles;
  #ifndef PARALLEL_OMP
  dt_particles = Calc_Particles_dt_function( 0, Particles.n_local );
  #else
  dt_particles = 1e100;
  Real dt_particles_all[N_OMP_THREADS];
  #pragma omp parallel num_threads( N_OMP_THREADS )
  {
    int omp_id, n_omp_procs;
    part_int_t p_start, p_end;
    omp_id = omp_get_thread_num();
    n_omp_procs = omp_get_num_threads();
    Get_OMP_Particles_Indxs( Particles.n_local, N_OMP_THREADS, omp_id,  &p_start, &p_end );
    dt_particles_all[omp_id] = Calc_Particles_dt_function( p_start, p_end );
  }

  for ( int i=0; i<N_OMP_THREADS; i++ ){
    dt_particles = fmin( dt_particles, dt_particles_all[i]);
  }
  #endif

  Real dt_particles_global;
  #ifdef MPI_CHOLLA
  dt_particles_global = ReduceRealMin( dt_particles );
  #else
  dt_particles_global = dt_particles;
  #endif

  return dt_particles_global;
}

Real Grid3D::Calc_Particles_dt_function( part_int_t p_start, part_int_t p_end ){
  part_int_t pID;
  Real dt, dt_min, vel;
  dt_min = 1e100;

  for ( pID=p_start; pID<p_end; pID++ ){
    vel = fabs(Particles.vel_x[pID]);
    if ( vel > 0){
      dt = Particles.G.dx / vel;
      dt_min = std::min( dt_min, dt);
    }
    vel = fabs(Particles.vel_y[pID]);
    if ( vel > 0){
      dt = Particles.G.dy / vel;
      dt_min = std::min( dt_min, dt);
    }
    vel = fabs(Particles.vel_z[pID]);
    if ( vel > 0){
      dt = Particles.G.dz / vel;
      dt_min = std::min( dt_min, dt);
    }
  }
  return Particles.C_cfl * dt_min;
}

void Grid3D::Advance_Particles( int N_KDK_step ){

  #ifdef CPU_TIME
  Timer.Start_Timer();
  #endif

  if ( N_KDK_step == 1 ) Advance_Particles_KDK_Step1();

  if ( N_KDK_step == 2 ){
    Get_Particles_Accelration();
    Advance_Particles_KDK_Step2();
  }

  #ifdef CPU_TIME
  if ( N_KDK_step == 1) Timer.End_and_Record_Time(6);
  if ( N_KDK_step == 2) Timer.End_and_Record_Time(7);
  #endif

}

void Grid3D::Get_Particles_Accelration(){
  // Get the accteleration for all the particles
  Get_Gravity_Field_Particles();
  Get_Gravity_CIC();
}

void Grid3D::Advance_Particles_KDK_Step1( ){

  #ifndef PARALLEL_OMP
  #ifdef COSMOLOGY
  Advance_Particles_KDK_Cosmo_Step1_function( 0, Particles.n_local );
  #else
  Advance_Particles_KDK_Step1_function( 0, Particles.n_local );
  #endif//COSMOLOGY
  #else
  #pragma omp parallel num_threads( N_OMP_THREADS )
  {
    int omp_id, n_omp_procs;
    part_int_t p_start, p_end;
    omp_id = omp_get_thread_num();
    n_omp_procs = omp_get_num_threads();
    Get_OMP_Particles_Indxs( Particles.n_local, N_OMP_THREADS, omp_id,  &p_start, &p_end );
    #ifdef COSMOLOGY
    Advance_Particles_KDK_Cosmo_Step1_function( p_start, p_end );
    #else
    Advance_Particles_KDK_Step1_function( p_start, p_end );
    #endif//COSMOLOGY
  }
  #endif
}

void Grid3D::Advance_Particles_KDK_Step2( ){

  #ifndef PARALLEL_OMP
  #ifdef COSMOLOGY
  Advance_Particles_KDK_Cosmo_Step2_function( 0, Particles.n_local );
  #else
  Advance_Particles_KDK_Step2_function( 0, Particles.n_local );
  #endif//COSMOLOGY
  #else
  #pragma omp parallel num_threads( N_OMP_THREADS )
  {
    int omp_id, n_omp_procs;
    part_int_t p_start, p_end;
    omp_id = omp_get_thread_num();
    n_omp_procs = omp_get_num_threads();
    Get_OMP_Particles_Indxs( Particles.n_local, N_OMP_THREADS, omp_id,  &p_start, &p_end );
    #ifdef COSMOLOGY
    Advance_Particles_KDK_Cosmo_Step2_function( p_start, p_end );
    #else
    Advance_Particles_KDK_Step2_function( p_start, p_end );
    #endif//COSMOLOGY
  }
  #endif
}

void Grid3D::Advance_Particles_KDK_Step1_function( part_int_t p_start, part_int_t p_end ){

  part_int_t pID;
  Real dt = Particles.dt;
  // Advance velocities by half a step
  for ( pID=p_start; pID<p_end; pID++ ){
    Particles.vel_x[pID] += 0.5 * dt * Particles.grav_x[pID];
    Particles.vel_y[pID] += 0.5 * dt * Particles.grav_y[pID];
    Particles.vel_z[pID] += 0.5 * dt * Particles.grav_z[pID];
  }

  //Advance Posiotions using advanced velocities
  for ( pID=p_start; pID<p_end; pID++ ){
    Particles.pos_x[pID] += dt * Particles.vel_x[pID];
    Particles.pos_y[pID] += dt * Particles.vel_y[pID];
    Particles.pos_z[pID] += dt * Particles.vel_z[pID];
  }
}

void Grid3D::Advance_Particles_KDK_Step2_function( part_int_t p_start, part_int_t p_end ){

  part_int_t pID;
  Real dt = Particles.dt;
  // Advance velocities by the second half a step
  for ( pID=p_start; pID<p_end; pID++ ){
    Particles.vel_x[pID] += 0.5 * dt * Particles.grav_x[pID];
    Particles.vel_y[pID] += 0.5 * dt * Particles.grav_y[pID];
    Particles.vel_z[pID] += 0.5 * dt * Particles.grav_z[pID];
  }
}

#ifdef COSMOLOGY

Real Grid3D::Calc_Particles_dt_Cosmo(){

  Real dt_particles;
  #ifndef PARALLEL_OMP
  dt_particles = Calc_Particles_dt_Cosmo_function( 0, Particles.n_local );
  #else
  dt_particles = 1e100;
  Real dt_particles_all[N_OMP_THREADS];
  #pragma omp parallel num_threads( N_OMP_THREADS )
  {
    int omp_id, n_omp_procs;
    part_int_t p_start, p_end;
    omp_id = omp_get_thread_num();
    n_omp_procs = omp_get_num_threads();
    Get_OMP_Particles_Indxs( Particles.n_local, N_OMP_THREADS, omp_id,  &p_start, &p_end );
    dt_particles_all[omp_id] = Calc_Particles_dt_Cosmo_function( p_start, p_end );
  }

  for ( int i=0; i<N_OMP_THREADS; i++ ){
    dt_particles = fmin( dt_particles, dt_particles_all[i]);
  }
  #endif

  Real dt_particles_global;
  #ifdef MPI_CHOLLA
  dt_particles_global = ReduceRealMin( dt_particles );
  #else
  dt_particles_global = dt_particles;
  #endif

  return dt_particles_global;

}

Real Grid3D::Calc_Particles_dt_Cosmo_function( part_int_t p_start, part_int_t p_end ){

  part_int_t pID;
  Real da, da_min, vel, dt_min;
  da_min = 1e100;
  Real scale_factor = Cosmo.Scale_Function( Cosmo.current_a , Cosmo.Omega_M, Cosmo.Omega_L, Cosmo.Omega_K  ) / Cosmo.H0 * Cosmo.cosmo_h;
  Real a2 = ( Cosmo.current_a )*( Cosmo.current_a  );
  Real vel_factor = a2 / scale_factor;
  Real vx_max, vy_max, vz_max;
  vx_max = 0;
  vy_max = 0;
  vz_max = 0;

  for ( pID=p_start; pID<p_end; pID++ ){
    vx_max = fmax( vx_max,  fabs(Particles.vel_x[pID]) );
    vy_max = fmax( vy_max,  fabs(Particles.vel_y[pID]) );
    vz_max = fmax( vz_max,  fabs(Particles.vel_z[pID]) );
  }

  da_min = fmin( Particles.G.dx / vx_max, Particles.G.dy / vy_max  );
  da_min = fmin( Particles.G.dz / vz_max, da_min  );

  da_min *= vel_factor;

  dt_min = Cosmo.Get_dt_from_da( da_min );

  // for ( pID=0; pID<Particles.n_local; pID++ ){
  //   vel = fabs(Particles.vel_x[pID]);
  //   if ( vel > 0){
  //     da = Particles.G.dx * vel_factor / vel;
  //     da_min = std::min( da_min, da);
  //   }
  //   vel = fabs(Particles.vel_y[pID]);
  //   if ( vel > 0){
  //     da = Particles.G.dy * vel_factor / vel;
  //     da_min = std::min( da_min, da);
  //   }
  //   vel = fabs(Particles.vel_z[pID]);
  //   if ( vel > 0){
  //     da = Particles.G.dz * vel_factor / vel;
  //     da_min = std::min( da_min, da);
  //   }
  // }
  // dt_min = Cosmo.Get_dt_from_da( da_min );
  return Particles.C_cfl * dt_min;
}

void Grid3D::Advance_Particles_KDK_Cosmo_Step1_function( part_int_t p_start, part_int_t p_end ){

  part_int_t pIndx;
  Real da = Cosmo.delta_a;
  Real current_a = Cosmo.current_a;

  Real scale_factor = Cosmo.Scale_Function( current_a, Cosmo.Omega_M, Cosmo.Omega_L, Cosmo.Omega_K ) / Cosmo.H0 * Cosmo.cosmo_h;
  Real scale_factor_1 = Cosmo.Scale_Function( current_a + 0.5*da, Cosmo.Omega_M, Cosmo.Omega_L, Cosmo.Omega_K  ) / Cosmo.H0 * Cosmo.cosmo_h;
  Real a2_inv = 1./( ( current_a + 0.5*da )*( current_a + 0.5*da ));
  // Advance velocities by half a step
  Real pos_x, vel_x, grav_x;
  Real pos_y, vel_y, grav_y;
  Real pos_z, vel_z, grav_z;
  for ( pIndx=p_start; pIndx<p_end; pIndx++ ){
    pos_x = Particles.pos_x[pIndx];
    pos_y = Particles.pos_y[pIndx];
    pos_z = Particles.pos_z[pIndx];
    vel_x = Particles.vel_x[pIndx];
    vel_y = Particles.vel_y[pIndx];
    vel_z = Particles.vel_z[pIndx];
    grav_x = Particles.grav_x[pIndx];
    grav_y = Particles.grav_y[pIndx];
    grav_z = Particles.grav_z[pIndx];

    vel_x += scale_factor * 0.5 * da * grav_x;
    vel_y += scale_factor * 0.5 * da * grav_y;
    vel_z += scale_factor * 0.5 * da * grav_z;

    pos_x += a2_inv * scale_factor_1 * da * vel_x;
    pos_y += a2_inv * scale_factor_1 * da * vel_y;
    pos_z += a2_inv * scale_factor_1 * da * vel_z;

    Particles.pos_x[pIndx] = pos_x;
    Particles.pos_y[pIndx] = pos_y;
    Particles.pos_z[pIndx] = pos_z;

    Particles.vel_x[pIndx] = vel_x;
    Particles.vel_y[pIndx] = vel_y;
    Particles.vel_z[pIndx] = vel_z;
  }
}

void Grid3D::Advance_Particles_KDK_Cosmo_Step2_function( part_int_t p_start, part_int_t p_end ){
  part_int_t pIndx;
  Real da = Cosmo.delta_a;
  Real current_a = Cosmo.current_a;

  Real scale_factor = Cosmo.Scale_Function( current_a , Cosmo.Omega_M, Cosmo.Omega_L, Cosmo.Omega_K ) / Cosmo.H0 * Cosmo.cosmo_h;
  // Advance velocities by half a step
  Real grav_x;
  Real grav_y;
  Real grav_z;
  for ( pIndx=p_start; pIndx<p_end; pIndx++ ){
    grav_x = Particles.grav_x[pIndx];
    grav_y = Particles.grav_y[pIndx];
    grav_z = Particles.grav_z[pIndx];
    Particles.vel_x[pIndx] += scale_factor * 0.5 * da * grav_x;
    Particles.vel_y[pIndx] += scale_factor * 0.5 * da * grav_y;
    Particles.vel_z[pIndx] += scale_factor * 0.5 * da * grav_z;
  }
}

#endif

#endif//PARTICLES<|endoftext|>


<commit_before>/*
 * File:   SensorSymbols.cpp
 * Author: thomas
 *
 * Created on 9. April 2009, 18:10
 */

#include "SensorSymbols.h"

void SensorSymbols::registerSymbols(xabsl::Engine& engine)
{
  engine.registerDecimalInputSymbol("sensor.gyro.x", &getGyrometerData().data.x);
  engine.registerDecimalInputSymbol("sensor.gyro.y", &getGyrometerData().data.y);

  engine.registerDecimalInputSymbol("sensor.inertial.x", &getInertialSensorX);
  engine.registerDecimalInputSymbol("sensor.inertial.y", &getInertialSensorY);

  engine.registerBooleanInputSymbol("sensor.inertial.calibrated", &getCalibrationData().calibrated);

  engine.registerDecimalInputSymbol("sensor.ultrasound", &getUltraSoundReceiveData().rawdata);
  engine.registerDecimalInputSymbol("sensor.ultrasound.left", &simplePassLeftSensor);
  engine.registerDecimalInputSymbol("sensor.ultrasound.right", &simplePassRightSensor);

  //engine.registerDecimalInputSymbol("platform.battery", &getBatteryData().charge);
  engine.registerDecimalInputSymbol("platform.battery", &getBodyState().batteryCharge);

  engine.registerEnumElement("fall_down_state", "fall_down_state.undefined", BodyState::undefined);
  engine.registerEnumElement("fall_down_state", "fall_down_state.upright", BodyState::upright);
  engine.registerEnumElement("fall_down_state", "fall_down_state.lying_on_front", BodyState::lying_on_front);
  engine.registerEnumElement("fall_down_state", "fall_down_state.lying_on_back", BodyState::lying_on_back);
  engine.registerEnumElement("fall_down_state", "fall_down_state.lying_on_left_side", BodyState::lying_on_left_side);
  engine.registerEnumElement("fall_down_state", "fall_down_state.lying_on_right_side", BodyState::lying_on_right_side);

  engine.registerEnumeratedInputSymbol("fall_down_state", "fall_down_state", &getFallDownState);

  engine.registerDecimalInputSymbol("body.temperature.leg.left", &getBodyState().temperatureLeftLeg);
  engine.registerDecimalInputSymbol("body.temperature.leg.right", &getBodyState().temperatureRightLeg);

  engine.registerBooleanInputSymbol("body.foot.groundcontact.left", &getBodyState().standByLeftFoot);
  engine.registerBooleanInputSymbol("body.foot.groundcontact.right", &getBodyState().standByRightFoot);
  engine.registerDecimalInputSymbol("body.foot.groundcontact.statetime", &getFootStateTime);

  engine.registerBooleanInputSymbol("body.lifted_up", &getBodyState().isLiftedUp);

  engine.registerBooleanInputSymbol("body.collision.arm.left", &getCollisionArmLeft);
  engine.registerBooleanInputSymbol("body.collision.arm.right", &getCollisionArmRight);

  engine.registerDecimalInputSymbol("platform.frameNumber", &getFrameNumber);

  engine.registerDecimalInputSymbol("obstacle.ultrasound.distance", &getObstacleDistance);
  engine.registerDecimalInputSymbol("obstacle.ultrasound.left.distance", &getObstacleDistanceLeft);
  engine.registerDecimalInputSymbol("obstacle.ultrasound.right.distance", &getObstacleDistanceRight);
  engine.registerDecimalInputSymbol("obstacle.ultrasound.blockedtime", &getBlockedTime);

  engine.registerBooleanInputSymbol("button.bumper.pressed.left", &getBumperLeftPressed);
  engine.registerBooleanInputSymbol("button.bumper.pressed.right", &getBumperRightPressed);
  engine.registerBooleanInputSymbol("button.head.pressed.front", &getButtonHeadFront);
  engine.registerBooleanInputSymbol("button.head.pressed.middle", &getButtonHeadMiddle);
  engine.registerBooleanInputSymbol("button.head.pressed.rear", &getButtonHeadRear);

  // deprecated?
  engine.registerBooleanInputSymbol("battery.isDischarging", &getBodyState().isDischarging);
  engine.registerBooleanInputSymbol("battery.isCharging", &getBodyState().isCharging);

  engine.registerBooleanOutputSymbol("audio.capture", &getAudioControl().capture);

}//end registerSymbols

SensorSymbols* SensorSymbols::theInstance = NULL;

SensorSymbols::~SensorSymbols()
{
}

void SensorSymbols::execute()
{

}//end execute

bool SensorSymbols::getBumperLeftPressed()
{
    return theInstance->getButtonData().isPressed[ButtonData::LeftFootLeft] || theInstance->getButtonData().isPressed[ButtonData::LeftFootRight];
  /*return (theInstance->getButtonData().eventCounter[ButtonData::LeftFootLeft] > 0) ||
         (theInstance->getButtonData().eventCounter[ButtonData::LeftFootRight] > 0);*/
}
bool SensorSymbols::getBumperLeftPressed()
{
    return theInstance->getButtonData().isPressed[ButtonData::LeftFootLeft] || theInstance->getButtonData().isPressed[ButtonData::LeftFootRight];
    /*return (theInstance->getButtonData().eventCounter[ButtonData::LeftFootLeft] > 0) ||
    (theInstance->getButtonData().eventCounter[ButtonData::LeftFootRight] > 0);*/
}
bool SensorSymbols::getButtonHeadFront()
{
  return (theInstance->getButtonData().isPressed[ButtonData::HeadFront]);
}
bool SensorSymbols::getButtonHeadMiddle()
{
  return (theInstance->getButtonData().isPressed[ButtonData::HeadMiddle]);
}
bool SensorSymbols::getButtonHeadRear()
{
  return (theInstance->getButtonData().isPressed[ButtonData::HeadRear]);
}

double SensorSymbols::getFrameNumber()
{
  return (double) (theInstance->getFrameInfo().getFrameNumber());
}

double SensorSymbols::getFootStateTime()
{
  return (double) theInstance->getFrameInfo().getTimeSince(theInstance->getBodyState().foot_state_time);
}

int SensorSymbols::getFallDownState()
{
  return (int)(theInstance->getBodyState().fall_down_state);
}

// returns whether some data is received or not
double SensorSymbols::simplePassLeftSensor()
{
  double r = 2550.0;
  // return minimum measurement = closest object
  for(size_t i = 0; i < UltraSoundReceiveData::numOfUSEcho; i++)
  {
    if((theInstance->getUltraSoundReceiveData().dataLeft[i] * 1000) < r && theInstance->getUltraSoundReceiveData().dataLeft[i] > 0.2)
    {
      r = theInstance->getUltraSoundReceiveData().dataLeft[i] * 1000;
    }
  }
  return r;
} // end simpleLeftUSSensor

double SensorSymbols::simplePassRightSensor()
{
  double r = 2550.0;
  // return minimum measurement = closest object
  for(size_t i = 0; i < UltraSoundReceiveData::numOfUSEcho; i++)
  {
    if((theInstance->getUltraSoundReceiveData().dataRight[i] * 1000) < r && theInstance->getUltraSoundReceiveData().dataRight[i] > 0.2)
    {
      r = theInstance->getUltraSoundReceiveData().dataRight[i] * 1000;
    }
  }
  return r;
} // end simpleRightUSSensor

double SensorSymbols::getObstacleDistance()
{
  return theInstance->getObstacleModel().frontDistance;
}

double SensorSymbols::getObstacleDistanceLeft()
{
  return theInstance->getObstacleModel().leftDistance;
}

double SensorSymbols::getObstacleDistanceRight()
{
  return theInstance->getObstacleModel().rightDistance;
}

double SensorSymbols::getBlockedTime()
{
  return theInstance->getObstacleModel().blockedTime;
}

double SensorSymbols::getInertialSensorX()
{
  return Math::toDegrees(theInstance->getInertialSensorData().data.x);
}

double SensorSymbols::getInertialSensorY()
{
  return Math::toDegrees(theInstance->getInertialSensorData().data.y);
}

bool SensorSymbols::getCollisionArmLeft()
{
  return
    theInstance->getCollisionPercept().timeCollisionArmLeft > 0 &&
    theInstance->getFrameInfo().getTimeSince(theInstance->getCollisionPercept().timeCollisionArmLeft) < 100;
}

bool SensorSymbols::getCollisionArmRight()
{
  return
    theInstance->getCollisionPercept().timeCollisionArmRight > 0 &&
    theInstance->getFrameInfo().getTimeSince(theInstance->getCollisionPercept().timeCollisionArmRight) < 100;
}
<commit_msg>forgot small thing<commit_after>/*
 * File:   SensorSymbols.cpp
 * Author: thomas
 *
 * Created on 9. April 2009, 18:10
 */

#include "SensorSymbols.h"

void SensorSymbols::registerSymbols(xabsl::Engine& engine)
{
  engine.registerDecimalInputSymbol("sensor.gyro.x", &getGyrometerData().data.x);
  engine.registerDecimalInputSymbol("sensor.gyro.y", &getGyrometerData().data.y);

  engine.registerDecimalInputSymbol("sensor.inertial.x", &getInertialSensorX);
  engine.registerDecimalInputSymbol("sensor.inertial.y", &getInertialSensorY);

  engine.registerBooleanInputSymbol("sensor.inertial.calibrated", &getCalibrationData().calibrated);

  engine.registerDecimalInputSymbol("sensor.ultrasound", &getUltraSoundReceiveData().rawdata);
  engine.registerDecimalInputSymbol("sensor.ultrasound.left", &simplePassLeftSensor);
  engine.registerDecimalInputSymbol("sensor.ultrasound.right", &simplePassRightSensor);

  //engine.registerDecimalInputSymbol("platform.battery", &getBatteryData().charge);
  engine.registerDecimalInputSymbol("platform.battery", &getBodyState().batteryCharge);

  engine.registerEnumElement("fall_down_state", "fall_down_state.undefined", BodyState::undefined);
  engine.registerEnumElement("fall_down_state", "fall_down_state.upright", BodyState::upright);
  engine.registerEnumElement("fall_down_state", "fall_down_state.lying_on_front", BodyState::lying_on_front);
  engine.registerEnumElement("fall_down_state", "fall_down_state.lying_on_back", BodyState::lying_on_back);
  engine.registerEnumElement("fall_down_state", "fall_down_state.lying_on_left_side", BodyState::lying_on_left_side);
  engine.registerEnumElement("fall_down_state", "fall_down_state.lying_on_right_side", BodyState::lying_on_right_side);

  engine.registerEnumeratedInputSymbol("fall_down_state", "fall_down_state", &getFallDownState);

  engine.registerDecimalInputSymbol("body.temperature.leg.left", &getBodyState().temperatureLeftLeg);
  engine.registerDecimalInputSymbol("body.temperature.leg.right", &getBodyState().temperatureRightLeg);

  engine.registerBooleanInputSymbol("body.foot.groundcontact.left", &getBodyState().standByLeftFoot);
  engine.registerBooleanInputSymbol("body.foot.groundcontact.right", &getBodyState().standByRightFoot);
  engine.registerDecimalInputSymbol("body.foot.groundcontact.statetime", &getFootStateTime);

  engine.registerBooleanInputSymbol("body.lifted_up", &getBodyState().isLiftedUp);

  engine.registerBooleanInputSymbol("body.collision.arm.left", &getCollisionArmLeft);
  engine.registerBooleanInputSymbol("body.collision.arm.right", &getCollisionArmRight);

  engine.registerDecimalInputSymbol("platform.frameNumber", &getFrameNumber);

  engine.registerDecimalInputSymbol("obstacle.ultrasound.distance", &getObstacleDistance);
  engine.registerDecimalInputSymbol("obstacle.ultrasound.left.distance", &getObstacleDistanceLeft);
  engine.registerDecimalInputSymbol("obstacle.ultrasound.right.distance", &getObstacleDistanceRight);
  engine.registerDecimalInputSymbol("obstacle.ultrasound.blockedtime", &getBlockedTime);

  engine.registerBooleanInputSymbol("button.bumper.pressed.left", &getBumperLeftPressed);
  engine.registerBooleanInputSymbol("button.bumper.pressed.right", &getBumperRightPressed);
  engine.registerBooleanInputSymbol("button.head.pressed.front", &getButtonHeadFront);
  engine.registerBooleanInputSymbol("button.head.pressed.middle", &getButtonHeadMiddle);
  engine.registerBooleanInputSymbol("button.head.pressed.rear", &getButtonHeadRear);

  // deprecated?
  engine.registerBooleanInputSymbol("battery.isDischarging", &getBodyState().isDischarging);
  engine.registerBooleanInputSymbol("battery.isCharging", &getBodyState().isCharging);

  engine.registerBooleanOutputSymbol("audio.capture", &getAudioControl().capture);

}//end registerSymbols

SensorSymbols* SensorSymbols::theInstance = NULL;

SensorSymbols::~SensorSymbols()
{
}

void SensorSymbols::execute()
{

}//end execute

bool SensorSymbols::getBumperLeftPressed()
{
    return theInstance->getButtonData().isPressed[ButtonData::LeftFootLeft] || theInstance->getButtonData().isPressed[ButtonData::LeftFootRight];
  /*return (theInstance->getButtonData().eventCounter[ButtonData::LeftFootLeft] > 0) ||
         (theInstance->getButtonData().eventCounter[ButtonData::LeftFootRight] > 0);*/
}
bool SensorSymbols::getBumperRightPressed()
{
    return theInstance->getButtonData().isPressed[ButtonData::LeftFootLeft] || theInstance->getButtonData().isPressed[ButtonData::LeftFootRight];
    /*return (theInstance->getButtonData().eventCounter[ButtonData::LeftFootLeft] > 0) ||
    (theInstance->getButtonData().eventCounter[ButtonData::LeftFootRight] > 0);*/
}
bool SensorSymbols::getButtonHeadFront()
{
  return (theInstance->getButtonData().isPressed[ButtonData::HeadFront]);
}
bool SensorSymbols::getButtonHeadMiddle()
{
  return (theInstance->getButtonData().isPressed[ButtonData::HeadMiddle]);
}
bool SensorSymbols::getButtonHeadRear()
{
  return (theInstance->getButtonData().isPressed[ButtonData::HeadRear]);
}

double SensorSymbols::getFrameNumber()
{
  return (double) (theInstance->getFrameInfo().getFrameNumber());
}

double SensorSymbols::getFootStateTime()
{
  return (double) theInstance->getFrameInfo().getTimeSince(theInstance->getBodyState().foot_state_time);
}

int SensorSymbols::getFallDownState()
{
  return (int)(theInstance->getBodyState().fall_down_state);
}

// returns whether some data is received or not
double SensorSymbols::simplePassLeftSensor()
{
  double r = 2550.0;
  // return minimum measurement = closest object
  for(size_t i = 0; i < UltraSoundReceiveData::numOfUSEcho; i++)
  {
    if((theInstance->getUltraSoundReceiveData().dataLeft[i] * 1000) < r && theInstance->getUltraSoundReceiveData().dataLeft[i] > 0.2)
    {
      r = theInstance->getUltraSoundReceiveData().dataLeft[i] * 1000;
    }
  }
  return r;
} // end simpleLeftUSSensor

double SensorSymbols::simplePassRightSensor()
{
  double r = 2550.0;
  // return minimum measurement = closest object
  for(size_t i = 0; i < UltraSoundReceiveData::numOfUSEcho; i++)
  {
    if((theInstance->getUltraSoundReceiveData().dataRight[i] * 1000) < r && theInstance->getUltraSoundReceiveData().dataRight[i] > 0.2)
    {
      r = theInstance->getUltraSoundReceiveData().dataRight[i] * 1000;
    }
  }
  return r;
} // end simpleRightUSSensor

double SensorSymbols::getObstacleDistance()
{
  return theInstance->getObstacleModel().frontDistance;
}

double SensorSymbols::getObstacleDistanceLeft()
{
  return theInstance->getObstacleModel().leftDistance;
}

double SensorSymbols::getObstacleDistanceRight()
{
  return theInstance->getObstacleModel().rightDistance;
}

double SensorSymbols::getBlockedTime()
{
  return theInstance->getObstacleModel().blockedTime;
}

double SensorSymbols::getInertialSensorX()
{
  return Math::toDegrees(theInstance->getInertialSensorData().data.x);
}

double SensorSymbols::getInertialSensorY()
{
  return Math::toDegrees(theInstance->getInertialSensorData().data.y);
}

bool SensorSymbols::getCollisionArmLeft()
{
  return
    theInstance->getCollisionPercept().timeCollisionArmLeft > 0 &&
    theInstance->getFrameInfo().getTimeSince(theInstance->getCollisionPercept().timeCollisionArmLeft) < 100;
}

bool SensorSymbols::getCollisionArmRight()
{
  return
    theInstance->getCollisionPercept().timeCollisionArmRight > 0 &&
    theInstance->getFrameInfo().getTimeSince(theInstance->getCollisionPercept().timeCollisionArmRight) < 100;
}
<|endoftext|>


<commit_before>#include "ROOT/TDataFrame.hxx"
#include "Compression.h"
#include "TFile.h"
#include "TInterpreter.h"
#include "TRandom.h"

#include "gtest/gtest.h"

using namespace ROOT::Experimental;

namespace TEST_CATEGORY {

int DefineFunction()
{
   return 1;
}

struct DefineStruct {
   int operator()() { return 1; }
};

void FillTree(const char *filename, const char *treeName, int nevents = 0)
{
   TFile f(filename, "RECREATE");
   TTree t(treeName, treeName);
   t.SetAutoFlush(1); // yes, one event per cluster: to make MT more meaningful
   double b1;
   int b2;
   t.Branch("b1", &b1);
   t.Branch("b2", &b2);
   for (int i = 0; i < nevents; ++i) {
      b1 = i;
      b2 = i * i;
      t.Fill();
   }
   t.Write();
   f.Close();
}
}

TEST(TEST_CATEGORY, CreateEmpty)
{
   TDataFrame tdf(10);
   auto c = tdf.Count();
   EXPECT_EQ(10U, *c);
}

TEST(TEST_CATEGORY, CreateZeroEntries)
{
   TDataFrame tdf(0);
   auto c = tdf.Count();
   EXPECT_EQ(0U, *c);
}

TEST(TEST_CATEGORY, CreateZeroEntriesWithBranches)
{
   auto filename = "dataframe_simple_0.root";
   auto treename = "t";
#ifndef testTDF_simple_0_CREATED
#define testTDF_simple_0_CREATED
   TEST_CATEGORY::FillTree(filename, treename);
#endif
   TDataFrame tdf(treename, filename);
   auto c = tdf.Count();
   auto m = tdf.Mean("b1");
   EXPECT_EQ(0U, *c);
   EXPECT_EQ(0., *m);
}

TEST(TEST_CATEGORY, BuildWithTDirectory)
{
   auto filename = "dataframe_simple_1.root";
   auto treename = "t";
#ifndef testTDF_simple_1_CREATED
#define testTDF_simple_1_CREATED
   TEST_CATEGORY::FillTree(filename, treename, 50);
#endif
   TFile f(filename);
   TDataFrame tdf(treename, &f);
   auto c = tdf.Count();
   EXPECT_EQ(50U, *c);
}

// Jitting of column types
TEST(TEST_CATEGORY, TypeGuessing)
{
   auto filename = "dataframe_simple_2.root";
   auto treename = "t";
#ifndef testTDF_simple_2_CREATED
#define testTDF_simple_2_CREATED
   TEST_CATEGORY::FillTree(filename, treename, 50);
#endif
   TDataFrame tdf(treename, filename, {"b1"});
   auto hcompiled = tdf.Histo1D<double>();
   auto hjitted = tdf.Histo1D();
   EXPECT_EQ(50, hcompiled->GetEntries());
   EXPECT_EQ(50, hjitted->GetEntries());
   EXPECT_DOUBLE_EQ(hcompiled->GetMean(), hjitted->GetMean());
}

// Define

TEST(TEST_CATEGORY, Define_lambda)
{
   TDataFrame tdf(10);
   auto d = tdf.Define("i", []() { return 1; });
   auto m = d.Mean("i");
   EXPECT_DOUBLE_EQ(1., *m);
}

TEST(TEST_CATEGORY, Define_function)
{
   TDataFrame tdf(10);
   auto d = tdf.Define("i", TEST_CATEGORY::DefineFunction);
   auto m = d.Mean("i");
   EXPECT_DOUBLE_EQ(1., *m);
}

TEST(TEST_CATEGORY, Define_functor)
{
   TDataFrame tdf(10);
   TEST_CATEGORY::DefineStruct def;
   auto d = tdf.Define("i", def);
   auto m = d.Mean("i");
   EXPECT_DOUBLE_EQ(1., *m);
}

TEST(TEST_CATEGORY, Define_jitted)
{
   TDataFrame tdf(10);
   auto d = tdf.Define("i", "1");
   auto m = d.Mean("i");
   EXPECT_DOUBLE_EQ(1., *m);
}

TEST(TEST_CATEGORY, Define_jitted_complex)
{
// The test can be run in sequential and MT mode.
#ifndef RNDM_GEN_CREATED
#define RNDM_GEN_CREATED
   gInterpreter->ProcessLine("TRandom r;");
#endif
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("i", "r.Uniform(0.,8.)");
   auto m = d.Max("i");
   EXPECT_EQ(7.867497533559811628, *m);
}

// Define + Filters
TEST(TEST_CATEGORY, Define_Filter)
{
   TRandom r(1);
   TDataFrame tdf(50);
   auto d = tdf.Define("r", [&r]() { return r.Uniform(0., 8.); });
   auto df = d.Filter([](double x) { return x > 5; }, {"r"});
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_Filter_jitted)
{
   TRandom r(1);
   TDataFrame tdf(50);
   auto d = tdf.Define("r", [&r]() { return r.Uniform(0., 8.); });
   auto df = d.Filter("r>5");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_Filter_named)
{
   TRandom r(1);
   TDataFrame tdf(50);
   auto d = tdf.Define("r", [&r]() { return r.Uniform(0., 8.); });
   auto df = d.Filter([](double x) { return x > 5; }, {"r"}, "myFilter");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_Filter_named_jitted)
{
   TRandom r(1);
   TDataFrame tdf(50);
   auto d = tdf.Define("r", [&r]() { return r.Uniform(0., 8.); });
   auto df = d.Filter("r>5", "myFilter");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

// jitted Define + Filters
TEST(TEST_CATEGORY, Define_jitted_Filter)
{
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("r", "r.Uniform(0.,8.)");
   auto df = d.Filter([](double x) { return x > 5; }, {"r"});
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_jitted_Filter_jitted)
{
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("r", "r.Uniform(0.,8.)");
   auto df = d.Filter("r>5");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_jitted_Filter_named)
{
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("r", "r.Uniform(0.,8.)");
   auto df = d.Filter([](double x) { return x > 5; }, {"r"}, "myFilter");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_jitted_Filter_named_jitted)
{
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("r", "r.Uniform(0.,8.)");
   auto df = d.Filter("r>5", "myFilter");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Snapshot_update)
{
   using SnapshotOptions = ROOT::Experimental::TDF::SnapshotOptions;

   SnapshotOptions opts;

   opts.fMode = "UPDATE";

   TDataFrame tdf1(1000);
   auto s1 = tdf1.Define("one", []() { return 1.0; })
               .Snapshot<double>("mytree1", "snapshot_test_update.root", {"one"});

   EXPECT_EQ(1000U, *s1.Count());

   EXPECT_EQ(1.0, *s1.Min("one"));
   EXPECT_EQ(1.0, *s1.Max("one"));
   EXPECT_EQ(1.0, *s1.Mean("one"));

   TDataFrame tdf2(1000);
   auto s2 = tdf2.Define("two", []() { return 2.0; })
               .Snapshot<double>("mytree2", "snapshot_test_update.root", {"two"}, opts);

   EXPECT_EQ(1000U, *s2.Count());

   EXPECT_EQ(2.0, *s2.Min("two"));
   EXPECT_EQ(2.0, *s2.Max("two"));
   EXPECT_EQ(2.0, *s2.Mean("two"));

   TFile *f = TFile::Open("snapshot_test_update.root", "READ");
   auto mytree1 = (TTree*) f->Get("mytree1");
   auto mytree2 = (TTree*) f->Get("mytree2");

   EXPECT_NE(nullptr, mytree1);
   EXPECT_NE(nullptr, mytree2);

   f->Close();
   delete f;
}

TEST(TEST_CATEGORY, Snapshot_action_with_options)
{
   using SnapshotOptions = ROOT::Experimental::TDF::SnapshotOptions;

   SnapshotOptions opts;
   opts.fAutoFlush = 10;
   opts.fMode = "RECREATE";

   for (auto algorithm : { ROOT::kZLIB, ROOT::kLZMA, ROOT::kLZ4 }) {
      TDataFrame tdf(1000);

      opts.fCompressionLevel = 6;
      opts.fCompressionAlgorithm = algorithm;

      auto s = tdf.Define("one", []() { return 1.0; })
                  .Snapshot<double>("mytree", "snapshot_test_opts.root", {"one"}, opts);

      EXPECT_EQ(1000U, *s.Count());
      EXPECT_EQ(1.0, *s.Min("one"));
      EXPECT_EQ(1.0, *s.Max("one"));
      EXPECT_EQ(1.0, *s.Mean("one"));

      TFile *f = TFile::Open("snapshot_test_opts.root", "READ");

      EXPECT_EQ(algorithm, f->GetCompressionAlgorithm());
      EXPECT_EQ(6, f->GetCompressionLevel());

      f->Close();
      delete f;
   }
}

// This tests the interface but we need to run it both w/ and w/o implicit mt
#ifdef R__USE_IMT
TEST(TEST_CATEGORY, GetNSlots)
{
   EXPECT_EQ(NSLOTS, ROOT::Internal::TDF::GetNSlots());
}
#endif
<commit_msg>[TDF] Add gtest for `DefineSlot` transformation<commit_after>#include "ROOT/TDataFrame.hxx"
#include "Compression.h"
#include "TFile.h"
#include "TInterpreter.h"
#include "TRandom.h"

#include "gtest/gtest.h"

using namespace ROOT::Experimental;

namespace TEST_CATEGORY {

int DefineFunction()
{
   return 1;
}

struct DefineStruct {
   int operator()() { return 1; }
};

void FillTree(const char *filename, const char *treeName, int nevents = 0)
{
   TFile f(filename, "RECREATE");
   TTree t(treeName, treeName);
   t.SetAutoFlush(1); // yes, one event per cluster: to make MT more meaningful
   double b1;
   int b2;
   t.Branch("b1", &b1);
   t.Branch("b2", &b2);
   for (int i = 0; i < nevents; ++i) {
      b1 = i;
      b2 = i * i;
      t.Fill();
   }
   t.Write();
   f.Close();
}
}

TEST(TEST_CATEGORY, CreateEmpty)
{
   TDataFrame tdf(10);
   auto c = tdf.Count();
   EXPECT_EQ(10U, *c);
}

TEST(TEST_CATEGORY, CreateZeroEntries)
{
   TDataFrame tdf(0);
   auto c = tdf.Count();
   EXPECT_EQ(0U, *c);
}

TEST(TEST_CATEGORY, CreateZeroEntriesWithBranches)
{
   auto filename = "dataframe_simple_0.root";
   auto treename = "t";
#ifndef testTDF_simple_0_CREATED
#define testTDF_simple_0_CREATED
   TEST_CATEGORY::FillTree(filename, treename);
#endif
   TDataFrame tdf(treename, filename);
   auto c = tdf.Count();
   auto m = tdf.Mean("b1");
   EXPECT_EQ(0U, *c);
   EXPECT_EQ(0., *m);
}

TEST(TEST_CATEGORY, BuildWithTDirectory)
{
   auto filename = "dataframe_simple_1.root";
   auto treename = "t";
#ifndef testTDF_simple_1_CREATED
#define testTDF_simple_1_CREATED
   TEST_CATEGORY::FillTree(filename, treename, 50);
#endif
   TFile f(filename);
   TDataFrame tdf(treename, &f);
   auto c = tdf.Count();
   EXPECT_EQ(50U, *c);
}

// Jitting of column types
TEST(TEST_CATEGORY, TypeGuessing)
{
   auto filename = "dataframe_simple_2.root";
   auto treename = "t";
#ifndef testTDF_simple_2_CREATED
#define testTDF_simple_2_CREATED
   TEST_CATEGORY::FillTree(filename, treename, 50);
#endif
   TDataFrame tdf(treename, filename, {"b1"});
   auto hcompiled = tdf.Histo1D<double>();
   auto hjitted = tdf.Histo1D();
   EXPECT_EQ(50, hcompiled->GetEntries());
   EXPECT_EQ(50, hjitted->GetEntries());
   EXPECT_DOUBLE_EQ(hcompiled->GetMean(), hjitted->GetMean());
}

// Define

TEST(TEST_CATEGORY, Define_lambda)
{
   TDataFrame tdf(10);
   auto d = tdf.Define("i", []() { return 1; });
   auto m = d.Mean("i");
   EXPECT_DOUBLE_EQ(1., *m);
}

TEST(TEST_CATEGORY, Define_function)
{
   TDataFrame tdf(10);
   auto d = tdf.Define("i", TEST_CATEGORY::DefineFunction);
   auto m = d.Mean("i");
   EXPECT_DOUBLE_EQ(1., *m);
}

TEST(TEST_CATEGORY, Define_functor)
{
   TDataFrame tdf(10);
   TEST_CATEGORY::DefineStruct def;
   auto d = tdf.Define("i", def);
   auto m = d.Mean("i");
   EXPECT_DOUBLE_EQ(1., *m);
}

TEST(TEST_CATEGORY, Define_jitted)
{
   TDataFrame tdf(10);
   auto d = tdf.Define("i", "1");
   auto m = d.Mean("i");
   EXPECT_DOUBLE_EQ(1., *m);
}

TEST(TEST_CATEGORY, Define_jitted_complex)
{
// The test can be run in sequential and MT mode.
#ifndef RNDM_GEN_CREATED
#define RNDM_GEN_CREATED
   gInterpreter->ProcessLine("TRandom r;");
#endif
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("i", "r.Uniform(0.,8.)");
   auto m = d.Max("i");
   EXPECT_EQ(7.867497533559811628, *m);
}

// Define + Filters
TEST(TEST_CATEGORY, Define_Filter)
{
   TRandom r(1);
   TDataFrame tdf(50);
   auto d = tdf.Define("r", [&r]() { return r.Uniform(0., 8.); });
   auto df = d.Filter([](double x) { return x > 5; }, {"r"});
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_Filter_jitted)
{
   TRandom r(1);
   TDataFrame tdf(50);
   auto d = tdf.Define("r", [&r]() { return r.Uniform(0., 8.); });
   auto df = d.Filter("r>5");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_Filter_named)
{
   TRandom r(1);
   TDataFrame tdf(50);
   auto d = tdf.Define("r", [&r]() { return r.Uniform(0., 8.); });
   auto df = d.Filter([](double x) { return x > 5; }, {"r"}, "myFilter");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_Filter_named_jitted)
{
   TRandom r(1);
   TDataFrame tdf(50);
   auto d = tdf.Define("r", [&r]() { return r.Uniform(0., 8.); });
   auto df = d.Filter("r>5", "myFilter");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

// jitted Define + Filters
TEST(TEST_CATEGORY, Define_jitted_Filter)
{
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("r", "r.Uniform(0.,8.)");
   auto df = d.Filter([](double x) { return x > 5; }, {"r"});
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_jitted_Filter_jitted)
{
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("r", "r.Uniform(0.,8.)");
   auto df = d.Filter("r>5");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_jitted_Filter_named)
{
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("r", "r.Uniform(0.,8.)");
   auto df = d.Filter([](double x) { return x > 5; }, {"r"}, "myFilter");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, Define_jitted_Filter_named_jitted)
{
   gInterpreter->ProcessLine("r.SetSeed(1);");
   TDataFrame tdf(50);
   auto d = tdf.Define("r", "r.Uniform(0.,8.)");
   auto df = d.Filter("r>5", "myFilter");
   auto m = df.Max("r");
   EXPECT_EQ(7.867497533559811628, *m);
}

TEST(TEST_CATEGORY, DefineSlot)
{
   std::array<int, NSLOTS> values;
   for (auto i = 0u; i < NSLOTS; ++i)
      values[i] = i;
   TDataFrame df(NSLOTS);
   auto ddf = df.DefineSlot("s", [values](unsigned int slot) { return values[slot]; });
   auto m = ddf.Max("s");
   EXPECT_EQ(*m, NSLOTS-1); // no matter the order of processing, the higher slot number is always taken at least once
}

TEST(TEST_CATEGORY, Snapshot_update)
{
   using SnapshotOptions = ROOT::Experimental::TDF::SnapshotOptions;

   SnapshotOptions opts;

   opts.fMode = "UPDATE";

   TDataFrame tdf1(1000);
   auto s1 = tdf1.Define("one", []() { return 1.0; })
               .Snapshot<double>("mytree1", "snapshot_test_update.root", {"one"});

   EXPECT_EQ(1000U, *s1.Count());

   EXPECT_EQ(1.0, *s1.Min("one"));
   EXPECT_EQ(1.0, *s1.Max("one"));
   EXPECT_EQ(1.0, *s1.Mean("one"));

   TDataFrame tdf2(1000);
   auto s2 = tdf2.Define("two", []() { return 2.0; })
               .Snapshot<double>("mytree2", "snapshot_test_update.root", {"two"}, opts);

   EXPECT_EQ(1000U, *s2.Count());

   EXPECT_EQ(2.0, *s2.Min("two"));
   EXPECT_EQ(2.0, *s2.Max("two"));
   EXPECT_EQ(2.0, *s2.Mean("two"));

   TFile *f = TFile::Open("snapshot_test_update.root", "READ");
   auto mytree1 = (TTree*) f->Get("mytree1");
   auto mytree2 = (TTree*) f->Get("mytree2");

   EXPECT_NE(nullptr, mytree1);
   EXPECT_NE(nullptr, mytree2);

   f->Close();
   delete f;
}

TEST(TEST_CATEGORY, Snapshot_action_with_options)
{
   using SnapshotOptions = ROOT::Experimental::TDF::SnapshotOptions;

   SnapshotOptions opts;
   opts.fAutoFlush = 10;
   opts.fMode = "RECREATE";

   for (auto algorithm : { ROOT::kZLIB, ROOT::kLZMA, ROOT::kLZ4 }) {
      TDataFrame tdf(1000);

      opts.fCompressionLevel = 6;
      opts.fCompressionAlgorithm = algorithm;

      auto s = tdf.Define("one", []() { return 1.0; })
                  .Snapshot<double>("mytree", "snapshot_test_opts.root", {"one"}, opts);

      EXPECT_EQ(1000U, *s.Count());
      EXPECT_EQ(1.0, *s.Min("one"));
      EXPECT_EQ(1.0, *s.Max("one"));
      EXPECT_EQ(1.0, *s.Mean("one"));

      TFile *f = TFile::Open("snapshot_test_opts.root", "READ");

      EXPECT_EQ(algorithm, f->GetCompressionAlgorithm());
      EXPECT_EQ(6, f->GetCompressionLevel());

      f->Close();
      delete f;
   }
}

// This tests the interface but we need to run it both w/ and w/o implicit mt
#ifdef R__USE_IMT
TEST(TEST_CATEGORY, GetNSlots)
{
   EXPECT_EQ(NSLOTS, ROOT::Internal::TDF::GetNSlots());
}
#endif
<|endoftext|>


<commit_before>//=============================================================================
// ■ audio.cpp
//-----------------------------------------------------------------------------
//   提供声音支持。
//=============================================================================

#include "global.hpp"

namespace Audio {
	//-------------------------------------------------------------------------
	// ● 全局变量
	//-------------------------------------------------------------------------
	const size_t vf_buffer_size = 4096;
	PaStream* wave_stream;
	struct callback_data data;
	struct active_sound* active_sounds[16] = {NULL};
	size_t active_sound_count = 0;
	// 为了避免反复计算，将正弦值存储在这里。其分布为
	// [0] = sin 0
	// [64] = sin ⅛π
	// [128] = sin ¼π
	// [192] = sin ⅜π
	float sine_table[256];
	const size_t sine_table_size = ARRAY_SIZE(sine_table);
	//-------------------------------------------------------------------------
	// ● 初始化
	//-------------------------------------------------------------------------
	void init() {
		ensure_no_error(Pa_Initialize());
		populate_sine_table();
		data.type = 0;
		// 44100Hz
		data.sample_rate = 44100.0d;
		ensure_no_error(Pa_OpenDefaultStream(
			&wave_stream,
			// 无声输入 - 立体声输出、32位浮点数
			0, 2, paFloat32,
			data.sample_rate,
			// 256格缓冲区
			256,
			play_callback, &data
		));
		ensure_no_error(Pa_StartStream(wave_stream));
	}
	//-------------------------------------------------------------------------
	// ● 释放
	//-------------------------------------------------------------------------
	void wobuzhidaozhegefangfayinggaijiaoshenmemingzi() {
		ensure_no_error(Pa_StopStream(wave_stream));
		ensure_no_error(Pa_CloseStream(wave_stream));
		Pa_Terminate();
	}
	//-------------------------------------------------------------------------
	// ● 统一处理错误
	//-------------------------------------------------------------------------
	void ensure_no_error(PaError err) {
		if (err >= 0) return;
		Pa_Terminate();
		rb_raise(
			rb_eRuntimeError,
			"PortAudio error %d: %s",
			err, Pa_GetErrorText(err)
		);
	}
	//-------------------------------------------------------------------------
	// ● 内部使用的回调函数
	//-------------------------------------------------------------------------
	int play_callback(
		const void* input_buffer UNUSED,
		void* output_buffer,
		unsigned long frame_count,
		const PaStreamCallbackTimeInfo* time_info UNUSED,
		PaStreamCallbackFlags status_flags UNUSED,
		void* user_data
	) {
		float* output = (float*) output_buffer;
		struct callback_data* data = (struct callback_data*) user_data;
		// Magic. 吔屎啦PortAudio！
		#define FEED_AUDIO_DATA(value) do { \
			*output++ = (value); \
			*output++ = (value); \
			frame_count--; \
		} while (false)
		while (frame_count > 0) switch (data->type) {
			case 0:
				FEED_AUDIO_DATA(.0f);
				break;
			case 1:
				get_next_triangle_value(&data->data.triangle);
				FEED_AUDIO_DATA(data->data.triangle.value);
				break;
			case 2:
			default:
				get_next_sine_value(&data->data.sine);
				FEED_AUDIO_DATA(data->data.sine.value);
				break;
			case 3:
				*((float*) 0) = .0f; // 音频就是爆炸！
				FEED_AUDIO_DATA(.0f);
				break;
			case 4:
				FEED_AUDIO_DATA((float) (
					(double) rand() / (double) RAND_MAX * 2.0d - 1.0d
				));
				break;
		}
		#undef FEED_AUDIO_DATA
		return paContinue;
	}
	//-------------------------------------------------------------------------
	// ● 停止播放
	//-------------------------------------------------------------------------
	void stop() {
		data.type = 0;
	}
	//-------------------------------------------------------------------------
	// ● 播放三角波
	//    freq : 频率（Hz）
	//-------------------------------------------------------------------------
	void play_triangle(float freq) {
		data.data.triangle.value = -1.0f;
		data.data.triangle.delta = 2.0f / ((float) data.sample_rate / freq / 2);
		data.type = 1;
	}
	//-------------------------------------------------------------------------
	// ● 计算下一个值使输出呈三角波形
	//-------------------------------------------------------------------------
	void get_next_triangle_value(struct triangle_data* data) {
		data->value += data->delta;
		if (data->value > 1.0f) {
			data->value = 2.0f - data->value;
			data->delta = -data->delta;
		} else if (data->value < -1.0f) {
			data->value = -2.0f - data->value;
			data->delta = -data->delta;
		}
	}
	//-------------------------------------------------------------------------
	// ● 播放正弦波
	//    freq : 频率（Hz）
	//-------------------------------------------------------------------------
	void play_sine(float freq) {
		data.data.sine.index = .0f;
		data.data.sine.index_delta =
			sine_table_size / ((float) data.sample_rate / 4 / freq);
		data.data.sine.minus = false;
		data.type = 2;
	}
	//-------------------------------------------------------------------------
	// ● 向正弦表中填充数据
	//-------------------------------------------------------------------------
	void populate_sine_table() {
		float k = 0.5f / (float) sine_table_size * Util::PIf;
		for (size_t i = 0; i < sine_table_size; i++) sine_table[i] = sin(i * k);
	}
	//-------------------------------------------------------------------------
	// ● 计算正弦函数的下一值
	//-------------------------------------------------------------------------
	void get_next_sine_value(struct sine_data* data) {
		data->index += data->index_delta;
		if (data->index > (float) sine_table_size) {
			data->index = sine_table_size * 2.0f - data->index;
			data->index_delta = -data->index_delta;
		} else if (data->index < 0) {
			data->index = -data->index;
			data->index_delta = -data->index_delta;
			data->minus = !data->minus;
		}
		data->value = sine_table[(size_t) (int) data->index];
		if (data->minus) data->value = -data->value;
	}
	//-------------------------------------------------------------------------
	// ● 播放声音
	//-------------------------------------------------------------------------
	void play_sound(const char* filename) {
		compact_active_sounds_array();
log("play sound %s", filename);
		struct active_sound* sound = new struct active_sound;
		sound->stream = NULL;
		// sound->file
		sound->file = fopen(filename, "rb");
		if (!sound->file) {
			delete sound;
			rb_raise(rb_eIOError, "can't open this file: %s", filename);
		}
		// sound->vf
		if (ov_open_callbacks(
			sound->file, &sound->vf,
			NULL, 0, OV_CALLBACKS_NOCLOSE
		) < 0) {
			delete sound;
			fclose(sound->file);
			rb_raise(rb_eRuntimeError, "can't open ogg vorbis file: %s", filename);
		}
		// sound->stream
		ensure_no_error(Pa_OpenDefaultStream(
			&sound->stream, 0, 2, paFloat32, 44100,
			256, play_sound_callback, sound
		));
		// sound->*_head
		sound->play_head = 0;
		sound->load_head = 0;
		// etc
		sound->eof = false;
		active_sounds[active_sound_count] = sound;
		active_sound_count++;
		decode_vorbis(sound);
		// sound->decode_thread
		sound->decode_thread = new thread(decode_vorbis_thread, sound);
		ensure_no_error(Pa_StartStream(sound->stream));
	}
	//-------------------------------------------------------------------------
	// ● 扔掉active_sounds中已经播放完的条目
	//-------------------------------------------------------------------------
	void compact_active_sounds_array() {
		size_t size = ARRAY_SIZE(active_sounds);
		for (size_t i = 0; i < size; i++) {
			if (!active_sounds[i]) continue;
			PaError active = Pa_IsStreamActive(active_sounds[i]->stream);
			ensure_no_error(active);
			if (!active) {
				Pa_CloseStream(active_sounds[i]->stream);
				ov_clear(&active_sounds[i]->vf);
				fclose(active_sounds[i]->file);
				delete active_sounds[i]->decode_thread;
				delete active_sounds[i];
				active_sounds[i] = NULL;
			}
		}
	}
	//-------------------------------------------------------------------------
	// ● 播放声音的回调函数
	//-------------------------------------------------------------------------
	int play_sound_callback(
		const void* input_buffer UNUSED,
		void* output_buffer,
		unsigned long frame_count,
		const PaStreamCallbackTimeInfo* time_info UNUSED,
		PaStreamCallbackFlags status_flags UNUSED,
		void* user_data
	) {
		float* output = (float*) output_buffer;
		struct active_sound* sound = (struct active_sound*) user_data;
		while (frame_count > 0) {
			if (sound->play_head < sound->load_head) {
				size_t index = sound->play_head % vf_buffer_size;
//log("%f %f", sound->vf_buffer[0][index], sound->vf_buffer[1][index]);
				*output++ = sound->vf_buffer[0][index];
				*output++ = sound->vf_buffer[1][index];
				sound->play_head++;
			} else {
				TWICE *output++ = .0f;
			}
			frame_count--;
		}
		return sound->eof ? paComplete : paContinue;
	}
	//-------------------------------------------------------------------------
	// ● 解码文件来填active_sound结构中的缓冲区
	//-------------------------------------------------------------------------
	void decode_vorbis(struct active_sound* sound) {
		while (sound->load_head - sound->play_head < vf_buffer_size) {
			// After ov_read_float(), tmp_buffer will be changed.
			float** tmp_buffer;
			long ret = ov_read_float(
				&sound->vf,
				&tmp_buffer,
				vf_buffer_size,
				&sound->bitstream
			);
			if (ret > 0) {
				for (long i = 0; i < ret; i++) {
					size_t j = (sound->load_head + i) % vf_buffer_size;
					sound->vf_buffer[0][j] = tmp_buffer[0][i];
					sound->vf_buffer[1][j] = tmp_buffer[1][i];
				}
				sound->load_head += ret;
			} else if (ret == 0) {
				while (sound->load_head - sound->play_head < vf_buffer_size) {
					size_t j = sound->load_head % vf_buffer_size;
					sound->vf_buffer[0][j] = .0f;
					sound->vf_buffer[1][j] = .0f;
					sound->load_head++;
				}
				sound->eof = true;
				break;
			} else if (ret == OV_EBADLINK) {
				rb_raise(rb_eIOError, "bad vorbis data (OV_EBADLINK)");
			} else if (ret == OV_EINVAL) {
				rb_raise(rb_eIOError, "bad vorbis data (OV_EINVAL)");
			}
			// We must not free(tmp_buffer). It isn't ours.
		}
	}
	//-------------------------------------------------------------------------
	// ● 解码线程函数
	//-------------------------------------------------------------------------
	void decode_vorbis_thread(struct active_sound* sound) {
		while (!sound->eof) {
			decode_vorbis(sound);
		}
	}
}
<commit_msg>I'm multi-threaded now!<commit_after>//=============================================================================
// ■ audio.cpp
//-----------------------------------------------------------------------------
//   提供声音支持。
//=============================================================================

#include "global.hpp"

namespace Audio {
	//-------------------------------------------------------------------------
	// ● 全局变量
	//-------------------------------------------------------------------------
	const size_t vf_buffer_size = 4096;
	PaStream* wave_stream;
	struct callback_data data;
	struct active_sound* active_sounds[16] = {NULL};
	size_t active_sound_count = 0;
	// 为了避免反复计算，将正弦值存储在这里。其分布为
	// [0] = sin 0
	// [64] = sin ⅛π
	// [128] = sin ¼π
	// [192] = sin ⅜π
	float sine_table[256];
	const size_t sine_table_size = ARRAY_SIZE(sine_table);
	//-------------------------------------------------------------------------
	// ● 初始化
	//-------------------------------------------------------------------------
	void init() {
		ensure_no_error(Pa_Initialize());
		populate_sine_table();
		data.type = 0;
		// 44100Hz
		data.sample_rate = 44100.0d;
		ensure_no_error(Pa_OpenDefaultStream(
			&wave_stream,
			// 无声输入 - 立体声输出、32位浮点数
			0, 2, paFloat32,
			data.sample_rate,
			// 256格缓冲区
			256,
			play_callback, &data
		));
		ensure_no_error(Pa_StartStream(wave_stream));
	}
	//-------------------------------------------------------------------------
	// ● 释放
	//-------------------------------------------------------------------------
	void wobuzhidaozhegefangfayinggaijiaoshenmemingzi() {
		ensure_no_error(Pa_StopStream(wave_stream));
		ensure_no_error(Pa_CloseStream(wave_stream));
		Pa_Terminate();
	}
	//-------------------------------------------------------------------------
	// ● 统一处理错误
	//-------------------------------------------------------------------------
	void ensure_no_error(PaError err) {
		if (err >= 0) return;
		Pa_Terminate();
		rb_raise(
			rb_eRuntimeError,
			"PortAudio error %d: %s",
			err, Pa_GetErrorText(err)
		);
	}
	//-------------------------------------------------------------------------
	// ● 内部使用的回调函数
	//-------------------------------------------------------------------------
	int play_callback(
		const void* input_buffer UNUSED,
		void* output_buffer,
		unsigned long frame_count,
		const PaStreamCallbackTimeInfo* time_info UNUSED,
		PaStreamCallbackFlags status_flags UNUSED,
		void* user_data
	) {
		float* output = (float*) output_buffer;
		struct callback_data* data = (struct callback_data*) user_data;
		// Magic. 吔屎啦PortAudio！
		#define FEED_AUDIO_DATA(value) do { \
			*output++ = (value); \
			*output++ = (value); \
			frame_count--; \
		} while (false)
		while (frame_count > 0) switch (data->type) {
			case 0:
				FEED_AUDIO_DATA(.0f);
				break;
			case 1:
				get_next_triangle_value(&data->data.triangle);
				FEED_AUDIO_DATA(data->data.triangle.value);
				break;
			case 2:
			default:
				get_next_sine_value(&data->data.sine);
				FEED_AUDIO_DATA(data->data.sine.value);
				break;
			case 3:
				*((float*) 0) = .0f; // 音频就是爆炸！
				FEED_AUDIO_DATA(.0f);
				break;
			case 4:
				FEED_AUDIO_DATA((float) (
					(double) rand() / (double) RAND_MAX * 2.0d - 1.0d
				));
				break;
		}
		#undef FEED_AUDIO_DATA
		return paContinue;
	}
	//-------------------------------------------------------------------------
	// ● 停止播放
	//-------------------------------------------------------------------------
	void stop() {
		data.type = 0;
	}
	//-------------------------------------------------------------------------
	// ● 播放三角波
	//    freq : 频率（Hz）
	//-------------------------------------------------------------------------
	void play_triangle(float freq) {
		data.data.triangle.value = -1.0f;
		data.data.triangle.delta = 2.0f / ((float) data.sample_rate / freq / 2);
		data.type = 1;
	}
	//-------------------------------------------------------------------------
	// ● 计算下一个值使输出呈三角波形
	//-------------------------------------------------------------------------
	void get_next_triangle_value(struct triangle_data* data) {
		data->value += data->delta;
		if (data->value > 1.0f) {
			data->value = 2.0f - data->value;
			data->delta = -data->delta;
		} else if (data->value < -1.0f) {
			data->value = -2.0f - data->value;
			data->delta = -data->delta;
		}
	}
	//-------------------------------------------------------------------------
	// ● 播放正弦波
	//    freq : 频率（Hz）
	//-------------------------------------------------------------------------
	void play_sine(float freq) {
		data.data.sine.index = .0f;
		data.data.sine.index_delta =
			sine_table_size / ((float) data.sample_rate / 4 / freq);
		data.data.sine.minus = false;
		data.type = 2;
	}
	//-------------------------------------------------------------------------
	// ● 向正弦表中填充数据
	//-------------------------------------------------------------------------
	void populate_sine_table() {
		float k = 0.5f / (float) sine_table_size * Util::PIf;
		for (size_t i = 0; i < sine_table_size; i++) sine_table[i] = sin(i * k);
	}
	//-------------------------------------------------------------------------
	// ● 计算正弦函数的下一值
	//-------------------------------------------------------------------------
	void get_next_sine_value(struct sine_data* data) {
		data->index += data->index_delta;
		if (data->index > (float) sine_table_size) {
			data->index = sine_table_size * 2.0f - data->index;
			data->index_delta = -data->index_delta;
		} else if (data->index < 0) {
			data->index = -data->index;
			data->index_delta = -data->index_delta;
			data->minus = !data->minus;
		}
		data->value = sine_table[(size_t) (int) data->index];
		if (data->minus) data->value = -data->value;
	}
	//-------------------------------------------------------------------------
	// ● 播放声音
	//-------------------------------------------------------------------------
	void play_sound(const char* filename) {
		compact_active_sounds_array();
		log("play sound %s", filename);
		struct active_sound* sound = new struct active_sound;
		sound->stream = NULL;
		// sound->file
		sound->file = fopen(filename, "rb");
		if (!sound->file) {
			delete sound;
			rb_raise(rb_eIOError, "can't open this file: %s", filename);
		}
		// sound->vf
		if (ov_open_callbacks(
			sound->file, &sound->vf,
			NULL, 0, OV_CALLBACKS_NOCLOSE
		) < 0) {
			delete sound;
			fclose(sound->file);
			rb_raise(rb_eRuntimeError, "can't open ogg vorbis file: %s", filename);
		}
		// sound->stream
		ensure_no_error(Pa_OpenDefaultStream(
			&sound->stream, 0, 2, paFloat32, 44100,
			256, play_sound_callback, sound
		));
		// sound->*_head
		sound->play_head = 0;
		sound->load_head = 0;
		// etc
		sound->eof = false;
		active_sounds[active_sound_count] = sound;
		active_sound_count++;
		decode_vorbis(sound);
		// sound->decode_thread
		sound->decode_thread = new thread(decode_vorbis_thread, sound);
		ensure_no_error(Pa_StartStream(sound->stream));
	}
	//-------------------------------------------------------------------------
	// ● 扔掉active_sounds中已经播放完的条目
	//-------------------------------------------------------------------------
	void compact_active_sounds_array() {
		size_t size = ARRAY_SIZE(active_sounds);
		for (size_t i = 0; i < size; i++) {
			if (!active_sounds[i]) continue;
			PaError active = Pa_IsStreamActive(active_sounds[i]->stream);
			ensure_no_error(active);
			if (!active) {
				Pa_CloseStream(active_sounds[i]->stream);
				ov_clear(&active_sounds[i]->vf);
				fclose(active_sounds[i]->file);
				active_sounds[i]->decode_thread->join();
				delete active_sounds[i]->decode_thread;
				delete active_sounds[i];
				active_sounds[i] = NULL;
			}
		}
	}
	//-------------------------------------------------------------------------
	// ● 播放声音的回调函数
	//-------------------------------------------------------------------------
	int play_sound_callback(
		const void* input_buffer UNUSED,
		void* output_buffer,
		unsigned long frame_count,
		const PaStreamCallbackTimeInfo* time_info UNUSED,
		PaStreamCallbackFlags status_flags UNUSED,
		void* user_data
	) {
		float* output = (float*) output_buffer;
		struct active_sound* sound = (struct active_sound*) user_data;
		while (frame_count > 0) {
			if (sound->play_head < sound->load_head) {
				size_t index = sound->play_head % vf_buffer_size;
				*output++ = sound->vf_buffer[0][index];
				*output++ = sound->vf_buffer[1][index];
				sound->play_head++;
			} else {
				TWICE *output++ = .0f;
			}
			frame_count--;
		}
		return sound->eof ? paComplete : paContinue;
	}
	//-------------------------------------------------------------------------
	// ● 解码文件来填active_sound结构中的缓冲区
	//-------------------------------------------------------------------------
	void decode_vorbis(struct active_sound* sound) {
		while (sound->load_head - sound->play_head < vf_buffer_size) {
			// After ov_read_float(), tmp_buffer will be changed.
			float** tmp_buffer;
			long ret = ov_read_float(
				&sound->vf,
				&tmp_buffer,
				vf_buffer_size,
				&sound->bitstream
			);
			if (ret > 0) {
				for (long i = 0; i < ret; i++) {
					size_t j = (sound->load_head + i) % vf_buffer_size;
					sound->vf_buffer[0][j] = tmp_buffer[0][i];
					sound->vf_buffer[1][j] = tmp_buffer[1][i];
				}
				sound->load_head += ret;
			} else if (ret == 0) {
				while (sound->load_head - sound->play_head < vf_buffer_size) {
					size_t j = sound->load_head % vf_buffer_size;
					sound->vf_buffer[0][j] = .0f;
					sound->vf_buffer[1][j] = .0f;
					sound->load_head++;
				}
				sound->eof = true;
				break;
			} else if (ret == OV_EBADLINK) {
				rb_raise(rb_eIOError, "bad vorbis data (OV_EBADLINK)");
			} else if (ret == OV_EINVAL) {
				rb_raise(rb_eIOError, "bad vorbis data (OV_EINVAL)");
			}
			// We must not free(tmp_buffer). It isn't ours.
		}
	}
	//-------------------------------------------------------------------------
	// ● 解码线程函数
	//-------------------------------------------------------------------------
	void decode_vorbis_thread(struct active_sound* sound) {
		while (!sound->eof) {
			decode_vorbis(sound);
		}
	}
}
<|endoftext|>