<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program 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 program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "Genre.h"

#include "Album.h"
#include "AlbumTrack.h"
#include "Artist.h"
#include "Media.h"
#include "database/SqliteQuery.h"

namespace medialibrary
{

const std::string Genre::Table::Name = "Genre";
const std::string Genre::Table::PrimaryKeyColumn = "id_genre";
int64_t Genre::* const Genre::Table::PrimaryKey = &Genre::m_id;
const std::string Genre::FtsTable::Name = "GenreFts";

Genre::Genre( MediaLibraryPtr ml, sqlite::Row& row )
    : m_ml( ml )
    , m_id( row.extract<decltype(m_id)>() )
    , m_name( row.extract<decltype(m_name)>() )
    , m_nbTracks( row.extract<decltype(m_nbTracks)>() )
    , m_nbPresentTracks( row.extract<decltype(m_nbPresentTracks)>() )
{
    assert( row.hasRemainingColumns() == false );
}

Genre::Genre( MediaLibraryPtr ml, std::string name )
    : m_ml( ml )
    , m_id( 0 )
    , m_name( std::move( name ) )
    , m_nbTracks( 0 )
    , m_nbPresentTracks( 0 )
{
}

int64_t Genre::id() const
{
    return m_id;
}

const std::string& Genre::name() const
{
    return m_name;
}

uint32_t Genre::nbTracks() const
{
    return m_nbTracks;
}

uint32_t Genre::nbPresentTracks() const
{
    return m_nbPresentTracks;
}

void Genre::updateCachedNbTracks( int increment )
{
    m_nbTracks += increment;
}

Query<IArtist> Genre::artists( const QueryParameters* params ) const
{
    std::string req = "FROM " + Artist::Table::Name + " a "
            "INNER JOIN " + AlbumTrack::Table::Name + " att ON att.artist_id = a.id_artist "
            "WHERE att.genre_id = ?";
    std::string groupAndOrderBy = "GROUP BY att.artist_id ORDER BY a.name";
    if ( params != nullptr )
    {
        if ( params->sort != SortingCriteria::Default && params->sort != SortingCriteria::Alpha )
            LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Alpha" );
        if ( params->desc == true )
            groupAndOrderBy += " DESC";
    }
    return make_query<Artist, IArtist>( m_ml, "a.*", std::move( req ),
                                        std::move( groupAndOrderBy ), m_id );
}

Query<IArtist> Genre::searchArtists( const std::string& pattern,
                                    const QueryParameters* params ) const
{
    return Artist::searchByGenre( m_ml, pattern, params, m_id );
}

Query<IMedia> Genre::tracks( TracksIncluded included,
                             const QueryParameters* params ) const
{
    return AlbumTrack::fromGenre( m_ml, m_id, included, params );
}

Query<IMedia> Genre::searchTracks( const std::string& pattern, const QueryParameters* params ) const
{
    return Media::searchGenreTracks( m_ml, pattern, m_id, params );
}

Query<IAlbum> Genre::albums( const QueryParameters* params ) const
{
    return Album::fromGenre( m_ml, m_id, params );
}

Query<IAlbum> Genre::searchAlbums( const std::string& pattern,
                                   const QueryParameters* params ) const
{
    return Album::searchFromGenre( m_ml, pattern, m_id, params );
}

const std::string&Genre::thumbnailMrl( ThumbnailSizeType sizeType ) const
{
    const auto t = thumbnail( sizeType );
    if ( t == nullptr )
        return Thumbnail::EmptyMrl;
    return t->mrl();
}

bool Genre::hasThumbnail( ThumbnailSizeType sizeType ) const
{
    if ( m_thumbnails[Thumbnail::SizeToInt( sizeType )] != nullptr )
        return true;
    return thumbnail( sizeType ) != nullptr;
}

bool Genre::shouldUpdateThumbnail( const Thumbnail& oldThumbnail )
{
    return oldThumbnail.isShared() == false;
}

bool Genre::setThumbnail( const std::string& mrl, ThumbnailSizeType sizeType,
                          bool takeOwnership )
{
    auto thumbnailIdx = Thumbnail::SizeToInt( sizeType );
    auto currentThumbnail = thumbnail( sizeType );
    auto newThumbnail = std::make_shared<Thumbnail>( m_ml, mrl,
                            Thumbnail::Origin::UserProvided, sizeType, false );
    currentThumbnail = Thumbnail::updateOrReplace( m_ml, currentThumbnail,
                                                   newThumbnail,
                                                   Genre::shouldUpdateThumbnail,
                                                   m_id, Thumbnail::EntityType::Genre );
    if ( currentThumbnail == nullptr )
        return false;
    m_thumbnails[thumbnailIdx] = std::move( currentThumbnail );
    if ( takeOwnership == true )
        m_thumbnails[thumbnailIdx]->relocate();
    return true;
}

std::shared_ptr<Thumbnail> Genre::thumbnail( ThumbnailSizeType sizeType ) const
{
    auto idx = Thumbnail::SizeToInt( sizeType );
    if ( m_thumbnails[idx] == nullptr )
    {
        auto thumbnail = Thumbnail::fetch( m_ml, Thumbnail::EntityType::Genre,
                                           m_id, sizeType );
        if ( thumbnail == nullptr )
            return nullptr;
        m_thumbnails[idx] = std::move( thumbnail );
    }
    return m_thumbnails[idx];
}

void Genre::createTable( sqlite::Connection* dbConn )
{
    const std::string reqs[] = {
        schema( Table::Name, Settings::DbModelVersion ),
        schema( FtsTable::Name, Settings::DbModelVersion ),
    };
    for ( const auto& req : reqs )
        sqlite::Tools::executeRequest( dbConn, req );
}

void Genre::createTriggers( sqlite::Connection* dbConn )
{
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::InsertFts, Settings::DbModelVersion ) );
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::DeleteFts, Settings::DbModelVersion ) );
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::UpdateOnNewTrack, Settings::DbModelVersion ) );
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::UpdateOnTrackDelete, Settings::DbModelVersion ) );
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::UpdateIsPresent, Settings::DbModelVersion ) );
}

std::string Genre::schema( const std::string& tableName, uint32_t dbModel )
{
    if ( tableName == FtsTable::Name )
    {
        return "CREATE VIRTUAL TABLE " + FtsTable::Name +
               " USING FTS3(name)";
    }
    assert( tableName == Table::Name );
    if ( dbModel < 30 )
    {
        return "CREATE TABLE " + Table::Name +
        "("
            "id_genre INTEGER PRIMARY KEY AUTOINCREMENT,"
            "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL,"
            "nb_tracks INTEGER NOT NULL DEFAULT 0"
        ")";
    }
    return "CREATE TABLE " + Table::Name +
    "("
        "id_genre INTEGER PRIMARY KEY AUTOINCREMENT,"
        "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL,"
        "nb_tracks INTEGER NOT NULL DEFAULT 0,"
        "is_present INTEGER NOT NULL DEFAULT 0 "
            "CHECK(is_present <= nb_tracks)"
    ")";
}

std::string Genre::trigger( Triggers trigger, uint32_t dbModel )
{
    switch ( trigger )
    {
        case Triggers::InsertFts:
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                   " AFTER INSERT ON " + Table::Name +
                   " BEGIN"
                        " INSERT INTO " + FtsTable::Name + "(rowid,name)"
                            " VALUES(new.id_genre, new.name);"
                   " END";
        case Triggers::DeleteFts:
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                   " BEFORE DELETE ON " + Table::Name +
                   " BEGIN"
                        " DELETE FROM " + FtsTable::Name +
                            " WHERE rowid = old.id_genre;"
                   " END";
        case Triggers::UpdateOnNewTrack:
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                    " AFTER INSERT ON " + AlbumTrack::Table::Name +
                    " WHEN new.genre_id IS NOT NULL"
                    " BEGIN"
                        " UPDATE " + Table::Name +
                            " SET nb_tracks = nb_tracks + 1,"
                                " is_present = is_present + 1"
                                " WHERE id_genre = new.genre_id;"
                    " END";
        case Triggers::UpdateOnTrackDelete:
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                   " AFTER DELETE ON " + AlbumTrack::Table::Name +
                   " WHEN old.genre_id IS NOT NULL"
                   " BEGIN"
                        " UPDATE " + Table::Name +
                            " SET nb_tracks = nb_tracks - 1,"
                                " is_present = is_present - 1"
                                " WHERE id_genre = old.genre_id;"
                        " DELETE FROM " + Table::Name +
                            " WHERE nb_tracks = 0;"
                   " END";
        case Triggers::UpdateIsPresent:
            assert( dbModel >= 30 );
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                   " AFTER UPDATE OF is_present ON " + Media::Table::Name +
                   " WHEN new.subtype = " +
                        std::to_string( static_cast<typename std::underlying_type<IMedia::SubType>::type>(
                                            IMedia::SubType::AlbumTrack ) ) +
                   " AND old.is_present != new.is_present"
                   " BEGIN"
                   " UPDATE " + Table::Name + " SET is_present = is_present + "
                       "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END) "
                       "WHERE id_genre = "
                           "(SELECT genre_id FROM " + AlbumTrack::Table::Name +
                               " WHERE media_id = new.id_media);"
                   " END";
        default:
            assert( !"Invalid trigger provided" );
    }
    return "<invalid request>";
}

std::string Genre::triggerName( Triggers trigger, uint32_t dbModel )
{
    UNUSED_IN_RELEASE( dbModel );

    switch ( trigger )
    {
        case Triggers::InsertFts:
            return "insert_genre_fts";
        case Triggers::DeleteFts:
            return "delete_genre_fts";
        case Triggers::UpdateOnNewTrack:
            return "update_genre_on_new_track";
        case Triggers::UpdateOnTrackDelete:
            return "update_genre_on_track_deleted";
        case Triggers::UpdateIsPresent:
            assert( dbModel >= 30 );
            return "genre_update_is_present";
        default:
            assert( !"Invalid trigger provided" );
    }
    return "<invalid request>";
}

bool Genre::checkDbModel(MediaLibraryPtr ml)
{
    if ( sqlite::Tools::checkTableSchema( ml->getConn(),
                                       schema( Table::Name, Settings::DbModelVersion ),
                                       Table::Name ) == false ||
           sqlite::Tools::checkTableSchema( ml->getConn(),
                                       schema( FtsTable::Name, Settings::DbModelVersion ),
                                       FtsTable::Name ) == false )
        return false;
    auto check = []( sqlite::Connection* dbConn, Triggers t ) {
        return sqlite::Tools::checkTriggerStatement( dbConn,
                                    trigger( t, Settings::DbModelVersion ),
                                    triggerName( t, Settings::DbModelVersion ) );
    };
    return check( ml->getConn(), Triggers::InsertFts ) &&
            check( ml->getConn(), Triggers::DeleteFts ) &&
            check( ml->getConn(), Triggers::UpdateOnNewTrack ) &&
            check( ml->getConn(), Triggers::UpdateOnTrackDelete ) &&
            check( ml->getConn(), Triggers::UpdateIsPresent );
}

std::shared_ptr<Genre> Genre::create( MediaLibraryPtr ml, std::string name )
{
    static const std::string req = "INSERT INTO " + Table::Name + "(name)"
            "VALUES(?)";
    auto self = std::make_shared<Genre>( ml, std::move( name ) );
    if ( insert( ml, self, req, self->m_name ) == false )
        return nullptr;
    return self;
}

std::shared_ptr<Genre> Genre::fromName( MediaLibraryPtr ml, const std::string& name )
{
    static const std::string req = "SELECT * FROM " + Table::Name + " WHERE name = ?";
    return fetch( ml, req, name );
}

Query<IGenre> Genre::search( MediaLibraryPtr ml, const std::string& name,
                             const QueryParameters* params )
{
    std::string req = "FROM " + Table::Name + " WHERE id_genre IN "
            "(SELECT rowid FROM " + FtsTable::Name + " "
            "WHERE name MATCH ?)";
    std::string orderBy = "ORDER BY name";
    if ( params != nullptr )
    {
        if ( params->sort != SortingCriteria::Default && params->sort != SortingCriteria::Alpha )
            LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Alpha" );
        if ( params->desc == true )
            orderBy += " DESC";
    }
    return make_query<Genre, IGenre>( ml, "*", std::move( req ),
                                      std::move( orderBy ),
                                      sqlite::Tools::sanitizePattern( name ) );
}

Query<IGenre> Genre::listAll( MediaLibraryPtr ml, const QueryParameters* params )
{
    std::string req = "FROM " + Table::Name;
    std::string orderBy = " ORDER BY name";
    if ( params != nullptr )
    {
        if ( params->sort != SortingCriteria::Default && params->sort != SortingCriteria::Alpha )
            LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Alpha" );
        if ( params->desc == true )
            orderBy += " DESC";
    }
    return make_query<Genre, IGenre>( ml, "*", std::move( req ),
                                      std::move( orderBy ) );
}

}
<commit_msg>Genre: Use enum_to_string<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program 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 program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "Genre.h"

#include "Album.h"
#include "AlbumTrack.h"
#include "Artist.h"
#include "Media.h"
#include "database/SqliteQuery.h"
#include "utils/Enums.h"

namespace medialibrary
{

const std::string Genre::Table::Name = "Genre";
const std::string Genre::Table::PrimaryKeyColumn = "id_genre";
int64_t Genre::* const Genre::Table::PrimaryKey = &Genre::m_id;
const std::string Genre::FtsTable::Name = "GenreFts";

Genre::Genre( MediaLibraryPtr ml, sqlite::Row& row )
    : m_ml( ml )
    , m_id( row.extract<decltype(m_id)>() )
    , m_name( row.extract<decltype(m_name)>() )
    , m_nbTracks( row.extract<decltype(m_nbTracks)>() )
    , m_nbPresentTracks( row.extract<decltype(m_nbPresentTracks)>() )
{
    assert( row.hasRemainingColumns() == false );
}

Genre::Genre( MediaLibraryPtr ml, std::string name )
    : m_ml( ml )
    , m_id( 0 )
    , m_name( std::move( name ) )
    , m_nbTracks( 0 )
    , m_nbPresentTracks( 0 )
{
}

int64_t Genre::id() const
{
    return m_id;
}

const std::string& Genre::name() const
{
    return m_name;
}

uint32_t Genre::nbTracks() const
{
    return m_nbTracks;
}

uint32_t Genre::nbPresentTracks() const
{
    return m_nbPresentTracks;
}

void Genre::updateCachedNbTracks( int increment )
{
    m_nbTracks += increment;
}

Query<IArtist> Genre::artists( const QueryParameters* params ) const
{
    std::string req = "FROM " + Artist::Table::Name + " a "
            "INNER JOIN " + AlbumTrack::Table::Name + " att ON att.artist_id = a.id_artist "
            "WHERE att.genre_id = ?";
    std::string groupAndOrderBy = "GROUP BY att.artist_id ORDER BY a.name";
    if ( params != nullptr )
    {
        if ( params->sort != SortingCriteria::Default && params->sort != SortingCriteria::Alpha )
            LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Alpha" );
        if ( params->desc == true )
            groupAndOrderBy += " DESC";
    }
    return make_query<Artist, IArtist>( m_ml, "a.*", std::move( req ),
                                        std::move( groupAndOrderBy ), m_id );
}

Query<IArtist> Genre::searchArtists( const std::string& pattern,
                                    const QueryParameters* params ) const
{
    return Artist::searchByGenre( m_ml, pattern, params, m_id );
}

Query<IMedia> Genre::tracks( TracksIncluded included,
                             const QueryParameters* params ) const
{
    return AlbumTrack::fromGenre( m_ml, m_id, included, params );
}

Query<IMedia> Genre::searchTracks( const std::string& pattern, const QueryParameters* params ) const
{
    return Media::searchGenreTracks( m_ml, pattern, m_id, params );
}

Query<IAlbum> Genre::albums( const QueryParameters* params ) const
{
    return Album::fromGenre( m_ml, m_id, params );
}

Query<IAlbum> Genre::searchAlbums( const std::string& pattern,
                                   const QueryParameters* params ) const
{
    return Album::searchFromGenre( m_ml, pattern, m_id, params );
}

const std::string&Genre::thumbnailMrl( ThumbnailSizeType sizeType ) const
{
    const auto t = thumbnail( sizeType );
    if ( t == nullptr )
        return Thumbnail::EmptyMrl;
    return t->mrl();
}

bool Genre::hasThumbnail( ThumbnailSizeType sizeType ) const
{
    if ( m_thumbnails[Thumbnail::SizeToInt( sizeType )] != nullptr )
        return true;
    return thumbnail( sizeType ) != nullptr;
}

bool Genre::shouldUpdateThumbnail( const Thumbnail& oldThumbnail )
{
    return oldThumbnail.isShared() == false;
}

bool Genre::setThumbnail( const std::string& mrl, ThumbnailSizeType sizeType,
                          bool takeOwnership )
{
    auto thumbnailIdx = Thumbnail::SizeToInt( sizeType );
    auto currentThumbnail = thumbnail( sizeType );
    auto newThumbnail = std::make_shared<Thumbnail>( m_ml, mrl,
                            Thumbnail::Origin::UserProvided, sizeType, false );
    currentThumbnail = Thumbnail::updateOrReplace( m_ml, currentThumbnail,
                                                   newThumbnail,
                                                   Genre::shouldUpdateThumbnail,
                                                   m_id, Thumbnail::EntityType::Genre );
    if ( currentThumbnail == nullptr )
        return false;
    m_thumbnails[thumbnailIdx] = std::move( currentThumbnail );
    if ( takeOwnership == true )
        m_thumbnails[thumbnailIdx]->relocate();
    return true;
}

std::shared_ptr<Thumbnail> Genre::thumbnail( ThumbnailSizeType sizeType ) const
{
    auto idx = Thumbnail::SizeToInt( sizeType );
    if ( m_thumbnails[idx] == nullptr )
    {
        auto thumbnail = Thumbnail::fetch( m_ml, Thumbnail::EntityType::Genre,
                                           m_id, sizeType );
        if ( thumbnail == nullptr )
            return nullptr;
        m_thumbnails[idx] = std::move( thumbnail );
    }
    return m_thumbnails[idx];
}

void Genre::createTable( sqlite::Connection* dbConn )
{
    const std::string reqs[] = {
        schema( Table::Name, Settings::DbModelVersion ),
        schema( FtsTable::Name, Settings::DbModelVersion ),
    };
    for ( const auto& req : reqs )
        sqlite::Tools::executeRequest( dbConn, req );
}

void Genre::createTriggers( sqlite::Connection* dbConn )
{
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::InsertFts, Settings::DbModelVersion ) );
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::DeleteFts, Settings::DbModelVersion ) );
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::UpdateOnNewTrack, Settings::DbModelVersion ) );
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::UpdateOnTrackDelete, Settings::DbModelVersion ) );
    sqlite::Tools::executeRequest( dbConn,
                                   trigger( Triggers::UpdateIsPresent, Settings::DbModelVersion ) );
}

std::string Genre::schema( const std::string& tableName, uint32_t dbModel )
{
    if ( tableName == FtsTable::Name )
    {
        return "CREATE VIRTUAL TABLE " + FtsTable::Name +
               " USING FTS3(name)";
    }
    assert( tableName == Table::Name );
    if ( dbModel < 30 )
    {
        return "CREATE TABLE " + Table::Name +
        "("
            "id_genre INTEGER PRIMARY KEY AUTOINCREMENT,"
            "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL,"
            "nb_tracks INTEGER NOT NULL DEFAULT 0"
        ")";
    }
    return "CREATE TABLE " + Table::Name +
    "("
        "id_genre INTEGER PRIMARY KEY AUTOINCREMENT,"
        "name TEXT COLLATE NOCASE UNIQUE ON CONFLICT FAIL,"
        "nb_tracks INTEGER NOT NULL DEFAULT 0,"
        "is_present INTEGER NOT NULL DEFAULT 0 "
            "CHECK(is_present <= nb_tracks)"
    ")";
}

std::string Genre::trigger( Triggers trigger, uint32_t dbModel )
{
    switch ( trigger )
    {
        case Triggers::InsertFts:
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                   " AFTER INSERT ON " + Table::Name +
                   " BEGIN"
                        " INSERT INTO " + FtsTable::Name + "(rowid,name)"
                            " VALUES(new.id_genre, new.name);"
                   " END";
        case Triggers::DeleteFts:
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                   " BEFORE DELETE ON " + Table::Name +
                   " BEGIN"
                        " DELETE FROM " + FtsTable::Name +
                            " WHERE rowid = old.id_genre;"
                   " END";
        case Triggers::UpdateOnNewTrack:
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                    " AFTER INSERT ON " + AlbumTrack::Table::Name +
                    " WHEN new.genre_id IS NOT NULL"
                    " BEGIN"
                        " UPDATE " + Table::Name +
                            " SET nb_tracks = nb_tracks + 1,"
                                " is_present = is_present + 1"
                                " WHERE id_genre = new.genre_id;"
                    " END";
        case Triggers::UpdateOnTrackDelete:
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                   " AFTER DELETE ON " + AlbumTrack::Table::Name +
                   " WHEN old.genre_id IS NOT NULL"
                   " BEGIN"
                        " UPDATE " + Table::Name +
                            " SET nb_tracks = nb_tracks - 1,"
                                " is_present = is_present - 1"
                                " WHERE id_genre = old.genre_id;"
                        " DELETE FROM " + Table::Name +
                            " WHERE nb_tracks = 0;"
                   " END";
        case Triggers::UpdateIsPresent:
            assert( dbModel >= 30 );
            return "CREATE TRIGGER " + triggerName( trigger, dbModel ) +
                   " AFTER UPDATE OF is_present ON " + Media::Table::Name +
                   " WHEN new.subtype = " +
                        utils::enum_to_string( IMedia::SubType::AlbumTrack ) +
                   " AND old.is_present != new.is_present"
                   " BEGIN"
                   " UPDATE " + Table::Name + " SET is_present = is_present + "
                       "(CASE new.is_present WHEN 0 THEN -1 ELSE 1 END) "
                       "WHERE id_genre = "
                           "(SELECT genre_id FROM " + AlbumTrack::Table::Name +
                               " WHERE media_id = new.id_media);"
                   " END";
        default:
            assert( !"Invalid trigger provided" );
    }
    return "<invalid request>";
}

std::string Genre::triggerName( Triggers trigger, uint32_t dbModel )
{
    UNUSED_IN_RELEASE( dbModel );

    switch ( trigger )
    {
        case Triggers::InsertFts:
            return "insert_genre_fts";
        case Triggers::DeleteFts:
            return "delete_genre_fts";
        case Triggers::UpdateOnNewTrack:
            return "update_genre_on_new_track";
        case Triggers::UpdateOnTrackDelete:
            return "update_genre_on_track_deleted";
        case Triggers::UpdateIsPresent:
            assert( dbModel >= 30 );
            return "genre_update_is_present";
        default:
            assert( !"Invalid trigger provided" );
    }
    return "<invalid request>";
}

bool Genre::checkDbModel(MediaLibraryPtr ml)
{
    if ( sqlite::Tools::checkTableSchema( ml->getConn(),
                                       schema( Table::Name, Settings::DbModelVersion ),
                                       Table::Name ) == false ||
           sqlite::Tools::checkTableSchema( ml->getConn(),
                                       schema( FtsTable::Name, Settings::DbModelVersion ),
                                       FtsTable::Name ) == false )
        return false;
    auto check = []( sqlite::Connection* dbConn, Triggers t ) {
        return sqlite::Tools::checkTriggerStatement( dbConn,
                                    trigger( t, Settings::DbModelVersion ),
                                    triggerName( t, Settings::DbModelVersion ) );
    };
    return check( ml->getConn(), Triggers::InsertFts ) &&
            check( ml->getConn(), Triggers::DeleteFts ) &&
            check( ml->getConn(), Triggers::UpdateOnNewTrack ) &&
            check( ml->getConn(), Triggers::UpdateOnTrackDelete ) &&
            check( ml->getConn(), Triggers::UpdateIsPresent );
}

std::shared_ptr<Genre> Genre::create( MediaLibraryPtr ml, std::string name )
{
    static const std::string req = "INSERT INTO " + Table::Name + "(name)"
            "VALUES(?)";
    auto self = std::make_shared<Genre>( ml, std::move( name ) );
    if ( insert( ml, self, req, self->m_name ) == false )
        return nullptr;
    return self;
}

std::shared_ptr<Genre> Genre::fromName( MediaLibraryPtr ml, const std::string& name )
{
    static const std::string req = "SELECT * FROM " + Table::Name + " WHERE name = ?";
    return fetch( ml, req, name );
}

Query<IGenre> Genre::search( MediaLibraryPtr ml, const std::string& name,
                             const QueryParameters* params )
{
    std::string req = "FROM " + Table::Name + " WHERE id_genre IN "
            "(SELECT rowid FROM " + FtsTable::Name + " "
            "WHERE name MATCH ?)";
    std::string orderBy = "ORDER BY name";
    if ( params != nullptr )
    {
        if ( params->sort != SortingCriteria::Default && params->sort != SortingCriteria::Alpha )
            LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Alpha" );
        if ( params->desc == true )
            orderBy += " DESC";
    }
    return make_query<Genre, IGenre>( ml, "*", std::move( req ),
                                      std::move( orderBy ),
                                      sqlite::Tools::sanitizePattern( name ) );
}

Query<IGenre> Genre::listAll( MediaLibraryPtr ml, const QueryParameters* params )
{
    std::string req = "FROM " + Table::Name;
    std::string orderBy = " ORDER BY name";
    if ( params != nullptr )
    {
        if ( params->sort != SortingCriteria::Default && params->sort != SortingCriteria::Alpha )
            LOG_WARN( "Unsupported sorting criteria, falling back to SortingCriteria::Alpha" );
        if ( params->desc == true )
            orderBy += " DESC";
    }
    return make_query<Genre, IGenre>( ml, "*", std::move( req ),
                                      std::move( orderBy ) );
}

}
<|endoftext|>


<commit_before>// This file was developed by Thomas Müller <thomas94@gmx.net>.
// It is published under the BSD-style license contained in the LICENSE.txt file.

#include "../include/Image.h"

#include <ImfChannelList.h>
#include <ImfInputFile.h>

#include <stb_image.h>

#include <array>
#include <iostream>

using namespace std;

namespace {
    bool endsWith(const string& a, const string& b) {
        return a.length() >= b.length() && a.compare(a.length() - b.length(), b.length(), b) == 0;
    }
}

Image::Image(const string& filename)
    : mName(filename) {
    if (endsWith(filename, ".exr")) {
        readExr(filename);
    } else {
        readStbi(filename);
    }
}

const GlTexture* Image::texture(const std::string& channelName) {
    auto iter = mTextures.find(channelName);
    if (iter != end(mTextures)) {
        return &iter->second;
    }

    const auto* chan = channel(channelName);
    if (!chan) {
        return nullptr;
    }

    mTextures.emplace(channelName, GlTexture{});
    auto& texture = mTextures.at(channelName);
    texture.setData(chan->data(), mSize, 1);

    return &texture;
}

void Image::readStbi(const std::string& filename) {
    // No exr image? Try our best using stbi
    cout << "Loading "s + filename + " via STBI." << endl;

    int numChannels;
    auto data = stbi_load(filename.c_str(), &mSize.x(), &mSize.y(), &numChannels, 0);
    if (!data) {
        throw invalid_argument("Could not load texture data from file " + filename);
    }

    mNumChannels = static_cast<size_t>(numChannels);

    vector<string> channelNames = {"R", "G", "B", "A"};

    vector<Channel> channels;
    for (size_t c = 0; c < mNumChannels; ++c) {
        string name = c < channelNames.size() ? channelNames[c] : to_string(c - channelNames.size());
        channels.emplace_back(name);
    }

    size_t numPixels = mSize.prod();
    for (size_t i = 0; i < numPixels; ++i) {
        size_t baseIdx = i * mNumChannels;
        for (size_t c = 0; c < mNumChannels; ++c) {
            // Assume LDR images were sRGB tonemapped and apply inverse.
            // For now, approximate sRGB curve by 2.2 gamma.
            // TODO: Proper sRGB curve.
            float value = data[baseIdx + c];
            channels[c].data().push_back(pow(value / 255.0f, 2.2f));
        }
    }

    stbi_image_free(data);

    for (auto& channel : channels) {
        string name = channel.name();
        mChannels.emplace(move(name), move(channel));
    }
}

void Image::readExr(const std::string& filename) {
    // OpenEXR for reading exr images
    cout << "Loading "s + filename + " via OpenEXR." << endl;

    Imf::InputFile file(filename.c_str());
    Imath::Box2i dw = file.header().dataWindow();
    mSize.x() = dw.max.x - dw.min.x + 1;
    mSize.y() = dw.max.y - dw.min.y + 1;

    // Inline helper class for dealing with the raw channels loaded from an exr file.
    class RawChannel {
    public:
        RawChannel(string name, Imf::PixelType type, size_t size)
            : mName(name), mType(type) {
            mData.resize(size * bytesPerPixel());
        }

        void registerWith(Imf::FrameBuffer& frameBuffer, const Imath::Box2i& dw) {
            int width = dw.max.x - dw.min.x + 1;
            frameBuffer.insert(mName.c_str(), Imf::Slice(
                mType,
                mData.data() - (dw.min.x - dw.min.y * width) * bytesPerPixel(),
                bytesPerPixel(), bytesPerPixel() * width, // stride in x and y
                1, 1, 0
            ));
        }

        void copyTo(Channel& channel) const {
            int bpp = bytesPerPixel();
            auto& dstData = channel.data();
            dstData.resize(mData.size() / bpp);

            for (size_t i = 0; i < dstData.size(); ++i) {
                size_t rawIdx = i * bpp;
                switch (mType) {
                    case Imf::HALF:  dstData[i] = static_cast<float>(*reinterpret_cast<const half*>(&mData[rawIdx]));     break;
                    case Imf::FLOAT: dstData[i] = static_cast<float>(*reinterpret_cast<const float*>(&mData[rawIdx]));    break;
                    case Imf::UINT:  dstData[i] = static_cast<float>(*reinterpret_cast<const uint32_t*>(&mData[rawIdx])); break;
                    default:
                        throw runtime_error("Invalid pixel type encountered.");
                }
            }
        }

        const auto& name() const {
            return mName;
        }

    private:
        int bytesPerPixel() const {
            return mType == Imf::HALF ? 2 : 4;
        }

        string mName;
        Imf::PixelType mType;
        vector<char> mData;
    };

    vector<RawChannel> rawChannels;
    Imf::FrameBuffer frameBuffer;

    const Imf::ChannelList& imfChannels = file.header().channels();
    for (Imf::ChannelList::ConstIterator i = imfChannels.begin(); i != imfChannels.end(); ++i) {
        rawChannels.emplace_back(i.name(), i.channel().type, mSize.prod());
        rawChannels.back().registerWith(frameBuffer, dw);
    }

    file.setFrameBuffer(frameBuffer);
    file.readPixels(dw.min.y, dw.max.y);

    for (const auto& rawChannel : rawChannels) {
        mChannels.emplace(rawChannel.name(), Channel{rawChannel.name()});
        rawChannel.copyTo(mChannels.at(rawChannel.name()));
    }
}
<commit_msg>Directly load linear colors via stbi<commit_after>// This file was developed by Thomas Müller <thomas94@gmx.net>.
// It is published under the BSD-style license contained in the LICENSE.txt file.

#include "../include/Image.h"

#include <ImfChannelList.h>
#include <ImfInputFile.h>

#include <stb_image.h>

#include <array>
#include <iostream>

using namespace std;

namespace {
    bool endsWith(const string& a, const string& b) {
        return a.length() >= b.length() && a.compare(a.length() - b.length(), b.length(), b) == 0;
    }
}

Image::Image(const string& filename)
    : mName(filename) {
    if (endsWith(filename, ".exr")) {
        readExr(filename);
    } else {
        readStbi(filename);
    }
}

const GlTexture* Image::texture(const std::string& channelName) {
    auto iter = mTextures.find(channelName);
    if (iter != end(mTextures)) {
        return &iter->second;
    }

    const auto* chan = channel(channelName);
    if (!chan) {
        return nullptr;
    }

    mTextures.emplace(channelName, GlTexture{});
    auto& texture = mTextures.at(channelName);
    texture.setData(chan->data(), mSize, 1);

    return &texture;
}

void Image::readStbi(const std::string& filename) {
    // No exr image? Try our best using stbi
    cout << "Loading "s + filename + " via STBI." << endl;

    int numChannels;
    auto data = stbi_loadf(filename.c_str(), &mSize.x(), &mSize.y(), &numChannels, 0);
    if (!data) {
        throw invalid_argument("Could not load texture data from file " + filename);
    }

    mNumChannels = static_cast<size_t>(numChannels);
    size_t numPixels = mSize.prod();

    vector<string> channelNames = {"R", "G", "B", "A"};

    vector<Channel> channels;
    for (size_t c = 0; c < mNumChannels; ++c) {
        string name = c < channelNames.size() ? channelNames[c] : to_string(c - channelNames.size());
        channels.emplace_back(name);
        channels.back().data().resize(numPixels);
    }

    for (size_t i = 0; i < numPixels; ++i) {
        size_t baseIdx = i * mNumChannels;
        for (size_t c = 0; c < mNumChannels; ++c) {
            channels[c].data()[i] = data[baseIdx + c];
        }
    }

    stbi_image_free(data);

    for (auto& channel : channels) {
        string name = channel.name();
        mChannels.emplace(move(name), move(channel));
    }
}

void Image::readExr(const std::string& filename) {
    // OpenEXR for reading exr images
    cout << "Loading "s + filename + " via OpenEXR." << endl;

    Imf::InputFile file(filename.c_str());
    Imath::Box2i dw = file.header().dataWindow();
    mSize.x() = dw.max.x - dw.min.x + 1;
    mSize.y() = dw.max.y - dw.min.y + 1;

    // Inline helper class for dealing with the raw channels loaded from an exr file.
    class RawChannel {
    public:
        RawChannel(string name, Imf::PixelType type, size_t size)
            : mName(name), mType(type) {
            mData.resize(size * bytesPerPixel());
        }

        void registerWith(Imf::FrameBuffer& frameBuffer, const Imath::Box2i& dw) {
            int width = dw.max.x - dw.min.x + 1;
            frameBuffer.insert(mName.c_str(), Imf::Slice(
                mType,
                mData.data() - (dw.min.x - dw.min.y * width) * bytesPerPixel(),
                bytesPerPixel(), bytesPerPixel() * width, // stride in x and y
                1, 1, 0
            ));
        }

        void copyTo(Channel& channel) const {
            int bpp = bytesPerPixel();
            auto& dstData = channel.data();
            dstData.resize(mData.size() / bpp);

            for (size_t i = 0; i < dstData.size(); ++i) {
                size_t rawIdx = i * bpp;
                switch (mType) {
                    case Imf::HALF:  dstData[i] = static_cast<float>(*reinterpret_cast<const half*>(&mData[rawIdx]));     break;
                    case Imf::FLOAT: dstData[i] = static_cast<float>(*reinterpret_cast<const float*>(&mData[rawIdx]));    break;
                    case Imf::UINT:  dstData[i] = static_cast<float>(*reinterpret_cast<const uint32_t*>(&mData[rawIdx])); break;
                    default:
                        throw runtime_error("Invalid pixel type encountered.");
                }
            }
        }

        const auto& name() const {
            return mName;
        }

    private:
        int bytesPerPixel() const {
            return mType == Imf::HALF ? 2 : 4;
        }

        string mName;
        Imf::PixelType mType;
        vector<char> mData;
    };

    vector<RawChannel> rawChannels;
    Imf::FrameBuffer frameBuffer;

    const Imf::ChannelList& imfChannels = file.header().channels();
    for (Imf::ChannelList::ConstIterator i = imfChannels.begin(); i != imfChannels.end(); ++i) {
        rawChannels.emplace_back(i.name(), i.channel().type, mSize.prod());
        rawChannels.back().registerWith(frameBuffer, dw);
    }

    file.setFrameBuffer(frameBuffer);
    file.readPixels(dw.min.y, dw.max.y);

    for (const auto& rawChannel : rawChannels) {
        mChannels.emplace(rawChannel.name(), Channel{rawChannel.name()});
        rawChannel.copyTo(mChannels.at(rawChannel.name()));
    }
}
<|endoftext|>


<commit_before>#include "MCEnv.hpp"

MCEnv::MCEnv(const QString path){
    if(QFile::exists(path)) this->path = path;
	mods = false;
}

bool MCEnv::open(){
    xmlReader = new Xml(path + fsp +  eachEnvDataXmlName);
    if(!xmlReader->open()) return false;
    xml_d data;
    xmlReader->getXmlData(&data);
    envName = data.name;
	mcVersion = data.version;
	mods = data.mods;
    comment = data.comment;
    return true;
}

bool MCEnv::save(){
    xml_d data;
    data.name = envName;
    data.version = mcVersion;
    data.mods = mods;
    data.comment = comment;
    xmlReader->setXmlData(&data);
    xmlReader->save();
    return true;
}

bool MCEnv::initEnv(init_d *d, const QString dir_path){
    if(d->name.isEmpty() or dir_path.isEmpty()) return false;
    if(!QDir(dir_path).exists()) return false;
    QDir envsdir(mcswitch_dir_env);
    QStringList envs = envsdir.entryList();

    /*同名のenvがないかチェック*/
    QStringListIterator i(envs);
    while(i.hasNext()){
        if(i.next() == d->name){
            return false; // あったらfalseを返して終了
        }
    }

    if(!QDir().mkdir(dir_path + "/" + d->name)) return false;
    if(!QFile::copy(tmp_xml1,dir_path + "/" + d->name + "/" + eachEnvDataXmlName)) return false;
    QFile::setPermissions(dir_path + "/" + d->name + "/" + eachEnvDataXmlName,
                          QFile::ReadOwner  |
                          QFile::WriteOwner |
                          QFile::ReadUser   |
                          QFile::WriteUser
                          );

    Xml writer(dir_path + "/" + d->name + "/" + eachEnvDataXmlName);
    writer.open();
    xml_d data;
    data.name = d->name;
    data.version = QString::number(d->version[0]) + "." + QString::number(d->version[1]) + "." + QString::number(d->version[2]);
    data.mods = d->usemod;
    data.comment = d->comment;
    writer.setXmlData(&data);
    if(!writer.save()) return false;
    return true;
}

bool MCEnv::setName(const QString name){
    if(name.isEmpty()){
        return false;
    }else{
        this->envName = name;
        return true;
    }
}

bool MCEnv::setVersion(const QString version){
    if(version.isEmpty()){
        return false;
    }else{
        this->mcVersion = version;
        return true;
    }
}

bool MCEnv::setComment(const QString comment){
    this->comment = comment;
    return true;
}

bool MCEnv::setMods(bool f){
    this->mods = f;
    return true;
}
<commit_msg>MCEnv.cppを修正<commit_after>#include "MCEnv.hpp"

MCEnv::MCEnv(const QString path){
    if(QFile::exists(path)) this->path = path;
	mods = false;
}

bool MCEnv::open(){
    xmlReader = new Xml(path + fsp +  eachEnvDataXmlName);
    if(!xmlReader->open()) return false;
    xml_d data;
    xmlReader->getXmlData(&data);
    envName = data.name;
	mcVersion = data.version;
	mods = data.mods;
    comment = data.comment;
    return true;
}

bool MCEnv::save(){
    xml_d data;
    data.name = envName;
    data.version = mcVersion;
    data.mods = mods;
    data.comment = comment;
    xmlReader->setXmlData(&data);
    xmlReader->save();
    return true;
}

bool MCEnv::initEnv(init_d *d, const QString dir_path){
    if(d->name.isEmpty() or dir_path.isEmpty()) return false;
    if(!QDir(dir_path).exists()) return false;
    QDir envsdir(mcswitch_dir_env);
    QStringList envs = envsdir.entryList();

    /*同名のenvがないかチェック*/
    QStringListIterator i(envs);
    while(i.hasNext()){
        if(i.next() == d->name){
            return false; // あったらfalseを返して終了
        }
    }

    if(!QDir().mkdir(dir_path + fsp + d->name)) return false;
    if(!QFile::copy(tmp_xml1,dir_path + fsp + d->name + fsp + eachEnvDataXmlName)) return false;
    QFile::setPermissions(dir_path + fsp + d->name + fsp + eachEnvDataXmlName,
                          QFile::ReadOwner  |
                          QFile::WriteOwner |
                          QFile::ReadUser   |
                          QFile::WriteUser
                          );

    Xml writer(dir_path + fsp + d->name + fsp + eachEnvDataXmlName);
    writer.open();
    xml_d data;
    data.name = d->name;
    data.version = QString::number(d->version[0]) + "." + QString::number(d->version[1]) + "." + QString::number(d->version[2]);
    data.mods = d->usemod;
    data.comment = d->comment;
    writer.setXmlData(&data);
    if(!writer.save()) return false;
    return true;
}

bool MCEnv::setName(const QString name){
    if(name.isEmpty()){
        return false;
    }else{
        this->envName = name;
        return true;
    }
}

bool MCEnv::setVersion(const QString version){
    if(version.isEmpty()){
        return false;
    }else{
        this->mcVersion = version;
        return true;
    }
}

bool MCEnv::setComment(const QString comment){
    this->comment = comment;
    return true;
}

bool MCEnv::setMods(bool f){
    this->mods = f;
    return true;
}
<|endoftext|>


<commit_before>#include "stan/math/functions/falling_factorial.hpp"
#include <gtest/gtest.h>

TEST(MathFunctions, falling_factorial) {
  using stan::math::falling_factorial;

  EXPECT_FLOAT_EQ(4, falling_factorial(4.0,3));
  EXPECT_FLOAT_EQ(0.25, falling_factorial(3.0,4));
  EXPECT_THROW(falling_factorial(-1, 4), std::domain_error);
}
<commit_msg>added NaN test for falling_factorial<commit_after>#include <stan/math/functions/falling_factorial.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <gtest/gtest.h>

TEST(MathFunctions, falling_factorial) {
  using stan::math::falling_factorial;

  EXPECT_FLOAT_EQ(4, falling_factorial(4.0,3));
  EXPECT_FLOAT_EQ(0.25, falling_factorial(3.0,4));
  EXPECT_THROW(falling_factorial(-1, 4), std::domain_error);
}

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

  EXPECT_PRED1(boost::math::isnan<double>,
               stan::math::falling_factorial(4.0, nan));

  EXPECT_PRED1(boost::math::isnan<double>,
               stan::math::falling_factorial(nan, 4.0));

  EXPECT_PRED1(boost::math::isnan<double>,
               stan::math::falling_factorial(nan, nan));
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/isteps/istep14/call_proc_exit_cache_contained.C $     */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015                             */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#include <errl/errlentry.H>
#include <errl/errlmanager.H>
#include <errl/errludtarget.H>

#include <isteps/hwpisteperror.H>
#include <initservice/isteps_trace.H>
#include <initservice/taskargs.H>

//  targeting support
#include <targeting/common/commontargeting.H>
#include <targeting/common/util.H>
#include <targeting/common/utilFilter.H>

#include <sys/mm.h>
#include <intr/interrupt.H>
#include <isteps/hwpf_reasoncodes.H>

using   namespace   ISTEP;
using   namespace   ISTEP_ERROR;
using   namespace   ERRORLOG;
using   namespace   TARGETING;

namespace ISTEP_14
{
void* call_proc_exit_cache_contained (void *io_pArgs)
{

    IStepError  l_stepError;

    TRACDCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
               "call_proc_exit_cache_contained entry" );

    // @@@@@    CUSTOM BLOCK:   @@@@@
    //  figure out what targets we need
    //  customize any other inputs
    //  set up loops to go through all targets (if parallel, spin off a task)
    //  extend the memory space from 8MEG to 32Meg

    //if mirrored then check that there is going to be memory at that location.
    //For sapphire with mirrored location flipped and at zero,
    //this also insures there is memory available to 'exit_cache' to.
    //Also set ATTR_PAYLOAD_BASE here.
    TARGETING::Target* l_sys = NULL;
    targetService().getTopLevelTarget(l_sys);
    assert( l_sys != NULL );

    //@TODO RTC:133831 Commenting out due to missing attributes
#if 0
    errlHndl_t  l_errl  =   NULL;
    uint8_t l_mpipl = l_sys->getAttr<ATTR_IS_MPIPL_HB>();
    ATTR_PAYLOAD_BASE_type payloadBase = 0;

    if(!l_mpipl)
    {
        ATTR_PAYLOAD_IN_MIRROR_MEM_type l_mirrored = false;

        // In Sapphire mode disable mirroring for now - @todo-RTC:108314
        // and force payload to zero
        if(!is_sapphire_load())
        {
            payloadBase = l_sys->getAttr<ATTR_PAYLOAD_BASE>();
            l_mirrored = l_sys->getAttr<ATTR_PAYLOAD_IN_MIRROR_MEM>();
        }

        if(l_mirrored)
        {
            ATTR_MIRROR_BASE_ADDRESS_type l_mirrorBaseAddr = 0;
            if(!is_sapphire_load())
            {
                uint64_t hrmor_base =
                    l_sys->getAttr<TARGETING::ATTR_HB_HRMOR_NODAL_BASE>();

                l_mirrorBaseAddr =
                    l_sys->getAttr<TARGETING::ATTR_MIRROR_BASE_ADDRESS>();

                // For single-node systems, the non-master processors can be
                // in a different logical (powerbus) node.
                // Need to migrate task to master.
                task_affinity_pin();
                task_affinity_migrate_to_master();
                uint64_t this_node = INTR::PIR_t(task_getcpuid()).nodeId;
                task_affinity_unpin();

                l_mirrorBaseAddr += (this_node * hrmor_base)/2;

            }

            // Verify there is memory at the mirrored location
            bool mirroredMemExists = false;
            TARGETING::TargetHandleList l_procList;
            getAllChips(l_procList, TYPE_PROC);

            for (TargetHandleList::const_iterator proc = l_procList.begin();
                 proc != l_procList.end() && !mirroredMemExists;
                 ++proc)
            {
                uint64_t mirrorBase[4];
                uint64_t mirrorSize[4];
                bool rc = (*proc)->
                    tryGetAttr<TARGETING::ATTR_PROC_MIRROR_BASES>(mirrorBase);
                if(false == rc)
                {
                    TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                              "Failed to get ATTR_PROC_MIRROR_BASES");
                    assert(0);
                }

                rc = (*proc)->
                    tryGetAttr<TARGETING::ATTR_PROC_MIRROR_SIZES>(mirrorSize);
                if(false == rc)
                {
                    TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                              "Failed to get ATTR_PROC_MIRROR_SIZES");
                    assert(0);
                }

                for(uint64_t i = 0; i < 4 && !mirroredMemExists; ++i)
                {
                    if(mirrorSize[i] != 0 &&
                       l_mirrorBaseAddr >= mirrorBase[i] &&
                       l_mirrorBaseAddr < (mirrorBase[i] + mirrorSize[i]))
                    {
                        mirroredMemExists = true;
                    }
                }
            }

            if (mirroredMemExists)
            {
                // ATTR_PAYLOAD_BASE is in MB
                payloadBase += l_mirrorBaseAddr/MEGABYTE;
            }
            else
            {
                TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, ERR_MRK
                          "Request to load payload into mirrored memory,"
                          " but no memory exists at address 0x%016lx",
                          l_mirrorBaseAddr);

                /*@
                 *  @errortype      ERRL_SEV_CRITICAL_SYS_TERM
                 *  @moduleid       fapi::MOD_EXIT_CACHE_CONTAINED,
                 *  @reasoncode     fapi::RC_NO_MIRRORED_MEMORY,
                 *  @userdata1      Mirrored Memory Address
                 *  @userdata2      0
                 *
                 *  @devdesc        Request given to load payload into mirrored
                 *                  memory, but no mirrored memory exists at
                 *                  that location.
                 */
                l_errl = new ERRORLOG::ErrlEntry
                    (
                     ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM,
                     fapi::MOD_EXIT_CACHE_CONTAINED,
                     fapi::RC_NO_MIRRORED_MEMORY,
                     l_mirrorBaseAddr,
                     0,
                     true); // callout firmware
            }
        }
        // If we're not mirrored, payloadBase is the lowest mem_base.
        // Note that if we are mirrored, finding the correct mirror
        // base yields the proper payloadBase. This should also work
        // for sapphire as a single (working) node will return 0 for
        // bottom_mem_addr.
        else {
            payloadBase += get_bottom_mem_addr()/MEGABYTE;
        }
    }

    if(!l_errl)
    {
        if(!l_mpipl)
        {
            TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                      "Payload base address is 0x%016lx",
                      payloadBase * MEGABYTE);

            l_sys->setAttr<ATTR_PAYLOAD_BASE>(payloadBase);
        }

        //@TODO RTC:133831  call the HWP with each fapi::Target
        //FAPI_INVOKE_HWP( l_errl,
        //                 proc_exit_cache_contained
        //               );

        if ( l_errl )
        {
            TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                      "ERROR : call_proc_exit_cache_contained, "
                      "errorlog PLID=0x%x",
                      l_errl->plid() );
        }
        // no errors so extend VMM.
        else
        {
            TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
                       "SUCCESS : call_proc_exit_cache_contained" );

            // Call the function to extend VMM to 32MEG
            int rc = mm_extend();

            if (rc!=0)
            {
                /*@
                 * @errortype
                 * @moduleid     fapi::MOD_EXIT_CACHE_CONTAINED
                 * @reasoncode   fapi::RC_MM_EXTEND_FAILED
                 * @userdata1    rc from mm_extend
                 * @userdata2    <UNUSED>
                 *
                 *   @devdesc  Failure extending memory to 32MEG after
                 *        exiting cache contained mode.
                 */
                l_errl = new ERRORLOG::ErrlEntry
                    (ERRORLOG::ERRL_SEV_UNRECOVERABLE,
                     fapi::MOD_EXIT_CACHE_CONTAINED,
                     fapi::RC_MM_EXTEND_FAILED,
                     rc,
                     0);

                TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                          "ERROR : call_proc_exit_cache_contained"
                          " - extendVMM, rc=0x%x",
                          rc );
            }
            else
            {
                // trace out the extend VMM was successful
                TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                          "SUCCESS : call_proc_exit_cache_contained"
                          " - extendVMM");
            }
        }
    }
    if ( l_errl )
    {
        // Create IStep error log and cross reference to error that occurred
        l_stepError.addErrorDetails( l_errl );

        // Commit Error
        errlCommit( l_errl, HWPF_COMP_ID );
    }

    TRACDCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
               "call_proc_exit_cache_contained exit" );
#endif
    // @@@@@    END CUSTOM BLOCK:   @@@@@

    // end task, returning any errorlogs to IStepDisp
   return l_stepError.getErrorHandle();
}

};
<commit_msg>P9 - Fake trigger for memory expansion<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/isteps/istep14/call_proc_exit_cache_contained.C $     */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015                             */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#include <errl/errlentry.H>
#include <errl/errlmanager.H>
#include <errl/errludtarget.H>

#include <isteps/hwpisteperror.H>
#include <initservice/isteps_trace.H>
#include <initservice/taskargs.H>

//  targeting support
#include <targeting/common/commontargeting.H>
#include <targeting/common/util.H>
#include <targeting/common/utilFilter.H>

#include <sys/mm.h>
#include <intr/interrupt.H>
#include <isteps/hwpf_reasoncodes.H>

// @TODO RTC:134082 remove below block
// Add P9 - Fake trigger for memory expansion
#include <kernel/console.H>                  // printk status
#include <devicefw/userif.H>
// @TODO RTC:134082 remove above block

using   namespace   ISTEP;
using   namespace   ISTEP_ERROR;
using   namespace   ERRORLOG;
using   namespace   TARGETING;

namespace ISTEP_14
{
void* call_proc_exit_cache_contained (void *io_pArgs)
{

    IStepError  l_stepError;

    TRACDCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
               "call_proc_exit_cache_contained entry" );

    // @@@@@    CUSTOM BLOCK:   @@@@@
    //  figure out what targets we need
    //  customize any other inputs
    //  set up loops to go through all targets (if parallel, spin off a task)
    //  extend the memory space from 8MEG to 32Meg

    //if mirrored then check that there is going to be memory at that location.
    //For sapphire with mirrored location flipped and at zero,
    //this also insures there is memory available to 'exit_cache' to.
    //Also set ATTR_PAYLOAD_BASE here.
    TARGETING::Target* l_sys = NULL;
    targetService().getTopLevelTarget(l_sys);
    assert( l_sys != NULL );

    //@TODO RTC:133831 Commenting out due to missing attributes
    errlHndl_t  l_errl  =   NULL;
    uint8_t l_mpipl = l_sys->getAttr<ATTR_IS_MPIPL_HB>();
    ATTR_PAYLOAD_BASE_type payloadBase = 0;

    if(!l_mpipl)
    {
        ATTR_PAYLOAD_IN_MIRROR_MEM_type l_mirrored = false;

        // In Sapphire mode disable mirroring for now - @todo-RTC:108314
        // and force payload to zero
        if(!is_sapphire_load())
        {
            payloadBase = l_sys->getAttr<ATTR_PAYLOAD_BASE>();
            l_mirrored = l_sys->getAttr<ATTR_PAYLOAD_IN_MIRROR_MEM>();
        }

        if(l_mirrored)
        {
            ATTR_MIRROR_BASE_ADDRESS_type l_mirrorBaseAddr = 0;
            if(!is_sapphire_load())
            {
                uint64_t hrmor_base =
                    l_sys->getAttr<TARGETING::ATTR_HB_HRMOR_NODAL_BASE>();

                l_mirrorBaseAddr =
                    l_sys->getAttr<TARGETING::ATTR_MIRROR_BASE_ADDRESS>();

                // For single-node systems, the non-master processors can be
                // in a different logical (powerbus) node.
                // Need to migrate task to master.
                task_affinity_pin();
                task_affinity_migrate_to_master();
                uint64_t this_node = INTR::PIR_t(task_getcpuid()).nodeId;
                task_affinity_unpin();

                l_mirrorBaseAddr += (this_node * hrmor_base)/2;

            }

            // Verify there is memory at the mirrored location
            bool mirroredMemExists = false;
            TARGETING::TargetHandleList l_procList;
            getAllChips(l_procList, TYPE_PROC);

            for (TargetHandleList::const_iterator proc = l_procList.begin();
                 proc != l_procList.end() && !mirroredMemExists;
                 ++proc)
            {
                uint64_t mirrorBase[4];
                uint64_t mirrorSize[4];
                bool rc = (*proc)->
                    tryGetAttr<TARGETING::ATTR_PROC_MIRROR_BASES>(mirrorBase);
                if(false == rc)
                {
                    TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                              "Failed to get ATTR_PROC_MIRROR_BASES");
                    assert(0);
                }

                rc = (*proc)->
                    tryGetAttr<TARGETING::ATTR_PROC_MIRROR_SIZES>(mirrorSize);
                if(false == rc)
                {
                    TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                              "Failed to get ATTR_PROC_MIRROR_SIZES");
                    assert(0);
                }

                for(uint64_t i = 0; i < 4 && !mirroredMemExists; ++i)
                {
                    if(mirrorSize[i] != 0 &&
                       l_mirrorBaseAddr >= mirrorBase[i] &&
                       l_mirrorBaseAddr < (mirrorBase[i] + mirrorSize[i]))
                    {
                        mirroredMemExists = true;
                    }
                }
            }

            if (mirroredMemExists)
            {
                // ATTR_PAYLOAD_BASE is in MB
                payloadBase += l_mirrorBaseAddr/MEGABYTE;
            }
            else
            {
                TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace, ERR_MRK
                          "Request to load payload into mirrored memory,"
                          " but no memory exists at address 0x%016lx",
                          l_mirrorBaseAddr);

                /*@
                 *  @errortype      ERRL_SEV_CRITICAL_SYS_TERM
                 *  @moduleid       fapi::MOD_EXIT_CACHE_CONTAINED,
                 *  @reasoncode     fapi::RC_NO_MIRRORED_MEMORY,
                 *  @userdata1      Mirrored Memory Address
                 *  @userdata2      0
                 *
                 *  @devdesc        Request given to load payload into mirrored
                 *                  memory, but no mirrored memory exists at
                 *                  that location.
                 */
                l_errl = new ERRORLOG::ErrlEntry
                    (
                     ERRORLOG::ERRL_SEV_CRITICAL_SYS_TERM,
                     fapi::MOD_EXIT_CACHE_CONTAINED,
                     fapi::RC_NO_MIRRORED_MEMORY,
                     l_mirrorBaseAddr,
                     0,
                     true); // callout firmware
            }
        }
        // If we're not mirrored, payloadBase is the lowest mem_base.
        // Note that if we are mirrored, finding the correct mirror
        // base yields the proper payloadBase. This should also work
        // for sapphire as a single (working) node will return 0 for
        // bottom_mem_addr.
        else {
            payloadBase += get_bottom_mem_addr()/MEGABYTE;
        }
    }

    if(!l_errl)
    {
        if(!l_mpipl)
        {
            TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                      "Payload base address is 0x%016lx",
                      payloadBase * MEGABYTE);

            l_sys->setAttr<ATTR_PAYLOAD_BASE>(payloadBase);
        }

        //@TODO RTC:133831  call the HWP with each fapi::Target
        //FAPI_INVOKE_HWP( l_errl,
        //                 proc_exit_cache_contained
        //               );

        if ( l_errl )
        {
            TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                      "ERROR : call_proc_exit_cache_contained, "
                      "errorlog PLID=0x%x",
                      l_errl->plid() );
        }
        // no errors so extend VMM.
        else
        {
            TRACFCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
                       "SUCCESS : call_proc_exit_cache_contained" );

            // @TODO RTC:134082 remove below block
#if 1
            // Add P9 - Fake trigger for memory expansion
            TARGETING::Target* l_masterProc = NULL;
            TARGETING::targetService()
              .masterProcChipTargetHandle( l_masterProc );

            uint64_t l_top_addr = get_top_mem_addr();
            uint64_t l_bottom_addr = get_bottom_mem_addr();
            uint64_t l_mem_size = l_top_addr - l_bottom_addr;

            // aggregate scom data to write
            uint64_t l_data[] = {l_bottom_addr,  // Memory Base Address
                                 l_mem_size,     // Memory Size in bytes
                                 1};             // Memory Valid
            uint64_t l_addr = 0x05000000;
            size_t scom_size = sizeof(uint64_t);

            for(uint8_t i = 0;
                i < sizeof(l_data) / sizeof(uint64_t);
                i++)
            {
                l_errl = deviceWrite( l_masterProc,
                                      &(l_data[i]),
                                      scom_size,
                                      DEVICE_SCOM_ADDRESS(l_addr+i) );
                if( l_errl ) { break; }
            }

            if ( l_errl )
            {
                // Create IStep error log and cross reference to error that
                // occurred
                l_stepError.addErrorDetails( l_errl );

                // Commit Error
                errlCommit( l_errl, HWPF_COMP_ID );
            }

            printk("Fake Memory now set up.\n");

            // exit cache contained mode
            l_errl = deviceWrite( l_masterProc,
                                  &(l_data[2]),
                                  scom_size,
                                  DEVICE_SCOM_ADDRESS(l_addr+3) );

            if ( l_errl )
            {
                // Create IStep error log and cross reference to error that
                // occurred
                l_stepError.addErrorDetails( l_errl );

                // Commit Error
                errlCommit( l_errl, HWPF_COMP_ID );
            }

            printk("Cache contained mode has been exited.\n");
            // End of Add P9 - Fake trigger for memory expansion
#endif
            // @TODO RTC:134082 remove above block

            // Call the function to extend VMM to 32MEG
            int rc = mm_extend();

            if (rc!=0)
            {
                /*@
                 * @errortype
                 * @moduleid     fapi::MOD_EXIT_CACHE_CONTAINED
                 * @reasoncode   fapi::RC_MM_EXTEND_FAILED
                 * @userdata1    rc from mm_extend
                 * @userdata2    <UNUSED>
                 *
                 *   @devdesc  Failure extending memory to 32MEG after
                 *        exiting cache contained mode.
                 */
                l_errl = new ERRORLOG::ErrlEntry
                    (ERRORLOG::ERRL_SEV_UNRECOVERABLE,
                     fapi::MOD_EXIT_CACHE_CONTAINED,
                     fapi::RC_MM_EXTEND_FAILED,
                     rc,
                     0);

                TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                          "ERROR : call_proc_exit_cache_contained"
                          " - extendVMM, rc=0x%x",
                          rc );
            }
            else
            {
                // trace out the extend VMM was successful
                TRACFCOMP(ISTEPS_TRACE::g_trac_isteps_trace,
                          "SUCCESS : call_proc_exit_cache_contained"
                          " - extendVMM");
            }
        }
    }
    if ( l_errl )
    {
        // Create IStep error log and cross reference to error that occurred
        l_stepError.addErrorDetails( l_errl );

        // Commit Error
        errlCommit( l_errl, HWPF_COMP_ID );
    }

    TRACDCOMP( ISTEPS_TRACE::g_trac_isteps_trace,
               "call_proc_exit_cache_contained exit" );
    // @@@@@    END CUSTOM BLOCK:   @@@@@

    // end task, returning any errorlogs to IStepDisp
   return l_stepError.getErrorHandle();
}

};
<|endoftext|>


<commit_before>#ifndef INCLUDE_AL_MATH_RANDOM_HPP
#define INCLUDE_AL_MATH_RANDOM_HPP

/*	Allocore --
	Multimedia / virtual environment application class library

	Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, UCSB.
	Copyright (C) 2012. The Regents of the University of California.
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

		Redistributions of source code must retain the above copyright notice,
		this list of conditions and the following disclaimer.

		Redistributions in binary form must reproduce the above copyright
		notice, this list of conditions and the following disclaimer in the
		documentation and/or other materials provided with the distribution.

		Neither the name of the University of California nor the names of its
		contributors may be used to endorse or promote products derived from
		this software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
	POSSIBILITY OF SUCH DAMAGE.

	File description:
	Various flavors of pseudo-random number generators and distributions

	File author(s):
	Lance Putnam, 2006, putnam.lance@gmail.com
*/

#include <time.h>							/* req'd for time() */
#include <cmath>
#include "allocore/types/al_Conversion.hpp"	/* req'd for int to float conversion */
#include "allocore/math/al_Constants.hpp"

namespace al {

/// Random number generation utilities
namespace rnd{

class LinCon;
class MulLinCon;
class Tausworthe;
template<class RNG> class Random;

/// Get a random seed in interval [0, 4294967296)
inline static uint32_t seed(){
	static uint32_t val = time(NULL);
	return val = val*1664525UL + 1013904223UL;
}

/// Random distribution generator
template <class RNG=al::rnd::Tausworthe>
class Random{
public:

	/// Default constructor uses a randomly generated seed
	Random(){}

	/// @param[in] seed		Initial seed value
	Random(uint32_t seed): mRNG(seed){}

	/// Set seed
	Random& seed(uint32_t v){ mRNG.seed(v); return *this; }

	/// Returns uniform random in [0, 1)
	float uniform(){ return al::uintToUnit<float>(mRNG()); }

	/// Returns uniform random in [0, hi)
	template <class T>
	T uniform(const T& hi){ return hi*uniform(); }

	/// Returns uniform random in [lo, hi)
	template <class T>
	T uniform(const T& hi, const T& lo){ return T((hi-lo)*uniform()) + lo; }

	/// Returns uniform random in [-1, 1)
	float uniformS(){ return al::uintToUnitS<float>(mRNG()); }

	/// Returns uniform random in [-lim, lim)
	template <class T>
	T uniformS(const T& lim){ return lim*uniformS(); }

	/// Returns point within a unit ball

	/// To get a random point on a sphere, simply normalize the result.
	/// \tparam		N		dimensions of ball
	/// @param[in]	point	an array of size N
	template <int N, class T>
	void ball(T * point);

	/// Returns random Gaussian
	float gaussian(){ float r; gaussian(r,r); return r; }

	/// Returns two random Gaussians
	template <class T>
	void gaussian(T& y1, T& y2);

	/// Returns true with a probability of p.
	bool prob(float p=0.5f){ return uniform() < p; }

	/// Randomly shuffles elements in array.
	template <class T>
	void shuffle(T * arr, uint32_t len);

protected:
	RNG mRNG;
};

/// Linear congruential uniform pseudo-random number generator.

///	This generator is very fast requiring only a single integer multiply and add
/// per iteration.  However, the least significant bits of the numbers are less
/// random; the most extreme case being the LSB which at best flips between
/// 0 and 1. This generator also exhibits poor dimensional distribution,
/// therefore it is best to have a different generator for each dimension,
/// rather than sharing one.
class LinCon {
public:
	/// Default constructor uses a randomly generated seed
	LinCon(){
		seed(al::rnd::seed());
		type(0);
	}

	/// @param[in] seed		Initial seed value
	LinCon(uint32_t seed)
	:	mVal(seed)
	{	type(0); }

	/// Generate next uniform random integer in [0, 2^32)
	uint32_t operator()(){
		return mVal = mVal*mMul + mAdd;
	}

	/// Set seed
	void seed(uint32_t v){ mVal=v; }

	/// Change the type of equation used.

	/// 0 - Knuth, Numerical Recipes in C\n
	/// 1 - BCPL
	void type(int v){
		switch(v){
		default:
		case 0: mMul =    1664525; mAdd = 1013904223; break;
		case 1:	mMul = 2147001325; mAdd =  715136305; break;
		}
	}

private:
	uint32_t mVal;
	uint32_t mMul, mAdd;
};

/// Multiplicative linear congruential uniform pseudo-random number generator.

///	This generator is faster than LinCon requiring only a single integer
/// multiply per iteration. However, the downside is that it produces lower
/// quality (less "random") results than LinCon. Because of this, it is really
/// not appropriate for simulations, but due to its speed it is very useful for
/// synthesizing noise for audio and graphics.
class MulLinCon{
public:
	/// Default constructor uses a randomly generated seed
	MulLinCon(){
		seed(al::rnd::seed());
		type(0);
	}

	/// @param[in] seed	Initial seed value
	MulLinCon(uint32_t seed)
	:	mVal(seed)
	{	type(0); }

	/// Generate next uniform random integer in [0, 2^32)
	uint32_t operator()(){
		return mVal *= mMul;
	}

	/// Set seed
	void seed(uint32_t v){ mVal=v; }

	/// Change the type of equation used.

	/// 0 - Marsaglia, Super-Duper\n
	///
	void type(int v){
		switch(v){
		default:
		case 0: mMul = 69069; break;
		}
	}

private:
	uint32_t mVal;
	uint32_t mMul;
};

/// Combined Tausworthe uniform pseudo-random number generator.

/// This generator produces highly random numbers, but is more expensive than
/// than a linear congruential RNG.
/// It is based on the paper
/// P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe Generators",
/// Mathematics of Computation, 65, 213 (1996), 203--213.
/// http://www.iro.umontreal.ca/~lecuyer/papers.html
class Tausworthe{
public:

	/// Default constructor uses a randomly generated seed
	Tausworthe();

	/// @param[in] seed		Initial seed value
	Tausworthe(uint32_t seed);

	/// Generate next uniform random integer in [0, 2^32)
	uint32_t operator()();

	/// Set seed
	void seed(uint32_t v);

	/// Set seed
	void seed(uint32_t v1, uint32_t v2, uint32_t v3, uint32_t v4);

private:
	uint32_t s1, s2, s3, s4;
	void iterate();
};

/// Get global random number generator
inline Random<>& global(){ static Random<> r; return r; }

/// Returns point within a unit ball

/// To get a random point on a sphere, simply normalize the result.
/// \tparam		N		dimensions of ball
/// @param[in]	point	an array of size N
template <int N, class T>
inline void ball(T * point){ global().ball<N>(point); }

/// Returns random Gaussian
inline float gaussian(){ return global().gaussian(); }

/// Returns true with probability p
inline bool prob(float p=0.5){ return global().prob(p); }

/// Returns uniform random in [0, 1)
inline float uniform(){ return global().uniform(); }

/// Returns uniform random in [0, hi)
template <class T>
inline T uniform(const T& hi){ return global().uniform(hi); }

/// Returns uniform random in [lo, hi)
template <class T>
inline T uniform(const T& hi, const T& lo){ return global().uniform(hi,lo); }

/// Returns signed uniform random in (-1, 1)
inline float uniformS(){ return global().uniformS(); }

/// Returns signed uniform random in (-lim, lim)
template <class T>
inline T uniformS(const T& lim){ return global().uniformS(lim); }

// Implementation_______________________________________________________________

inline Tausworthe::Tausworthe(){ seed(al::rnd::seed()); }
inline Tausworthe::Tausworthe(uint32_t sd){ seed(sd); }

inline uint32_t Tausworthe::operator()(){
	iterate();
	return s1 ^ s2 ^ s3 ^ s4;
}

inline void Tausworthe::seed(uint32_t v){
	al::rnd::LinCon g(v);
	g();
	seed(g(), g(), g(), g());
}

inline void Tausworthe::seed(uint32_t v1, uint32_t v2, uint32_t v3, uint32_t v4){
	//printf("%u %u %u %u\n", v1, v2, v3, v4);
	v1 & 0xffffffe ? s1 = v1 : s1 = ~v1;
	v2 & 0xffffff8 ? s2 = v2 : s2 = ~v2;
	v3 & 0xffffff0 ? s3 = v3 : s3 = ~v3;
	v4 & 0xfffff80 ? s4 = v4 : s4 = ~v4;
}

inline void Tausworthe::iterate(){
	s1 = ((s1 & 0xfffffffe) << 18) ^ (((s1 <<  6) ^ s1) >> 13);
	s2 = ((s2 & 0xfffffff8) <<  2) ^ (((s2 <<  2) ^ s2) >> 27);
	s3 = ((s3 & 0xfffffff0) <<  7) ^ (((s3 << 13) ^ s3) >> 21);
	s4 = ((s4 & 0xffffff80) << 13) ^ (((s4 <<  3) ^ s4) >> 12);
}

template <class RNG>
template <int N, class T>
void Random<RNG>::ball(T * point){
	T w;
	do{
		w = T(0);
		for(int i=0; i<N; ++i){
			float v = uniformS();
			point[i] = v;
			w += v*v;
		}
	} while(w >= T(1)); // if on or outside unit ball, try again
}

// Box-Muller transform
//		Box, G. and Muller, M. A note on the generation of normal deviates.
//		Ann. Math. Slat. 28, (1958).
//
// http://en.wikipedia.org/wiki/Box–Muller_transform
template <class RNG>
template <class T> void Random<RNG>::gaussian(T& y1, T& y2){
	float x1, x2, w;

	// Search for point within unit circle using sample-reject.
	// This will pass with probability π/4 = ~0.785.
	do{
		x1 = uniformS();
		x2 = uniformS();
		w = x1 * x1 + x2 * x2;
	} while(w >= 1.f);

	// perform inverse Gaussian function mapping
	w = std::sqrt((-2.f * std::log(w)) / w);
	y1 = T(x1 * w);
	y2 = T(x2 * w);
}

// Fisher-Yates shuffle
template <class RNG>
template <class T>
void Random<RNG>::shuffle(T * arr, uint32_t len){
	for(uint32_t i=len-1; i>0; --i){
		uint32_t j = uniform(i+1);
		T t = arr[i];
		arr[i] = arr[j];
		arr[j] = t;
	}
}

} // al::rnd::
} // al::
#endif

<commit_msg>rename gaussian to normal; more conventional and easier to type<commit_after>#ifndef INCLUDE_AL_MATH_RANDOM_HPP
#define INCLUDE_AL_MATH_RANDOM_HPP

/*	Allocore --
	Multimedia / virtual environment application class library

	Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, UCSB.
	Copyright (C) 2012. The Regents of the University of California.
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

		Redistributions of source code must retain the above copyright notice,
		this list of conditions and the following disclaimer.

		Redistributions in binary form must reproduce the above copyright
		notice, this list of conditions and the following disclaimer in the
		documentation and/or other materials provided with the distribution.

		Neither the name of the University of California nor the names of its
		contributors may be used to endorse or promote products derived from
		this software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
	POSSIBILITY OF SUCH DAMAGE.

	File description:
	Various flavors of pseudo-random number generators and distributions

	File author(s):
	Lance Putnam, 2006, putnam.lance@gmail.com
*/

#include <time.h>							/* req'd for time() */
#include <cmath>
#include "allocore/types/al_Conversion.hpp"	/* req'd for int to float conversion */
#include "allocore/math/al_Constants.hpp"

namespace al {

/// Random number generation utilities
namespace rnd{

class LinCon;
class MulLinCon;
class Tausworthe;
template<class RNG> class Random;

/// Get a random seed in interval [0, 4294967296)
inline static uint32_t seed(){
	static uint32_t val = time(NULL);
	return val = val*1664525UL + 1013904223UL;
}

/// Random distribution generator
template <class RNG=al::rnd::Tausworthe>
class Random{
public:

	/// Default constructor uses a randomly generated seed
	Random(){}

	/// @param[in] seed		Initial seed value
	Random(uint32_t seed): mRNG(seed){}

	/// Set seed
	Random& seed(uint32_t v){ mRNG.seed(v); return *this; }

	/// Returns uniform random in [0, 1)
	float uniform(){ return al::uintToUnit<float>(mRNG()); }

	/// Returns uniform random in [0, hi)
	template <class T>
	T uniform(const T& hi){ return hi*uniform(); }

	/// Returns uniform random in [lo, hi)
	template <class T>
	T uniform(const T& hi, const T& lo){ return T((hi-lo)*uniform()) + lo; }

	/// Returns uniform random in [-1, 1)
	float uniformS(){ return al::uintToUnitS<float>(mRNG()); }

	/// Returns uniform random in [-lim, lim)
	template <class T>
	T uniformS(const T& lim){ return lim*uniformS(); }

	/// Returns point within a unit ball

	/// To get a random point on a sphere, simply normalize the result.
	/// \tparam		N		dimensions of ball
	/// @param[in]	point	an array of size N
	template <int N, class T>
	void ball(T * point);

	/// Returns standard normal variate
	float normal(){ float r; normal(r,r); return r; }

	/// Returns two standard normal variates (for the price of one)
	template <class T>
	void normal(T& y1, T& y2);

	/// Returns true with a probability of p
	bool prob(float p=0.5f){ return uniform() < p; }

	/// Randomly shuffles elements in array
	template <class T>
	void shuffle(T * arr, uint32_t len);

	// DEPRECATED:
	float gaussian(){ return normal(); }
	template <class T> void gaussian(T& y1, T& y2){ normal(y1,y2); }
protected:
	RNG mRNG;
};

/// Linear congruential uniform pseudo-random number generator.

///	This generator is very fast requiring only a single integer multiply and add
/// per iteration.  However, the least significant bits of the numbers are less
/// random; the most extreme case being the LSB which at best flips between
/// 0 and 1. This generator also exhibits poor dimensional distribution,
/// therefore it is best to have a different generator for each dimension,
/// rather than sharing one.
class LinCon {
public:
	/// Default constructor uses a randomly generated seed
	LinCon(){
		seed(al::rnd::seed());
		type(0);
	}

	/// @param[in] seed		Initial seed value
	LinCon(uint32_t seed)
	:	mVal(seed)
	{	type(0); }

	/// Generate next uniform random integer in [0, 2^32)
	uint32_t operator()(){
		return mVal = mVal*mMul + mAdd;
	}

	/// Set seed
	void seed(uint32_t v){ mVal=v; }

	/// Change the type of equation used.

	/// 0 - Knuth, Numerical Recipes in C\n
	/// 1 - BCPL
	void type(int v){
		switch(v){
		default:
		case 0: mMul =    1664525; mAdd = 1013904223; break;
		case 1:	mMul = 2147001325; mAdd =  715136305; break;
		}
	}

private:
	uint32_t mVal;
	uint32_t mMul, mAdd;
};

/// Multiplicative linear congruential uniform pseudo-random number generator.

///	This generator is faster than LinCon requiring only a single integer
/// multiply per iteration. However, the downside is that it produces lower
/// quality (less "random") results than LinCon. Because of this, it is really
/// not appropriate for simulations, but due to its speed it is very useful for
/// synthesizing noise for audio and graphics.
class MulLinCon{
public:
	/// Default constructor uses a randomly generated seed
	MulLinCon(){
		seed(al::rnd::seed());
		type(0);
	}

	/// @param[in] seed	Initial seed value
	MulLinCon(uint32_t seed)
	:	mVal(seed)
	{	type(0); }

	/// Generate next uniform random integer in [0, 2^32)
	uint32_t operator()(){
		return mVal *= mMul;
	}

	/// Set seed
	void seed(uint32_t v){ mVal=v; }

	/// Change the type of equation used.

	/// 0 - Marsaglia, Super-Duper\n
	///
	void type(int v){
		switch(v){
		default:
		case 0: mMul = 69069; break;
		}
	}

private:
	uint32_t mVal;
	uint32_t mMul;
};

/// Combined Tausworthe uniform pseudo-random number generator.

/// This generator produces highly random numbers, but is more expensive than
/// than a linear congruential RNG.
/// It is based on the paper
/// P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe Generators",
/// Mathematics of Computation, 65, 213 (1996), 203--213.
/// http://www.iro.umontreal.ca/~lecuyer/papers.html
class Tausworthe{
public:

	/// Default constructor uses a randomly generated seed
	Tausworthe();

	/// @param[in] seed		Initial seed value
	Tausworthe(uint32_t seed);

	/// Generate next uniform random integer in [0, 2^32)
	uint32_t operator()();

	/// Set seed
	void seed(uint32_t v);

	/// Set seed
	void seed(uint32_t v1, uint32_t v2, uint32_t v3, uint32_t v4);

private:
	uint32_t s1, s2, s3, s4;
	void iterate();
};

/// Get global random number generator
inline Random<>& global(){ static Random<> r; return r; }

/// Returns point within a unit ball

/// To get a random point on a sphere, simply normalize the result.
/// \tparam		N		dimensions of ball
/// @param[in]	point	an array of size N
template <int N, class T>
inline void ball(T * point){ global().ball<N>(point); }

/// Returns standard normal variate
inline float normal(){ return global().normal(); }
inline float gaussian(){ return normal(); }

/// Returns true with probability p
inline bool prob(float p=0.5){ return global().prob(p); }

/// Returns uniform random in [0, 1)
inline float uniform(){ return global().uniform(); }

/// Returns uniform random in [0, hi)
template <class T>
inline T uniform(const T& hi){ return global().uniform(hi); }

/// Returns uniform random in [lo, hi)
template <class T>
inline T uniform(const T& hi, const T& lo){ return global().uniform(hi,lo); }

/// Returns signed uniform random in (-1, 1)
inline float uniformS(){ return global().uniformS(); }

/// Returns signed uniform random in (-lim, lim)
template <class T>
inline T uniformS(const T& lim){ return global().uniformS(lim); }

// Implementation_______________________________________________________________

inline Tausworthe::Tausworthe(){ seed(al::rnd::seed()); }
inline Tausworthe::Tausworthe(uint32_t sd){ seed(sd); }

inline uint32_t Tausworthe::operator()(){
	iterate();
	return s1 ^ s2 ^ s3 ^ s4;
}

inline void Tausworthe::seed(uint32_t v){
	al::rnd::LinCon g(v);
	g();
	seed(g(), g(), g(), g());
}

inline void Tausworthe::seed(uint32_t v1, uint32_t v2, uint32_t v3, uint32_t v4){
	//printf("%u %u %u %u\n", v1, v2, v3, v4);
	v1 & 0xffffffe ? s1 = v1 : s1 = ~v1;
	v2 & 0xffffff8 ? s2 = v2 : s2 = ~v2;
	v3 & 0xffffff0 ? s3 = v3 : s3 = ~v3;
	v4 & 0xfffff80 ? s4 = v4 : s4 = ~v4;
}

inline void Tausworthe::iterate(){
	s1 = ((s1 & 0xfffffffe) << 18) ^ (((s1 <<  6) ^ s1) >> 13);
	s2 = ((s2 & 0xfffffff8) <<  2) ^ (((s2 <<  2) ^ s2) >> 27);
	s3 = ((s3 & 0xfffffff0) <<  7) ^ (((s3 << 13) ^ s3) >> 21);
	s4 = ((s4 & 0xffffff80) << 13) ^ (((s4 <<  3) ^ s4) >> 12);
}

template <class RNG>
template <int N, class T>
void Random<RNG>::ball(T * point){
	T w;
	do{
		w = T(0);
		for(int i=0; i<N; ++i){
			float v = uniformS();
			point[i] = v;
			w += v*v;
		}
	} while(w >= T(1)); // if on or outside unit ball, try again
}

// Box-Muller transform
//		Box, G. and Muller, M. A note on the generation of normal deviates.
//		Ann. Math. Slat. 28, (1958).
//
// http://en.wikipedia.org/wiki/Box–Muller_transform
template <class RNG>
template <class T> void Random<RNG>::normal(T& y1, T& y2){
	float x1, x2, w;

	// Search for point within unit circle using sample-reject.
	// This will pass with probability π/4 = ~0.785.
	do{
		x1 = uniformS();
		x2 = uniformS();
		w = x1 * x1 + x2 * x2;
	} while(w >= 1.f);

	// perform inverse Gaussian function mapping
	w = std::sqrt((-2.f * std::log(w)) / w);
	y1 = T(x1 * w);
	y2 = T(x2 * w);
}

// Fisher-Yates shuffle
template <class RNG>
template <class T>
void Random<RNG>::shuffle(T * arr, uint32_t len){
	for(uint32_t i=len-1; i>0; --i){
		uint32_t j = uniform(i+1);
		T t = arr[i];
		arr[i] = arr[j];
		arr[j] = t;
	}
}

} // al::rnd::
} // al::
#endif

<|endoftext|>