<commit_before>#include "TelegramServer.hpp"

#include <QLoggingCategory>
#include <QTcpServer>
#include <QTcpSocket>

#include "ApiUtils.hpp"
#include "TelegramServerUser.hpp"
#include "RemoteClientConnection.hpp"
#include "RemoteServerConnection.hpp"
#include "Session.hpp"

#include "CServerTcpTransport.hpp"

// Generated RPC Operation Factory includes
#include "AccountOperationFactory.hpp"
#include "AuthOperationFactory.hpp"
#include "BotsOperationFactory.hpp"
#include "ChannelsOperationFactory.hpp"
#include "ContactsOperationFactory.hpp"
#include "HelpOperationFactory.hpp"
#include "LangpackOperationFactory.hpp"
#include "MessagesOperationFactory.hpp"
#include "PaymentsOperationFactory.hpp"
#include "PhoneOperationFactory.hpp"
#include "PhotosOperationFactory.hpp"
#include "StickersOperationFactory.hpp"
#include "UpdatesOperationFactory.hpp"
#include "UploadOperationFactory.hpp"
#include "UsersOperationFactory.hpp"
// End of generated RPC Operation Factory includes

#include "ServerMessageData.hpp"
#include "ServerRpcLayer.hpp"
#include "ServerUtils.hpp"
#include "Storage.hpp"
#include "Debug.hpp"

Q_LOGGING_CATEGORY(loggingCategoryServer, "telegram.server.main", QtInfoMsg)
Q_LOGGING_CATEGORY(loggingCategoryServerApi, "telegram.server.api", QtWarningMsg)

namespace Telegram {

namespace Server {

Server::Server(QObject *parent) :
    QObject(parent)
{
    m_rpcOperationFactories = {
        // Generated RPC Operation Factory initialization
        new AccountOperationFactory(),
        new AuthOperationFactory(),
        new BotsOperationFactory(),
        new ChannelsOperationFactory(),
        new ContactsOperationFactory(),
        new HelpOperationFactory(),
        new LangpackOperationFactory(),
        new MessagesOperationFactory(),
        new PaymentsOperationFactory(),
        new PhoneOperationFactory(),
        new PhotosOperationFactory(),
        new StickersOperationFactory(),
        new UpdatesOperationFactory(),
        new UploadOperationFactory(),
        new UsersOperationFactory(),
        // End of generated RPC Operation Factory initialization
    };
    m_serverSocket = new QTcpServer(this);
    connect(m_serverSocket, &QTcpServer::newConnection, this, &Server::onNewConnection);
}

void Server::setDcOption(const DcOption &option)
{
    m_dcOption = option;
}

void Server::setServerPrivateRsaKey(const Telegram::RsaKey &key)
{
    m_key = key;
}

bool Server::start()
{
    if (!m_serverSocket->listen(QHostAddress(m_dcOption.address), m_dcOption.port)) {
        qCCritical(loggingCategoryServer).noquote().nospace() << "Unable to listen port " << m_dcOption.port
                                                              << " ("  << m_serverSocket->serverError() << ")";
        return false;
    }
    qCInfo(loggingCategoryServer).nospace().noquote() << this << " start server (DC " << m_dcOption.id << ") "
                                                      << "on " << m_dcOption.address << ":" << m_dcOption.port
                                                      << "; Key:" << hex << showbase << m_key.fingerprint;
    return true;
}

void Server::stop()
{
    qCInfo(loggingCategoryServer).nospace().noquote() << this << " stop server (DC " << m_dcOption.id << ") "
                                                      << "on " << m_dcOption.address << ":" << m_dcOption.port;
    if (m_serverSocket) {
        m_serverSocket->close();
    }

    // Connections removed from the set on disconnected.
    // Copy connections to a variable to iterate over a constant container instead of
    // (virtually) simultanously mutated member variable.
    QSet<RemoteClientConnection*> activeConnections = m_activeConnections;
    for (RemoteClientConnection *client : activeConnections) {
        client->transport()->disconnectFromHost();
    }
}

void Server::loadData()
{
    const int number = 10;
    for (int i = 0; i < number; ++i) {
        LocalUser *newUser = new LocalUser();
        newUser->setPhoneNumber(QStringLiteral("%1").arg(i, 6, 10, QLatin1Char('0')));
        insertUser(newUser);
    }
}

void Server::setServerConfiguration(const DcConfiguration &config)
{
    m_dcConfiguration = config;
}

void Server::addServerConnection(RemoteServerConnection *remoteServer)
{
    m_remoteServers.insert(remoteServer);
}

quint32 Server::getDcIdForUserIdentifier(const QString &phoneNumber)
{
    if (m_phoneToUserId.contains(phoneNumber)) {
        return m_dcOption.id;
    }
    return 0;
}

void Server::setAuthorizationProvider(Authorization::Provider *provider)
{
    m_authProvider = provider;
}

void Server::setStorage(Storage *storage)
{
    m_storage = storage;
}

void Server::onNewConnection()
{
    QTcpSocket *socket = m_serverSocket->nextPendingConnection();
    if (!socket) {
        qCDebug(loggingCategoryServer) << "expected pending connection does not exist";
        return;
    }
    qCInfo(loggingCategoryServer) << this << "An incoming connection from" << socket->peerAddress().toString();
    TcpTransport *transport = new TcpTransport(socket, this);
    socket->setParent(transport);
    RemoteClientConnection *client = new RemoteClientConnection(this);
    connect(client, &BaseConnection::statusChanged, this, &Server::onClientConnectionStatusChanged);
    client->setServerRsaKey(m_key);
    client->setTransport(transport);
    client->setServerApi(this);
    client->setRpcFactories(m_rpcOperationFactories);

    m_activeConnections.insert(client);
}

void Server::onClientConnectionStatusChanged()
{
    RemoteClientConnection *client = qobject_cast<RemoteClientConnection*>(sender());
    if (client->status() == RemoteClientConnection::Status::HasDhKey) {
        if (!client->session()) {
            qCDebug(loggingCategoryServer) << Q_FUNC_INFO << "Connected a client with a new auth key"
                                              << "from" << client->transport()->remoteAddress();
        }
    } else if (client->status() == RemoteClientConnection::Status::Disconnected) {
        if (client->session()) {
            qCInfo(loggingCategoryServer) << this << __func__ << "Disconnected a client with session id"
                                          << hex << showbase << client->session()->id()
                                          << "from" << client->transport()->remoteAddress();
            client->session()->setConnection(nullptr);
        } else {
            qCInfo(loggingCategoryServer) << this << __func__ << "Disconnected a client without a session"
                                          << "from" << client->transport()->remoteAddress();
        }
        // TODO: Initiate session cleanup after session expiration time out
        m_activeConnections.remove(client);
        client->deleteLater();
    }
}

Peer Server::getPeer(const TLInputPeer &peer, const LocalUser *applicant) const
{
    switch (peer.tlType) {
    case TLValue::InputPeerEmpty:
        return Peer();
    case TLValue::InputPeerSelf:
        return Peer::fromUserId(applicant->id());
    case TLValue::InputPeerChat:
        return Peer::fromChatId(peer.chatId);
    case TLValue::InputPeerUser:
        return Peer::fromUserId(peer.userId);
    case TLValue::InputPeerChannel:
        return Peer::fromChannelId(peer.channelId);
    default:
        qCWarning(loggingCategoryServerApi) << this << __func__ << "Invalid input peer type" << peer.tlType;
        return Peer();
    };
}

MessageRecipient *Server::getRecipient(const Peer &peer, const LocalUser *applicant) const
{
    Q_UNUSED(applicant)
    switch (peer.type) {
    case Telegram::Peer::User:
        return getUser(peer.id);
    case Telegram::Peer::Chat:
        // recipient = api()->getChannel(arguments.peer.groupId, arguments.peer.accessHash);
        break;
    case Telegram::Peer::Channel:
        //recipient = api()->getChannel(arguments.peer.channelId, arguments.peer.accessHash);
        break;
    }
    return nullptr;
}

LocalUser *Server::getUser(const QString &identifier) const
{
    quint32 id = m_phoneToUserId.value(identifier);
    if (!id) {
        return nullptr;
    }
    return m_users.value(id);
}

LocalUser *Server::getUser(quint32 userId) const
{
    return m_users.value(userId);
}

AbstractUser *Server::getUser(const TLInputUser &inputUser, LocalUser *self) const
{
    switch (inputUser.tlType) {
    case TLValue::InputUserSelf:
        return self;
    case TLValue::InputUser:
        return tryAccessUser(inputUser.userId, inputUser.accessHash, self);
    case TLValue::InputUserEmpty:
        return nullptr;
    default:
        return nullptr;
    }
}

AbstractUser *Server::tryAccessUser(quint32 userId, quint64 accessHash, LocalUser *applicant) const
{
    AbstractUser *u = getAbstractUser(userId);
    // TODO: Check access hash
    return u;
}

LocalUser *Server::addUser(const QString &identifier)
{
    qCDebug(loggingCategoryServerApi) << Q_FUNC_INFO << identifier;
    LocalUser *user = new LocalUser();
    user->setPhoneNumber(identifier);
    user->setDcId(dcId());
    insertUser(user);
    return user;
}

Session *Server::createSession(quint64 authId, const QByteArray &authKey, const QString &address)
{
    Session *session = new Session();
    session->authId = authId;
    session->authKey = authKey;
    session->ip = address;
    m_authIdToSession.insert(authId, session);
    return session;
}

Session *Server::getSessionByAuthId(quint64 authKeyId) const
{
    return m_authIdToSession.value(authKeyId);
}

void Server::bindUserSession(LocalUser *user, Session *session)
{
    user->addSession(session);
}

void Server::queueUpdates(const QVector<UpdateNotification> &notifications)
{
    for (const UpdateNotification &notification : notifications) {
        LocalUser *recipient = getUser(notification.userId);
        if (!recipient) {
            qWarning() << Q_FUNC_INFO << "Invalid user!" << notification.userId;
        }

        TLUpdates updates;
        updates.tlType = TLValue::Updates;
        updates.date = notification.date;

        QSet<Peer> interestingPeers;
        switch (notification.type) {
        case UpdateNotification::Type::NewMessage: {
            TLUpdate update;
            update.tlType = TLValue::UpdateNewMessage;

            const quint64 globalMessageId = recipient->getPostBox()->getMessageGlobalId(notification.messageId);
            const MessageData *messageData = storage()->getMessage(globalMessageId);

            if (!messageData) {
                qWarning() << Q_FUNC_INFO << "no message";
                continue;
            }
            Utils::setupTLMessage(&update.message, messageData, notification.messageId, recipient);
            update.pts = notification.pts;
            update.ptsCount = 1;

            interestingPeers.insert(messageData->toPeer());
            if (update.message.fromId) {
                interestingPeers.insert(Peer::fromUserId(update.message.fromId));
            }

            updates.seq = 0; // ??
            updates.updates = { update };
        }
            break;
        case UpdateNotification::Type::Invalid:
            break;
        }

        Utils::setupTLPeers(&updates, interestingPeers, this, recipient);
        for (Session *session : recipient->activeSessions()) {
            if (session == notification.excludeSession) {
                continue;
            }
            session->rpcLayer()->sendUpdates(updates);
        }
    }
}

void Server::insertUser(LocalUser *user)
{
    qCDebug(loggingCategoryServerApi) << Q_FUNC_INFO << user << user->phoneNumber() << user->id();
    m_users.insert(user->id(), user);
    m_phoneToUserId.insert(user->phoneNumber(), user->id());
    for (Session *session : user->sessions()) {
        m_authIdToSession.insert(session->authId, session);
    }
}

PhoneStatus Server::getPhoneStatus(const QString &identifier) const
{
    PhoneStatus result;
    AbstractUser *user = getAbstractUser(identifier);
    if (user) {
        result.online = user->isOnline();
        result.dcId = user->dcId();
    }
    return result;
}

PasswordInfo Server::getPassword(const QString &identifier)
{
    PasswordInfo result;
    LocalUser *user = getUser(identifier);
    if (user && user->hasPassword()) {
        result.currentSalt = user->passwordSalt();
        result.hint = user->passwordHint();
    }
    return result;
}

bool Server::checkPassword(const QString &identifier, const QByteArray &hash)
{
    LocalUser *user = getUser(identifier);
    if (user && user->hasPassword()) {
        return user->passwordHash() == hash;
    }
    return false;

}

bool Server::identifierIsValid(const QString &identifier) const
{
    const bool result = identifier.length() > 4;
    qCDebug(loggingCategoryServerApi) << "identifierIsValid(" << identifier << "):" << result;
    return result;
}

QString Server::normalizeIdentifier(const QString &identifier) const
{
    if (identifier.startsWith(QLatin1Char('+'))) {
        return identifier.mid(1);
    }
    return identifier;
}

AbstractUser *Server::getAbstractUser(quint32 userId) const
{
    AbstractUser *user = getUser(userId);
    if (!user) {
        user = getRemoteUser(userId);
    }
    return user;
}

AbstractUser *Server::getAbstractUser(const QString &identifier) const
{
    AbstractUser *user = getUser(identifier);
    if (!user) {
        user = getRemoteUser(identifier);
    }
    return user;
}

AbstractUser *Server::getRemoteUser(quint32 userId) const
{
    for (RemoteServerConnection *remoteServer : m_remoteServers) {
        AbstractUser *u = remoteServer->api()->getUser(userId);
        if (u) {
            return u;
        }
    }
    return nullptr;
}

AbstractUser *Server::getRemoteUser(const QString &identifier) const
{
    for (RemoteServerConnection *remoteServer : m_remoteServers) {
        AbstractUser *u = remoteServer->api()->getUser(identifier);
        if (u) {
            return u;
        }
    }
    return nullptr;
}

} // Server namespace

} // Telegram namespace
<commit_msg>Server: Implement ReadHistory{In,Out}box update<commit_after>#include "TelegramServer.hpp"

#include <QLoggingCategory>
#include <QTcpServer>
#include <QTcpSocket>

#include "ApiUtils.hpp"
#include "TelegramServerUser.hpp"
#include "RemoteClientConnection.hpp"
#include "RemoteServerConnection.hpp"
#include "Session.hpp"

#include "CServerTcpTransport.hpp"

// Generated RPC Operation Factory includes
#include "AccountOperationFactory.hpp"
#include "AuthOperationFactory.hpp"
#include "BotsOperationFactory.hpp"
#include "ChannelsOperationFactory.hpp"
#include "ContactsOperationFactory.hpp"
#include "HelpOperationFactory.hpp"
#include "LangpackOperationFactory.hpp"
#include "MessagesOperationFactory.hpp"
#include "PaymentsOperationFactory.hpp"
#include "PhoneOperationFactory.hpp"
#include "PhotosOperationFactory.hpp"
#include "StickersOperationFactory.hpp"
#include "UpdatesOperationFactory.hpp"
#include "UploadOperationFactory.hpp"
#include "UsersOperationFactory.hpp"
// End of generated RPC Operation Factory includes

#include "ServerMessageData.hpp"
#include "ServerRpcLayer.hpp"
#include "ServerUtils.hpp"
#include "Storage.hpp"
#include "Debug.hpp"

Q_LOGGING_CATEGORY(loggingCategoryServer, "telegram.server.main", QtInfoMsg)
Q_LOGGING_CATEGORY(loggingCategoryServerApi, "telegram.server.api", QtWarningMsg)

namespace Telegram {

namespace Server {

Server::Server(QObject *parent) :
    QObject(parent)
{
    m_rpcOperationFactories = {
        // Generated RPC Operation Factory initialization
        new AccountOperationFactory(),
        new AuthOperationFactory(),
        new BotsOperationFactory(),
        new ChannelsOperationFactory(),
        new ContactsOperationFactory(),
        new HelpOperationFactory(),
        new LangpackOperationFactory(),
        new MessagesOperationFactory(),
        new PaymentsOperationFactory(),
        new PhoneOperationFactory(),
        new PhotosOperationFactory(),
        new StickersOperationFactory(),
        new UpdatesOperationFactory(),
        new UploadOperationFactory(),
        new UsersOperationFactory(),
        // End of generated RPC Operation Factory initialization
    };
    m_serverSocket = new QTcpServer(this);
    connect(m_serverSocket, &QTcpServer::newConnection, this, &Server::onNewConnection);
}

void Server::setDcOption(const DcOption &option)
{
    m_dcOption = option;
}

void Server::setServerPrivateRsaKey(const Telegram::RsaKey &key)
{
    m_key = key;
}

bool Server::start()
{
    if (!m_serverSocket->listen(QHostAddress(m_dcOption.address), m_dcOption.port)) {
        qCCritical(loggingCategoryServer).noquote().nospace() << "Unable to listen port " << m_dcOption.port
                                                              << " ("  << m_serverSocket->serverError() << ")";
        return false;
    }
    qCInfo(loggingCategoryServer).nospace().noquote() << this << " start server (DC " << m_dcOption.id << ") "
                                                      << "on " << m_dcOption.address << ":" << m_dcOption.port
                                                      << "; Key:" << hex << showbase << m_key.fingerprint;
    return true;
}

void Server::stop()
{
    qCInfo(loggingCategoryServer).nospace().noquote() << this << " stop server (DC " << m_dcOption.id << ") "
                                                      << "on " << m_dcOption.address << ":" << m_dcOption.port;
    if (m_serverSocket) {
        m_serverSocket->close();
    }

    // Connections removed from the set on disconnected.
    // Copy connections to a variable to iterate over a constant container instead of
    // (virtually) simultanously mutated member variable.
    QSet<RemoteClientConnection*> activeConnections = m_activeConnections;
    for (RemoteClientConnection *client : activeConnections) {
        client->transport()->disconnectFromHost();
    }
}

void Server::loadData()
{
    const int number = 10;
    for (int i = 0; i < number; ++i) {
        LocalUser *newUser = new LocalUser();
        newUser->setPhoneNumber(QStringLiteral("%1").arg(i, 6, 10, QLatin1Char('0')));
        insertUser(newUser);
    }
}

void Server::setServerConfiguration(const DcConfiguration &config)
{
    m_dcConfiguration = config;
}

void Server::addServerConnection(RemoteServerConnection *remoteServer)
{
    m_remoteServers.insert(remoteServer);
}

quint32 Server::getDcIdForUserIdentifier(const QString &phoneNumber)
{
    if (m_phoneToUserId.contains(phoneNumber)) {
        return m_dcOption.id;
    }
    return 0;
}

void Server::setAuthorizationProvider(Authorization::Provider *provider)
{
    m_authProvider = provider;
}

void Server::setStorage(Storage *storage)
{
    m_storage = storage;
}

void Server::onNewConnection()
{
    QTcpSocket *socket = m_serverSocket->nextPendingConnection();
    if (!socket) {
        qCDebug(loggingCategoryServer) << "expected pending connection does not exist";
        return;
    }
    qCInfo(loggingCategoryServer) << this << "An incoming connection from" << socket->peerAddress().toString();
    TcpTransport *transport = new TcpTransport(socket, this);
    socket->setParent(transport);
    RemoteClientConnection *client = new RemoteClientConnection(this);
    connect(client, &BaseConnection::statusChanged, this, &Server::onClientConnectionStatusChanged);
    client->setServerRsaKey(m_key);
    client->setTransport(transport);
    client->setServerApi(this);
    client->setRpcFactories(m_rpcOperationFactories);

    m_activeConnections.insert(client);
}

void Server::onClientConnectionStatusChanged()
{
    RemoteClientConnection *client = qobject_cast<RemoteClientConnection*>(sender());
    if (client->status() == RemoteClientConnection::Status::HasDhKey) {
        if (!client->session()) {
            qCDebug(loggingCategoryServer) << Q_FUNC_INFO << "Connected a client with a new auth key"
                                              << "from" << client->transport()->remoteAddress();
        }
    } else if (client->status() == RemoteClientConnection::Status::Disconnected) {
        if (client->session()) {
            qCInfo(loggingCategoryServer) << this << __func__ << "Disconnected a client with session id"
                                          << hex << showbase << client->session()->id()
                                          << "from" << client->transport()->remoteAddress();
            client->session()->setConnection(nullptr);
        } else {
            qCInfo(loggingCategoryServer) << this << __func__ << "Disconnected a client without a session"
                                          << "from" << client->transport()->remoteAddress();
        }
        // TODO: Initiate session cleanup after session expiration time out
        m_activeConnections.remove(client);
        client->deleteLater();
    }
}

Peer Server::getPeer(const TLInputPeer &peer, const LocalUser *applicant) const
{
    switch (peer.tlType) {
    case TLValue::InputPeerEmpty:
        return Peer();
    case TLValue::InputPeerSelf:
        return Peer::fromUserId(applicant->id());
    case TLValue::InputPeerChat:
        return Peer::fromChatId(peer.chatId);
    case TLValue::InputPeerUser:
        return Peer::fromUserId(peer.userId);
    case TLValue::InputPeerChannel:
        return Peer::fromChannelId(peer.channelId);
    default:
        qCWarning(loggingCategoryServerApi) << this << __func__ << "Invalid input peer type" << peer.tlType;
        return Peer();
    };
}

MessageRecipient *Server::getRecipient(const Peer &peer, const LocalUser *applicant) const
{
    Q_UNUSED(applicant)
    switch (peer.type) {
    case Telegram::Peer::User:
        return getUser(peer.id);
    case Telegram::Peer::Chat:
        // recipient = api()->getChannel(arguments.peer.groupId, arguments.peer.accessHash);
        break;
    case Telegram::Peer::Channel:
        //recipient = api()->getChannel(arguments.peer.channelId, arguments.peer.accessHash);
        break;
    }
    return nullptr;
}

LocalUser *Server::getUser(const QString &identifier) const
{
    quint32 id = m_phoneToUserId.value(identifier);
    if (!id) {
        return nullptr;
    }
    return m_users.value(id);
}

LocalUser *Server::getUser(quint32 userId) const
{
    return m_users.value(userId);
}

AbstractUser *Server::getUser(const TLInputUser &inputUser, LocalUser *self) const
{
    switch (inputUser.tlType) {
    case TLValue::InputUserSelf:
        return self;
    case TLValue::InputUser:
        return tryAccessUser(inputUser.userId, inputUser.accessHash, self);
    case TLValue::InputUserEmpty:
        return nullptr;
    default:
        return nullptr;
    }
}

AbstractUser *Server::tryAccessUser(quint32 userId, quint64 accessHash, LocalUser *applicant) const
{
    AbstractUser *u = getAbstractUser(userId);
    // TODO: Check access hash
    return u;
}

LocalUser *Server::addUser(const QString &identifier)
{
    qCDebug(loggingCategoryServerApi) << Q_FUNC_INFO << identifier;
    LocalUser *user = new LocalUser();
    user->setPhoneNumber(identifier);
    user->setDcId(dcId());
    insertUser(user);
    return user;
}

Session *Server::createSession(quint64 authId, const QByteArray &authKey, const QString &address)
{
    Session *session = new Session();
    session->authId = authId;
    session->authKey = authKey;
    session->ip = address;
    m_authIdToSession.insert(authId, session);
    return session;
}

Session *Server::getSessionByAuthId(quint64 authKeyId) const
{
    return m_authIdToSession.value(authKeyId);
}

void Server::bindUserSession(LocalUser *user, Session *session)
{
    user->addSession(session);
}

void Server::queueUpdates(const QVector<UpdateNotification> &notifications)
{
    for (const UpdateNotification &notification : notifications) {
        LocalUser *recipient = getUser(notification.userId);
        if (!recipient) {
            qWarning() << Q_FUNC_INFO << "Invalid user!" << notification.userId;
        }

        TLUpdates updates;
        updates.tlType = TLValue::Updates;
        updates.date = notification.date;

        QSet<Peer> interestingPeers;
        switch (notification.type) {
        case UpdateNotification::Type::NewMessage: {
            TLUpdate update;
            update.tlType = TLValue::UpdateNewMessage;

            const quint64 globalMessageId = recipient->getPostBox()->getMessageGlobalId(notification.messageId);
            const MessageData *messageData = storage()->getMessage(globalMessageId);

            if (!messageData) {
                qWarning() << Q_FUNC_INFO << "no message";
                continue;
            }
            Utils::setupTLMessage(&update.message, messageData, notification.messageId, recipient);
            update.pts = notification.pts;
            update.ptsCount = 1;

            interestingPeers.insert(messageData->toPeer());
            if (update.message.fromId) {
                interestingPeers.insert(Peer::fromUserId(update.message.fromId));
            }

            updates.seq = 0; // ??
            updates.updates = { update };
        }
            break;
        case UpdateNotification::Type::ReadInbox:
        case UpdateNotification::Type::ReadOutbox:
        {
            TLUpdate update;
            update.tlType = notification.type == UpdateNotification::Type::ReadInbox
                      ? TLValue::UpdateReadHistoryInbox
                      : TLValue::UpdateReadHistoryOutbox;
            update.pts = notification.pts;
            update.ptsCount = 1;
            update.peer = Telegram::Utils::toTLPeer(notification.dialogPeer);
            update.maxId = notification.messageId;

            updates.seq = 0; // ??
            updates.updates = { update };
        }
            break;
        case UpdateNotification::Type::Invalid:
            break;
        }

        Utils::setupTLPeers(&updates, interestingPeers, this, recipient);
        for (Session *session : recipient->activeSessions()) {
            if (session == notification.excludeSession) {
                continue;
            }
            session->rpcLayer()->sendUpdates(updates);
        }
    }
}

void Server::insertUser(LocalUser *user)
{
    qCDebug(loggingCategoryServerApi) << Q_FUNC_INFO << user << user->phoneNumber() << user->id();
    m_users.insert(user->id(), user);
    m_phoneToUserId.insert(user->phoneNumber(), user->id());
    for (Session *session : user->sessions()) {
        m_authIdToSession.insert(session->authId, session);
    }
}

PhoneStatus Server::getPhoneStatus(const QString &identifier) const
{
    PhoneStatus result;
    AbstractUser *user = getAbstractUser(identifier);
    if (user) {
        result.online = user->isOnline();
        result.dcId = user->dcId();
    }
    return result;
}

PasswordInfo Server::getPassword(const QString &identifier)
{
    PasswordInfo result;
    LocalUser *user = getUser(identifier);
    if (user && user->hasPassword()) {
        result.currentSalt = user->passwordSalt();
        result.hint = user->passwordHint();
    }
    return result;
}

bool Server::checkPassword(const QString &identifier, const QByteArray &hash)
{
    LocalUser *user = getUser(identifier);
    if (user && user->hasPassword()) {
        return user->passwordHash() == hash;
    }
    return false;

}

bool Server::identifierIsValid(const QString &identifier) const
{
    const bool result = identifier.length() > 4;
    qCDebug(loggingCategoryServerApi) << "identifierIsValid(" << identifier << "):" << result;
    return result;
}

QString Server::normalizeIdentifier(const QString &identifier) const
{
    if (identifier.startsWith(QLatin1Char('+'))) {
        return identifier.mid(1);
    }
    return identifier;
}

AbstractUser *Server::getAbstractUser(quint32 userId) const
{
    AbstractUser *user = getUser(userId);
    if (!user) {
        user = getRemoteUser(userId);
    }
    return user;
}

AbstractUser *Server::getAbstractUser(const QString &identifier) const
{
    AbstractUser *user = getUser(identifier);
    if (!user) {
        user = getRemoteUser(identifier);
    }
    return user;
}

AbstractUser *Server::getRemoteUser(quint32 userId) const
{
    for (RemoteServerConnection *remoteServer : m_remoteServers) {
        AbstractUser *u = remoteServer->api()->getUser(userId);
        if (u) {
            return u;
        }
    }
    return nullptr;
}

AbstractUser *Server::getRemoteUser(const QString &identifier) const
{
    for (RemoteServerConnection *remoteServer : m_remoteServers) {
        AbstractUser *u = remoteServer->api()->getUser(identifier);
        if (u) {
            return u;
        }
    }
    return nullptr;
}

} // Server namespace

} // Telegram namespace
<|endoftext|>


<commit_before>#include "atoms_md.h"
#include "xmath.h"
/*--------------------------------------------

 --------------------------------------------*/
AtomsMD::AtomsMD(MPI_Comm& world):
Atoms(world),
S_pe{DESIG2(__dim__,__dim__,NAN)},
pe(NAN)
{
    elem=new Vec<elem_type>(this,1,"elem");
    x_d=new Vec<type0>(this,__dim__,"x_d");
    x_d->empty(0.0);
}
/*--------------------------------------------

 --------------------------------------------*/
AtomsMD::~AtomsMD()
{
    delete x_d;
    delete elem;
}
/*--------------------------------------------

 --------------------------------------------*/
AtomsMD& AtomsMD::operator=(const Atoms& r)
{
    elements=r.elements;
    comm=r.comm;
    natms_lcl=r.natms_lcl;
    natms_ph=r.natms_ph;
    natms=r.natms;
    step=r.step;

    max_cut=r.max_cut;
    kB=r.kB;
    hP=r.hP;

    vol=r.vol;
    memcpy(depth_inv,r.depth_inv,__dim__*sizeof(type0));
    memcpy(__h,r.__h,__nvoigt__*sizeof(type0));
    memcpy(__b,r.__b,__nvoigt__*sizeof(type0));
    memcpy(&H[0][0],&r.H[0][0],__dim__*__dim__*sizeof(type0));
    memcpy(&B[0][0],&r.B[0][0],__dim__*__dim__*sizeof(type0));

    for(int i=0;i<nvecs;i++)
        if(!vecs[i]->is_empty())
            vecs[i]->resize(natms_lcl);
    memcpy(x->begin(),r.x->begin(),natms_lcl*__dim__*sizeof(type0));
    memcpy(id->begin(),r.id->begin(),natms_lcl*sizeof(unsigned int));
    return* this;
}
/*--------------------------------------------
 x2s
 --------------------------------------------*/
void AtomsMD::x_d2s_d_dump()
{
    Algebra::X2S_NOCORR<__dim__>(__b,natms,x_d->begin_dump());
}
/*--------------------------------------------

 --------------------------------------------*/
#include "random.h"
#include "elements.h"
#include "xmath.h"
void AtomsMD::create_T(type0 T,int seed)
{
    x_d->fill();
    Random rand(seed+comm_rank);
    type0* __x_d=x_d->begin();
    type0* m=elements.masses;
    elem_type* __elem=elem->begin();
    type0 fac;
    if(!x_dof->is_empty())
    {
        bool* __dof=x_dof->begin();
        for(int i=0;i<natms_lcl;i++)
        {
            fac=sqrt(kB*T/m[*__elem]);
            Algebra::Do<__dim__>::func([&fac,&rand,&__x_d,__dof,this](const int i){if(__dof[i]) __x_d[i]=rand.gaussian()*fac;});
            __x_d+=__dim__;
            __dof+=__dim__;
            ++__elem;
        }
    }
    else
    {
        for(int i=0;i<natms_lcl;i++)
        {
            fac=sqrt(kB*T/m[*__elem]);
            Algebra::Do<__dim__>::func([&fac,&rand,&__x_d,this](const int i){__x_d[i]=rand.gaussian()*fac;});
            __x_d+=__dim__;
            ++__elem;
        }
    }
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::DO(PyObject* op)
{
    class Func_x
    {
    public:
        void operator()(type0*,type0*)
        {};
    };
    Func_x func_x;
    VecPy<type0,Func_x> x_vec_py(x,func_x);

    class Func_x_d
    {
    public:
        void operator()(type0*,type0*)
        {};
    };
    Func_x_d func_x_d;
    VecPy<type0,Func_x_d> x_d_vec_py(x_d,func_x_d);

    class Func_id
    {
    public:
        void operator()(unsigned int* old_val,unsigned int* new_val)
        {
            if(*old_val!=*new_val)
                throw std::string("id of atoms cannot be changed");
        };
    };
    Func_id func_id;
    VecPy<unsigned int,Func_id> id_vec_py(id,func_id);

    class Func_elem
    {
    public:
        elem_type nelem;
        Func_elem(elem_type __nelem):nelem(__nelem){}
        void operator()(elem_type* old_val,elem_type* new_val)
        {
            if(*new_val>=nelem)
                throw std::string("elem of atoms should be less than ")+Print::to_string(static_cast<int>(nelem));
        };
    };
    Func_elem func_elem(elements.__nelems);
    VecPy<elem_type,Func_elem> elem_vec_py(elem,func_elem);

    class Func_dof
    {
    public:
        void operator()(bool*,bool*)
        {
        };
    };
    Func_dof func_dof;
    VecPy<bool,Func_dof> dof_vec_py(x_dof,func_dof);
    try
    {
        VecPyFunc::Do(this,op,id_vec_py,x_vec_py,x_d_vec_py,elem_vec_py,dof_vec_py);
    }
    catch(std::string& err_msg)
    {
        throw err_msg;
    }

    if(x_vec_py.inc) this->reset_domain();

}
/*------------------------------------------------------------------------------------------------------------------------------------

 ------------------------------------------------------------------------------------------------------------------------------------*/
#include "ff_styles.h"
#include "import_styles.h"
PyObject* AtomsMD::__new__(PyTypeObject* type,PyObject* args,PyObject* kwds)
{
    Object* __self=reinterpret_cast<Object*>(type->tp_alloc(type,0));
    PyObject* self=reinterpret_cast<PyObject*>(__self);
    return self;
}
/*--------------------------------------------

 --------------------------------------------*/
int AtomsMD::__init__(PyObject* self,PyObject* args,PyObject* kwds)
{
    FuncAPI<> func("__init__");
    if(func(args,kwds)==-1) return -1;
    Object* __self=reinterpret_cast<Object*>(self);
    __self->atoms=NULL;
    __self->ff=NULL;
    return 0;
}
/*--------------------------------------------

 --------------------------------------------*/
PyObject* AtomsMD::__alloc__(PyTypeObject* type,Py_ssize_t)
{
    Object* __self=new Object;
    Py_TYPE(__self)=type;
    Py_REFCNT(__self)=1;
    __self->atoms=NULL;
    __self->ff=NULL;
    return reinterpret_cast<PyObject*>(__self);
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::__dealloc__(PyObject* self)
{
    Object* __self=reinterpret_cast<Object*>(self);
    delete __self->ff;
    delete __self->atoms;
    delete __self;
}
/*--------------------------------------------*/
PyTypeObject AtomsMD::TypeObject ={PyObject_HEAD_INIT(NULL)};
/*--------------------------------------------*/
int AtomsMD::setup_tp()
{
    TypeObject.tp_name="mapp.md.atoms";
    TypeObject.tp_doc="container class";

    TypeObject.tp_flags=Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
    TypeObject.tp_basicsize=sizeof(Object);

    TypeObject.tp_new=__new__;
    TypeObject.tp_init=__init__;
    TypeObject.tp_alloc=__alloc__;
    TypeObject.tp_dealloc=__dealloc__;

    setup_tp_getset();
    TypeObject.tp_getset=getset;
    setup_tp_methods();
    TypeObject.tp_methods=methods;

    int ichk=PyType_Ready(&TypeObject);
    if(ichk<0) return ichk;
    Py_INCREF(&TypeObject);
    return ichk;
}
/*--------------------------------------------*/
PyGetSetDef AtomsMD::getset[]=EmptyPyGetSetDef(15);
/*--------------------------------------------*/
void AtomsMD::setup_tp_getset()
{
    getset_step(getset[0]);
    getset_hP(getset[1]);
    getset_kB(getset[2]);
    getset_H(getset[3]);
    getset_B(getset[4]);
    getset_vol(getset[5]);
    getset_elems(getset[6]);
    getset_skin(getset[7]);
    getset_comm_rank(getset[8]);
    getset_comm_size(getset[9]);
    getset_comm_coords(getset[10]);
    getset_comm_dims(getset[11]);
    getset_pe(getset[12]);
    getset_S_pe(getset[13]);
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::getset_S_pe(PyGetSetDef& getset)
{
    getset.name=(char*)"S_pe";
    getset.doc=(char*)R"---(
    (double) potential energy stress

    Potential energy part of virial stress
    )---";
    getset.get=[](PyObject* self,void*)->PyObject*
    {
        return var<type0[__dim__][__dim__]>::build(reinterpret_cast<Object*>(self)->atoms->S_pe);
    };
    getset.set=[](PyObject* self,PyObject* val,void*)->int
    {
        PyErr_SetString(PyExc_TypeError,"readonly attribute");
        return -1;
    };
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::getset_pe(PyGetSetDef& getset)
{
    getset.name=(char*)"pe";
    getset.doc=(char*)R"---(
    (double) potential energy

    Potential energy
    )---";
    getset.get=[](PyObject* self,void*)->PyObject*
    {
        return var<type0>::build(reinterpret_cast<Object*>(self)->atoms->pe);
    };
    getset.set=[](PyObject* self,PyObject* val,void*)->int
    {
        PyErr_SetString(PyExc_TypeError,"readonly attribute");
        return -1;
    };
}
/*--------------------------------------------*/
#ifdef POTFIT
PyMethodDef AtomsMD::methods[]=EmptyPyMethodDef(16);
#else
PyMethodDef AtomsMD::methods[]=EmptyPyMethodDef(13);
#endif
/*--------------------------------------------*/
void AtomsMD::setup_tp_methods()
{
    ml_do(methods[0]);
    ml_mul(methods[1]);
    ml_strain(methods[2]);
    ml_create_temp(methods[3]);
    ml_add_elem(methods[4]);
    ForceFieldLJ::ml_new(methods[5]);
    ForceFieldEAM::ml_new(methods[6],methods[7],methods[8]);
    ForceFieldFS::ml_new(methods[9]);
    ForceFieldEAMFunc::ml_new(methods[10]);
    ImportCFGMD::ml_import(methods[11]);
#ifdef POTFIT
    ForceFieldEAMFit::ml_new(methods[12]);
    ForceFieldEAMFitO::ml_new(methods[13]);
    ForceFieldEAMPotFitAckOgata::ml_new(methods[14]);
#endif
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::ml_create_temp(PyMethodDef& tp_method)
{
    tp_method.ml_flags=METH_VARARGS | METH_KEYWORDS;
    tp_method.ml_name="create_temp";
    tp_method.ml_meth=(PyCFunction)(PyCFunctionWithKeywords)(
    [](PyObject* self,PyObject* args,PyObject* kwds)->PyObject*
    {
        FuncAPI<type0,int> f("create_temp",{"temp","seed"});
        f.logics<0>()[0]=VLogics("gt",0.0);
        f.logics<1>()[0]=VLogics("gt",0);
        if(f(args,kwds)) return NULL;

        AtomsMD::Object* __self=reinterpret_cast<AtomsMD::Object*>(self);
        __self->atoms->create_T(f.val<0>(),f.val<1>());
        Py_RETURN_NONE;
    });
    tp_method.ml_doc=R"---(
    create_temp(temp,seed)

    Create a random velcity field

    Parameters
    ----------
    temp : double
       Temperature
    seed : int
       random seed

    Returns
    -------
    None

    )---";
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::ml_add_elem(PyMethodDef& tp_method)
{
    tp_method.ml_flags=METH_VARARGS | METH_KEYWORDS;
    tp_method.ml_name="add_elem";
    tp_method.ml_meth=(PyCFunction)(PyCFunctionWithKeywords)(
    [](PyObject* self,PyObject* args,PyObject* kwds)->PyObject*
    {
        FuncAPI<std::string,type0> f("add_elem",{"elem","mass"});
        f.logics<1>()[0]=VLogics("gt",0.0);
        if(f(args,kwds)) return NULL;

        AtomsMD::Object* __self=reinterpret_cast<AtomsMD::Object*>(self);
        __self->atoms->elements.add_type(f.val<1>(),f.val<0>().c_str());

        Py_RETURN_NONE;
    });
    tp_method.ml_doc=R"---(
    add_elem(elem,mass)

    Add a new element to the system

    Parameters
    ----------
    elem : string
       New element
    seed : double
       Mass of the element

    Returns
    -------
    None

    )---";
}

<commit_msg>nothing important<commit_after>#include "atoms_md.h"
#include "xmath.h"
/*--------------------------------------------

 --------------------------------------------*/
AtomsMD::AtomsMD(MPI_Comm& world):
Atoms(world),
S_pe{DESIG2(__dim__,__dim__,NAN)},
pe(NAN)
{
    elem=new Vec<elem_type>(this,1,"elem");
    x_d=new Vec<type0>(this,__dim__,"x_d");
    x_d->empty(0.0);
}
/*--------------------------------------------

 --------------------------------------------*/
AtomsMD::~AtomsMD()
{
    delete x_d;
    delete elem;
}
/*--------------------------------------------

 --------------------------------------------*/
AtomsMD& AtomsMD::operator=(const Atoms& r)
{
    elements=r.elements;
    comm=r.comm;
    natms_lcl=r.natms_lcl;
    natms_ph=r.natms_ph;
    natms=r.natms;
    step=r.step;

    max_cut=r.max_cut;
    kB=r.kB;
    hP=r.hP;

    vol=r.vol;
    memcpy(depth_inv,r.depth_inv,__dim__*sizeof(type0));
    memcpy(__h,r.__h,__nvoigt__*sizeof(type0));
    memcpy(__b,r.__b,__nvoigt__*sizeof(type0));
    memcpy(&H[0][0],&r.H[0][0],__dim__*__dim__*sizeof(type0));
    memcpy(&B[0][0],&r.B[0][0],__dim__*__dim__*sizeof(type0));

    for(int i=0;i<nvecs;i++)
        if(!vecs[i]->is_empty())
            vecs[i]->resize(natms_lcl);
    memcpy(x->begin(),r.x->begin(),natms_lcl*__dim__*sizeof(type0));
    memcpy(id->begin(),r.id->begin(),natms_lcl*sizeof(unsigned int));
    return* this;
}
/*--------------------------------------------
 x2s
 --------------------------------------------*/
void AtomsMD::x_d2s_d_dump()
{
    Algebra::X2S_NOCORR<__dim__>(__b,natms,x_d->begin_dump());
}
/*--------------------------------------------

 --------------------------------------------*/
#include "random.h"
#include "elements.h"
#include "xmath.h"
void AtomsMD::create_T(type0 T,int seed)
{
    x_d->fill();
    Random rand(seed+comm_rank);
    type0* __x_d=x_d->begin();
    type0* m=elements.masses;
    elem_type* __elem=elem->begin();
    type0 fac;
    if(!x_dof->is_empty())
    {
        bool* __dof=x_dof->begin();
        for(int i=0;i<natms_lcl;i++)
        {
            fac=sqrt(kB*T/m[*__elem]);
            Algebra::Do<__dim__>::func([&fac,&rand,&__x_d,__dof,this](const int i){if(__dof[i]) __x_d[i]=rand.gaussian()*fac;});
            __x_d+=__dim__;
            __dof+=__dim__;
            ++__elem;
        }
    }
    else
    {
        for(int i=0;i<natms_lcl;i++)
        {
            fac=sqrt(kB*T/m[*__elem]);
            Algebra::Do<__dim__>::func([&fac,&rand,&__x_d,this](const int i){__x_d[i]=rand.gaussian()*fac;});
            __x_d+=__dim__;
            ++__elem;
        }
    }
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::DO(PyObject* op)
{
    class Func_x
    {
    public:
        void operator()(type0*,type0*)
        {};
    };
    Func_x func_x;
    VecPy<type0,Func_x> x_vec_py(x,func_x);

    class Func_x_d
    {
    public:
        void operator()(type0*,type0*)
        {};
    };
    Func_x_d func_x_d;
    VecPy<type0,Func_x_d> x_d_vec_py(x_d,func_x_d);

    class Func_id
    {
    public:
        void operator()(unsigned int* old_val,unsigned int* new_val)
        {
            if(*old_val!=*new_val)
                throw std::string("id of atoms cannot be changed");
        };
    };
    Func_id func_id;
    VecPy<unsigned int,Func_id> id_vec_py(id,func_id);

    class Func_elem
    {
    public:
        elem_type nelem;
        Func_elem(elem_type __nelem):nelem(__nelem){}
        void operator()(elem_type* old_val,elem_type* new_val)
        {
            if(*new_val>=nelem)
                throw std::string("elem of atoms should be less than ")+Print::to_string(static_cast<int>(nelem));
        };
    };
    Func_elem func_elem(elements.__nelems);
    VecPy<elem_type,Func_elem> elem_vec_py(elem,func_elem);

    class Func_dof
    {
    public:
        void operator()(bool*,bool*)
        {
        };
    };
    Func_dof func_dof;
    VecPy<bool,Func_dof> dof_vec_py(x_dof,func_dof);
    try
    {
        VecPyFunc::Do(this,op,id_vec_py,x_vec_py,x_d_vec_py,elem_vec_py,dof_vec_py);
    }
    catch(std::string& err_msg)
    {
        throw err_msg;
    }

    if(x_vec_py.inc) this->reset_domain();

}
/*------------------------------------------------------------------------------------------------------------------------------------

 ------------------------------------------------------------------------------------------------------------------------------------*/
#include "ff_styles.h"
#include "import_styles.h"
PyObject* AtomsMD::__new__(PyTypeObject* type,PyObject* args,PyObject* kwds)
{
    Object* __self=reinterpret_cast<Object*>(type->tp_alloc(type,0));
    PyObject* self=reinterpret_cast<PyObject*>(__self);
    return self;
}
/*--------------------------------------------

 --------------------------------------------*/
int AtomsMD::__init__(PyObject* self,PyObject* args,PyObject* kwds)
{
    FuncAPI<> func("__init__");
    if(func(args,kwds)==-1) return -1;
    Object* __self=reinterpret_cast<Object*>(self);
    __self->atoms=NULL;
    __self->ff=NULL;
    return 0;
}
/*--------------------------------------------

 --------------------------------------------*/
PyObject* AtomsMD::__alloc__(PyTypeObject* type,Py_ssize_t)
{
    Object* __self=new Object;
    Py_TYPE(__self)=type;
    Py_REFCNT(__self)=1;
    __self->atoms=NULL;
    __self->ff=NULL;
    return reinterpret_cast<PyObject*>(__self);
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::__dealloc__(PyObject* self)
{
    Object* __self=reinterpret_cast<Object*>(self);
    delete __self->ff;
    delete __self->atoms;
    delete __self;
}
/*--------------------------------------------*/
PyTypeObject AtomsMD::TypeObject ={PyObject_HEAD_INIT(NULL)};
/*--------------------------------------------*/
int AtomsMD::setup_tp()
{
    TypeObject.tp_name="mapp.md.atoms";
    TypeObject.tp_doc="container class";

    TypeObject.tp_flags=Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
    TypeObject.tp_basicsize=sizeof(Object);

    TypeObject.tp_new=__new__;
    TypeObject.tp_init=__init__;
    TypeObject.tp_alloc=__alloc__;
    TypeObject.tp_dealloc=__dealloc__;

    setup_tp_getset();
    TypeObject.tp_getset=getset;
    setup_tp_methods();
    TypeObject.tp_methods=methods;

    int ichk=PyType_Ready(&TypeObject);
    if(ichk<0) return ichk;
    Py_INCREF(&TypeObject);
    return ichk;
}
/*--------------------------------------------*/
PyGetSetDef AtomsMD::getset[]=EmptyPyGetSetDef(15);
/*--------------------------------------------*/
void AtomsMD::setup_tp_getset()
{
    getset_step(getset[0]);
    getset_hP(getset[1]);
    getset_kB(getset[2]);
    getset_H(getset[3]);
    getset_B(getset[4]);
    getset_vol(getset[5]);
    getset_elems(getset[6]);
    getset_skin(getset[7]);
    getset_comm_rank(getset[8]);
    getset_comm_size(getset[9]);
    getset_comm_coords(getset[10]);
    getset_comm_dims(getset[11]);
    getset_pe(getset[12]);
    getset_S_pe(getset[13]);
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::getset_S_pe(PyGetSetDef& getset)
{
    getset.name=(char*)"S_pe";
    getset.doc=(char*)R"---(
    (double) potential energy stress

    Potential energy part of virial stress
    )---";
    getset.get=[](PyObject* self,void*)->PyObject*
    {
        return var<type0[__dim__][__dim__]>::build(reinterpret_cast<Object*>(self)->atoms->S_pe);
    };
    getset.set=[](PyObject* self,PyObject* val,void*)->int
    {
        PyErr_SetString(PyExc_TypeError,"readonly attribute");
        return -1;
    };
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::getset_pe(PyGetSetDef& getset)
{
    getset.name=(char*)"pe";
    getset.doc=(char*)R"---(
    (double) potential energy

    Potential energy
    )---";
    getset.get=[](PyObject* self,void*)->PyObject*
    {
        return var<type0>::build(reinterpret_cast<Object*>(self)->atoms->pe);
    };
    getset.set=[](PyObject* self,PyObject* val,void*)->int
    {
        PyErr_SetString(PyExc_TypeError,"readonly attribute");
        return -1;
    };
}
/*--------------------------------------------*/
#ifdef POTFIT
PyMethodDef AtomsMD::methods[]=EmptyPyMethodDef(16);
#else
PyMethodDef AtomsMD::methods[]=EmptyPyMethodDef(13);
#endif
/*--------------------------------------------*/
void AtomsMD::setup_tp_methods()
{
    ml_do(methods[0]);
    ml_mul(methods[1]);
    ml_strain(methods[2]);
    ml_create_temp(methods[3]);
    ml_add_elem(methods[4]);
    ForceFieldLJ::ml_new(methods[5]);
    ForceFieldEAM::ml_new(methods[6],methods[7],methods[8]);
    ForceFieldFS::ml_new(methods[9]);
    ForceFieldEAMFunc::ml_new(methods[10]);
    ImportCFGMD::ml_import(methods[11]);
#ifdef POTFIT
    ForceFieldEAMFit::ml_new(methods[12]);
    ForceFieldEAMFitO::ml_new(methods[13]);
    ForceFieldEAMPotFitAckOgata::ml_new(methods[14]);
#endif
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::ml_create_temp(PyMethodDef& tp_method)
{
    tp_method.ml_flags=METH_VARARGS | METH_KEYWORDS;
    tp_method.ml_name="create_temp";
    tp_method.ml_meth=(PyCFunction)(PyCFunctionWithKeywords)(
    [](PyObject* self,PyObject* args,PyObject* kwds)->PyObject*
    {
        AtomsMD::Object* __self=reinterpret_cast<AtomsMD::Object*>(self);
        if(std::isnan(__self->atoms->kB))
        {
            PyErr_SetString(PyExc_TypeError,"boltzmann constant should be set prior to create_temp");
            return NULL;
        }

        FuncAPI<type0,int> f("create_temp",{"temp","seed"});
        f.logics<0>()[0]=VLogics("gt",0.0);
        f.logics<1>()[0]=VLogics("gt",0);
        if(f(args,kwds)) return NULL;

        __self->atoms->create_T(f.val<0>(),f.val<1>());
        Py_RETURN_NONE;
    });
    tp_method.ml_doc=R"---(
    create_temp(temp,seed)

    Create a random velcity field

    Parameters
    ----------
    temp : double
       Temperature
    seed : int
       random seed

    Returns
    -------
    None

    )---";
}
/*--------------------------------------------

 --------------------------------------------*/
void AtomsMD::ml_add_elem(PyMethodDef& tp_method)
{
    tp_method.ml_flags=METH_VARARGS | METH_KEYWORDS;
    tp_method.ml_name="add_elem";
    tp_method.ml_meth=(PyCFunction)(PyCFunctionWithKeywords)(
    [](PyObject* self,PyObject* args,PyObject* kwds)->PyObject*
    {
        FuncAPI<std::string,type0> f("add_elem",{"elem","mass"});
        f.logics<1>()[0]=VLogics("gt",0.0);
        if(f(args,kwds)) return NULL;

        AtomsMD::Object* __self=reinterpret_cast<AtomsMD::Object*>(self);
        __self->atoms->elements.add_type(f.val<1>(),f.val<0>().c_str());

        Py_RETURN_NONE;
    });
    tp_method.ml_doc=R"---(
    add_elem(elem,mass)

    Add a new element to the system

    Parameters
    ----------
    elem : string
       New element
    seed : double
       Mass of the element

    Returns
    -------
    None

    )---";
}

<|endoftext|>


<commit_before>#pragma once

#include <geneial/core/operations/coupling/SimpleCouplingOperation.h>

geneial_private_namespace(geneial)
{
geneial_private_namespace(operation)
{
geneial_private_namespace(coupling)
{
using ::geneial::population::Population;
using ::geneial::operation::crossover::BaseCrossoverOperation;
using ::geneial::operation::selection::BaseSelectionOperation;
using ::geneial::population::management::BaseManager;

geneial_export_namespace
{

template<typename FITNESS_TYPE>
typename BaseCouplingOperation<FITNESS_TYPE>::offspring_result_set SimpleCouplingOperation<FITNESS_TYPE>::doCopulate(
        const typename BaseSelectionOperation<FITNESS_TYPE>::selection_result_set &mating_pool,
        const BaseCrossoverOperation<FITNESS_TYPE> &crossoverOperation, BaseManager<FITNESS_TYPE> &manager)
{

    typedef typename BaseCouplingOperation<FITNESS_TYPE>::offspring_result_set offspring_container;
    typedef typename BaseCrossoverOperation<FITNESS_TYPE>::crossover_result_set children_container;
    typedef typename BaseSelectionOperation<FITNESS_TYPE>::selection_result_set mating_container;
    typedef typename BaseChromosome<FITNESS_TYPE>::ptr chrom_ptr;

    unsigned int offspring_left = this->getSettings().getNumberOfOffspring();

    offspring_container offspring; //A small container for all the little children :-)
    offspring.reserve(offspring_left);

    assert(mating_pool.size() > 1);
    //iterate over mating candidates and create a pairwise mapping.
    /**
     * Example getNumberOfOffspring: 8, #mating_pool = 5
     *
     *  Parent1 \/ Child1
     *  Parent2 /\ Child2
     *
     *  Parent3 \/ Child3
     *  Parent4 /\ Child4
     *
     *  Parent5 \/ Child5
     *  Parent1 /\ Child6
     *
     *  Parent2 \/ Child7
     *  Parent3 /
     */
    for (typename mating_container::const_iterator it = mating_pool.begin(); offspring_left > 0;)
    {
        //TODO (bewo) REFACTOR: research cyclic iterators to avoid all those ugly case distinctions
        chrom_ptr parent1(*it);
        it++;
        //wrap around if necessary
        if (it == mating_pool.end())
        {
            it = mating_pool.begin();
        }

        chrom_ptr parent2(*it);
        it++;
        if (it == mating_pool.end())
        {
            it = mating_pool.begin();
        }

        const children_container children(crossoverOperation.doCrossover(parent1, parent2));
        offspring_left -= this->copyUnlessMaximumReached(offspring,children,offspring_left);

        if (offspring_left > 0 && !crossoverOperation.isSymmetric())
        {
            const children_container assymetricChildren(crossoverOperation.doCrossover(parent2, parent1));
            offspring_left -= this->copyUnlessMaximumReached(offspring,assymetricChildren,offspring_left);
        }
    }

    return offspring;
}

} /* geneial_export_namespace */
} /* private namespace coupling */
} /* private namespace operation */
} /* private namespace geneial */

<commit_msg>Update SimpleCouplingOperation.hpp<commit_after>#pragma once

#include <geneial/core/operations/coupling/SimpleCouplingOperation.h>

geneial_private_namespace(geneial)
{
geneial_private_namespace(operation)
{
geneial_private_namespace(coupling)
{
using ::geneial::population::Population;
using ::geneial::operation::crossover::BaseCrossoverOperation;
using ::geneial::operation::selection::BaseSelectionOperation;
using ::geneial::population::management::BaseManager;

geneial_export_namespace
{

template<typename FITNESS_TYPE>
typename BaseCouplingOperation<FITNESS_TYPE>::offspring_result_set SimpleCouplingOperation<FITNESS_TYPE>::doCopulate(
        const typename BaseSelectionOperation<FITNESS_TYPE>::selection_result_set &mating_pool,
        const BaseCrossoverOperation<FITNESS_TYPE> &crossoverOperation, BaseManager<FITNESS_TYPE> &manager)
{

    typedef typename BaseCouplingOperation<FITNESS_TYPE>::offspring_result_set offspring_container;
    typedef typename BaseCrossoverOperation<FITNESS_TYPE>::crossover_result_set children_container;
    typedef typename BaseSelectionOperation<FITNESS_TYPE>::selection_result_set mating_container;
    typedef typename BaseChromosome<FITNESS_TYPE>::ptr chrom_ptr;

    unsigned int offspring_left = this->getSettings().getNumberOfOffspring();

    offspring_container offspring; //A small container for all the little children :-)
    offspring.reserve(offspring_left);

    assert(mating_pool.size() > 1);
    //iterate over mating candidates and create a pairwise mapping.
    /**
     * Example getNumberOfOffspring: 7, #mating_pool = 5
     *
     *  Parent1 \/ Child1
     *  Parent2 /\ Child2
     *
     *  Parent3 \/ Child3
     *  Parent4 /\ Child4
     *
     *  Parent5 \/ Child5
     *  Parent1 /\ Child6
     *
     *  Parent2 \/ Child7
     *  Parent3 /
     */
    for (typename mating_container::const_iterator it = mating_pool.begin(); offspring_left > 0;)
    {
        //TODO (bewo) REFACTOR: research cyclic iterators to avoid all those ugly case distinctions
        chrom_ptr parent1(*it);
        it++;
        //wrap around if necessary
        if (it == mating_pool.end())
        {
            it = mating_pool.begin();
        }

        chrom_ptr parent2(*it);
        it++;
        if (it == mating_pool.end())
        {
            it = mating_pool.begin();
        }

        const children_container children(crossoverOperation.doCrossover(parent1, parent2));
        offspring_left -= this->copyUnlessMaximumReached(offspring,children,offspring_left);

        if (offspring_left > 0 && !crossoverOperation.isSymmetric())
        {
            const children_container assymetricChildren(crossoverOperation.doCrossover(parent2, parent1));
            offspring_left -= this->copyUnlessMaximumReached(offspring,assymetricChildren,offspring_left);
        }
    }

    return offspring;
}

} /* geneial_export_namespace */
} /* private namespace coupling */
} /* private namespace operation */
} /* private namespace geneial */

<|endoftext|>


<commit_before>// Author: Hong Jiang <hong@hjiang.net>

#include "jsonxx.h"

#include <cctype>
#include <iostream>
#include <sstream>

namespace jsonxx {

// Try to consume characters from the input stream and match the
// pattern string.
bool match(const char* pattern, std::istream& input) {
    input >> std::ws;
    const char* cur(pattern);
    char ch(0);
    while(input && !input.eof() && *cur != 0) {
        input.get(ch);
        if (ch != *cur) {
            input.putback(ch);
            return false;
        } else {
            cur++;
        }
    }
    return *cur == 0;
}

bool parse_string(std::istream& input, std::string* value) {
    if (!match("\"", input))  {
        return false;
    }
    char ch;
    while(!input.eof() && input.good()) {
        input.get(ch);
        if (ch == '"') {
            break;
        }
        if (ch == '\\') {
            input.get(ch);
            switch(ch) {
            case '"':
            case '\\':
            case '/':
                value->push_back(ch);
                break;
            case 'b':
                value->push_back('\b');
                break;
            case 'f':
                value->push_back('\f');
                break;
            case 'n':
                value->push_back('\n');
                break;
            case 'r':
                value->push_back('\r');
                break;
            case 't':
                value->push_back('\t');
                break;
            default:
                value->push_back('\\');
                value->push_back(ch);
                break;
            }
        } else {
            value->push_back(ch);
        }
    }
    if (input && ch == '"') {
        return true;
    } else {
        return false;
    }
}

bool parse_bool(std::istream& input, bool* value) {
    if (match("true", input))  {
        *value = true;
        return true;
    }
    if (match("false", input)) {
        *value = false;
        return true;
    }
    return false;
}

bool parse_null(std::istream& input) {
    if (match("null", input))  {
        return true;
    }
    return false;
}

bool parse_number(std::istream& input, long* value) {
    input >> std::ws;
    char ch;
    std::string value_str;
    int sign = 1;
    if (match("-", input)) {
        sign = -1;
    } else {
        match("+", input);
    }
    while(input && !input.eof()) {
        input.get(ch);
        if (!isdigit(ch)) {
            input.putback(ch);
            break;
        }
        value_str.push_back(ch);
    }
    if (value_str.size() > 0) {
        std::istringstream(value_str) >> *value;
        return true;
    } else {
        return false;
    }
}

Object::Object() : value_map_() {}

Object::~Object() {
    std::map<std::string, Value*>::iterator i;
    for (i = value_map_.begin(); i != value_map_.end(); ++i) {
        delete i->second;
    }
}

bool Object::parse(std::istream& input) {
    if (!match("{", input)) {
        return false;
    }

    do {
        std::string key;
        if (!parse_string(input, &key)) {
            return false;
        }
        if (!match(":", input)) {
            return false;
        }
        Value* v = new Value();
        if (!v->parse(input)) {
            delete v;
            break;
        }
        value_map_[key] = v;
    } while (match(",", input));

    if (!match("}", input)) {
        return false;
    }
    return true;
}

Value::Value() : type_(INVALID_) {}

Value::~Value() {
    if (type_ == STRING_) {
        delete string_value_;
    }
    if (type_ == OBJECT_) {
        delete object_value_;
    }
    if (type_ == ARRAY_) {
        delete array_value_;
    }
}

bool Value::parse(std::istream& input) {
    std::string string_value;
    if (parse_string(input, &string_value)) {
        string_value_ = new std::string();
        string_value_->swap(string_value);
        type_ = STRING_;
        return true;
    }
    if (parse_number(input, &integer_value_)) {
        type_ = INTEGER_;
        return true;
    }

    if (parse_bool(input, &bool_value_)) {
        type_ = BOOL_;
        return true;
    }
    if (parse_null(input)) {
        type_ = NULL_;
        return true;
    }
    if (input.peek() == '[') {
        array_value_ = new Array();
        if (array_value_->parse(input)) {
            type_ = ARRAY_;
            return true;
        }
        delete array_value_;
    }
    object_value_ = new Object();
    if (object_value_->parse(input)) {
        type_ = OBJECT_;
        return true;
    }
    delete object_value_;
    return false;
}

Array::Array() : values_() {}

Array::~Array() {
    for (unsigned int i = 0; i < values_.size(); ++i) {
        delete values_[i];
    }
}

bool Array::parse(std::istream& input) {
    if (!match("[", input)) {
        return false;
    }

    do {
        Value* v = new Value();
        if (!v->parse(input)) {
            delete v;
            break;
        }
        values_.push_back(v);
    } while (match(",", input));

    if (!match("]", input)) {
        return false;
    }
    return true;
}

static std::ostream& stream_string(std::ostream& stream,
        const std::string& string) {
    stream << '"';
    for (std::string::const_iterator i = string.begin(),
            e = string.end(); i != e; ++i) {
        if (*i == '"' || *i == '\\') {
            stream << '\\' << *i;
        } else {
            stream << *i;
        }
    }
    stream << '"';
    return stream;
}

}  // namespace jsonxx

std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v) {
  if (v.is<long>()) {
    return stream << v.get<long>();
  } else if (v.is<std::string>()) {
    return jsonxx::stream_string(stream, v.get<std::string>());
  } else if (v.is<bool>()) {
    if (v.get<bool>()) {
      return stream << "true";
    } else {
      return stream << "false";
    }
  } else if (v.is<jsonxx::Value::Null>()) {
    return stream << "null";
  } else if (v.is<jsonxx::Object>()) {
    return stream << v.get<jsonxx::Object>();
  } else if (v.is<jsonxx::Array>()){
    return stream << v.get<jsonxx::Array>();
  }
  // Shouldn't reach here.
  return stream;
}

std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v) {
  stream << "[";
  const std::vector<jsonxx::Value*>& values(v.values());
  for (unsigned int i = 0; i < values.size()-1; ++i) {
    stream << *(values[i]) << ", ";
  }
  return stream << *(values[values.size()-1]) << "]";
}

std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v) {
  stream << "{";
  const std::map<std::string, jsonxx::Value*>& kv(v.kv_map());
  for (std::map<std::string, jsonxx::Value*>::const_iterator i = kv.begin();
       i != kv.end(); /**/) {
    jsonxx::stream_string(stream, i->first);
    stream << ": " << *(i->second);
    ++i;
    if ( i != kv.end()) {
      stream << ", ";
    }
  }
  return stream << "}";
}
<commit_msg>[performance] improve number parsing speed<commit_after>// Author: Hong Jiang <hong@hjiang.net>

#include "jsonxx.h"

#include <cctype>
#include <iostream>
#include <sstream>

namespace jsonxx {

// Try to consume characters from the input stream and match the
// pattern string.
bool match(const char* pattern, std::istream& input) {
    input >> std::ws;
    const char* cur(pattern);
    char ch(0);
    while(input && !input.eof() && *cur != 0) {
        input.get(ch);
        if (ch != *cur) {
            input.putback(ch);
            return false;
        } else {
            cur++;
        }
    }
    return *cur == 0;
}

bool parse_string(std::istream& input, std::string* value) {
    if (!match("\"", input))  {
        return false;
    }
    char ch;
    while(!input.eof() && input.good()) {
        input.get(ch);
        if (ch == '"') {
            break;
        }
        if (ch == '\\') {
            input.get(ch);
            switch(ch) {
            case '"':
            case '\\':
            case '/':
                value->push_back(ch);
                break;
            case 'b':
                value->push_back('\b');
                break;
            case 'f':
                value->push_back('\f');
                break;
            case 'n':
                value->push_back('\n');
                break;
            case 'r':
                value->push_back('\r');
                break;
            case 't':
                value->push_back('\t');
                break;
            default:
                value->push_back('\\');
                value->push_back(ch);
                break;
            }
        } else {
            value->push_back(ch);
        }
    }
    if (input && ch == '"') {
        return true;
    } else {
        return false;
    }
}

bool parse_bool(std::istream& input, bool* value) {
    if (match("true", input))  {
        *value = true;
        return true;
    }
    if (match("false", input)) {
        *value = false;
        return true;
    }
    return false;
}

bool parse_null(std::istream& input) {
    if (match("null", input))  {
        return true;
    }
    return false;
}

bool parse_number(std::istream& input, long* value) {
    input >> std::ws;
    input >> *value;
    if (input.fail()) {
        input.clear();
        return false;
    }
    return true;
}

Object::Object() : value_map_() {}

Object::~Object() {
    std::map<std::string, Value*>::iterator i;
    for (i = value_map_.begin(); i != value_map_.end(); ++i) {
        delete i->second;
    }
}

bool Object::parse(std::istream& input) {
    if (!match("{", input)) {
        return false;
    }

    do {
        std::string key;
        if (!parse_string(input, &key)) {
            return false;
        }
        if (!match(":", input)) {
            return false;
        }
        Value* v = new Value();
        if (!v->parse(input)) {
            delete v;
            break;
        }
        value_map_[key] = v;
    } while (match(",", input));

    if (!match("}", input)) {
        return false;
    }
    return true;
}

Value::Value() : type_(INVALID_) {}

Value::~Value() {
    if (type_ == STRING_) {
        delete string_value_;
    }
    if (type_ == OBJECT_) {
        delete object_value_;
    }
    if (type_ == ARRAY_) {
        delete array_value_;
    }
}

bool Value::parse(std::istream& input) {
    std::string string_value;
    if (parse_string(input, &string_value)) {
        string_value_ = new std::string();
        string_value_->swap(string_value);
        type_ = STRING_;
        return true;
    }
    if (parse_number(input, &integer_value_)) {
        type_ = INTEGER_;
        return true;
    }

    if (parse_bool(input, &bool_value_)) {
        type_ = BOOL_;
        return true;
    }
    if (parse_null(input)) {
        type_ = NULL_;
        return true;
    }
    if (input.peek() == '[') {
        array_value_ = new Array();
        if (array_value_->parse(input)) {
            type_ = ARRAY_;
            return true;
        }
        delete array_value_;
    }
    object_value_ = new Object();
    if (object_value_->parse(input)) {
        type_ = OBJECT_;
        return true;
    }
    delete object_value_;
    return false;
}

Array::Array() : values_() {}

Array::~Array() {
    for (unsigned int i = 0; i < values_.size(); ++i) {
        delete values_[i];
    }
}

bool Array::parse(std::istream& input) {
    if (!match("[", input)) {
        return false;
    }

    do {
        Value* v = new Value();
        if (!v->parse(input)) {
            delete v;
            break;
        }
        values_.push_back(v);
    } while (match(",", input));

    if (!match("]", input)) {
        return false;
    }
    return true;
}

static std::ostream& stream_string(std::ostream& stream,
        const std::string& string) {
    stream << '"';
    for (std::string::const_iterator i = string.begin(),
            e = string.end(); i != e; ++i) {
        if (*i == '"' || *i == '\\') {
            stream << '\\' << *i;
        } else {
            stream << *i;
        }
    }
    stream << '"';
    return stream;
}

}  // namespace jsonxx

std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v) {
  if (v.is<long>()) {
    return stream << v.get<long>();
  } else if (v.is<std::string>()) {
    return jsonxx::stream_string(stream, v.get<std::string>());
  } else if (v.is<bool>()) {
    if (v.get<bool>()) {
      return stream << "true";
    } else {
      return stream << "false";
    }
  } else if (v.is<jsonxx::Value::Null>()) {
    return stream << "null";
  } else if (v.is<jsonxx::Object>()) {
    return stream << v.get<jsonxx::Object>();
  } else if (v.is<jsonxx::Array>()){
    return stream << v.get<jsonxx::Array>();
  }
  // Shouldn't reach here.
  return stream;
}

std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v) {
  stream << "[";
  const std::vector<jsonxx::Value*>& values(v.values());
  for (unsigned int i = 0; i < values.size()-1; ++i) {
    stream << *(values[i]) << ", ";
  }
  return stream << *(values[values.size()-1]) << "]";
}

std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v) {
  stream << "{";
  const std::map<std::string, jsonxx::Value*>& kv(v.kv_map());
  for (std::map<std::string, jsonxx::Value*>::const_iterator i = kv.begin();
       i != kv.end(); /**/) {
    jsonxx::stream_string(stream, i->first);
    stream << ": " << *(i->second);
    ++i;
    if ( i != kv.end()) {
      stream << ", ";
    }
  }
  return stream << "}";
}
<|endoftext|>


<commit_before>//---------------------------------------------------------------------------//
//!
//! \file   tstSubshellIncoherentPhotonScatteringDistribution.cpp
//! \author Alex Robinson
//! \brier  Subshell incoherent photon scattering distribution unit tests
//!
//---------------------------------------------------------------------------//

// Std Lib Includes
#include <iostream>
#include <limits>

// Trilinos Includes
#include <Teuchos_UnitTestHarness.hpp>
#include <Teuchos_RCP.hpp>
#include <Teuchos_VerboseObject.hpp>
#include <Teuchos_Array.hpp>

// FRENSIE Includes
#include "MonteCarlo_UnitTestHarnessExtensions.hpp"
#include "MonteCarlo_SubshellIncoherentPhotonScatteringDistribution.hpp"
#include "MonteCarlo_SubshellType.hpp"
#include "Data_ElectronPhotonRelaxationDataContainer.hpp"
#include "Utility_TabularDistribution.hpp"
#include "Utility_RandomNumberGenerator.hpp"
#include "Utility_DirectionHelpers.hpp"

//---------------------------------------------------------------------------//
// Testing Variables
//---------------------------------------------------------------------------//

Teuchos::RCP<MonteCarlo::PhotonScatteringDistribution>
  distribution;

//---------------------------------------------------------------------------//
// Tests.
//---------------------------------------------------------------------------//
// Check that the subshell can be returned
TEUCHOS_UNIT_TEST( SubshellIncoherentPhotonScatteringDistribution,
		   getSubshell )
{
  Teuchos::RCP<MonteCarlo::SubshellIncoherentPhotonScatteringDistribution>
    derived_dist = Teuchos::rcp_dynamic_cast<MonteCarlo::SubshellIncoherentPhotonScatteringDistribution>( distribution );

  TEST_EQUALITY_CONST( derived_dist->getSubshell(), MonteCarlo::K_SUBSHELL);
}

//---------------------------------------------------------------------------//
// Check that the binding energy can be returned
TEUCHOS_UNIT_TEST( SubshellIncoherentPhotonScatteringDistribution,
		   getBindingEnergy )
{
  Teuchos::RCP<MonteCarlo::SubshellIncoherentPhotonScatteringDistribution>
    derived_dist = Teuchos::rcp_dynamic_cast<MonteCarlo::SubshellIncoherentPhotonScatteringDistribution>( distribution );

  TEST_EQUALITY_CONST( derived_dist->getBindingEnergy(),
		       8.82899999999999935e-02 );
}

//---------------------------------------------------------------------------//
// Check that a photon can be scattered incoherently without Doppler broadening
TEUCHOS_UNIT_TEST( SubshellIncoherentPhotonScatteringDistribution,
		   scatterPhoton )
{
  MonteCarlo::ParticleBank bank;

  MonteCarlo::PhotonState photon( 0 );
  photon.setEnergy( 20.0 );
  photon.setDirection( 0.0, 0.0, 1.0 );

  MonteCarlo::SubshellType shell_of_interaction;

  // Set up the random number stream
  std::vector<double> fake_stream( 4 );
  fake_stream[0] = 0.001; // sample from first term of koblinger's method
  fake_stream[1] = 0.5; // x = 40.13902672495315, mu = 0.0
  fake_stream[2] = 1.0-1e-15; // accept x in occupation number rejection loop
  fake_stream[3] = 0.5; // azimuthal_angle = pi

  Utility::RandomNumberGenerator::setFakeStream( fake_stream );

  distribution->scatterPhoton( photon,
			       bank,
			       shell_of_interaction);

  Utility::RandomNumberGenerator::unsetFakeStream();

  TEST_EQUALITY_CONST( bank.size(), 1 );
  TEST_EQUALITY_CONST( bank.top()->getParticleType(), MonteCarlo::ELECTRON );
  TEST_FLOATING_EQUALITY( bank.top()->getEnergy(),
			  19.50173181484825,
			  1e-15 );
  TEST_FLOATING_EQUALITY( bank.top()->getZDirection(),
			  0.9996898054103247,
			  1e-15 );
  TEST_FLOATING_EQUALITY( bank.top()->getYDirection(),
			  -0.024905681252821114,
			  1e-12 );
  UTILITY_TEST_FLOATING_EQUALITY( bank.top()->getXDirection(), 0.0, 1e-15 );
  TEST_FLOATING_EQUALITY( photon.getEnergy(), 0.4982681851517501, 1e-15 );
  UTILITY_TEST_FLOATING_EQUALITY( photon.getZDirection(), 0.0, 1e-15 );
  TEST_FLOATING_EQUALITY( photon.getYDirection(), 1.0, 1e-15 );
  UTILITY_TEST_FLOATING_EQUALITY( photon.getXDirection(), 0.0, 1e-15 );
  TEST_EQUALITY_CONST( shell_of_interaction, MonteCarlo::K_SUBSHELL );
}

//---------------------------------------------------------------------------//
// Custom main function
//---------------------------------------------------------------------------//
int main( int argc, char** argv )
{
  std::string test_native_file_name;

  Teuchos::CommandLineProcessor& clp = Teuchos::UnitTestRepository::getCLP();

  clp.setOption( "test_native_file",
		 &test_native_file_name,
		 "Test Native file name" );

  const Teuchos::RCP<Teuchos::FancyOStream> out =
    Teuchos::VerboseObjectBase::getDefaultOStream();

  Teuchos::CommandLineProcessor::EParseCommandLineReturn parse_return =
    clp.parse(argc,argv);

  if ( parse_return != Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL )
  {
    *out << "\nEnd Result: TEST FAILED" << std::endl;
    return parse_return;
  }

  {
    // Create the native data file container
    Data::ElectronPhotonRelaxationDataContainer
      data_container( test_native_file_name );

    // Extract the Compton profile and occupation number for the first subshell
    const std::vector<double>& compton_profile_grid_s1 =
      data_container.getComptonProfileMomentumGrid( 1 );

    const std::vector<double>& compton_profile_s1 =
      data_container.getComptonProfile( 1 );

    const std::vector<double>& occupation_number_grid_s1 =
      data_container.getOccupationNumberMomentumGrid( 1 );

    const std::vector<double>& occupation_number_s1 =
      data_container.getOccupationNumber( 1 );

    // Create the Compton profile and occupation number distributions
    Teuchos::RCP<const Utility::TabularOneDDistribution> compton_profile_s1_dist(
			    new Utility::TabularDistribution<Utility::LinLin>(
						       compton_profile_grid_s1,
						       compton_profile_s1 ) );

    Teuchos::RCP<const Utility::OneDDistribution> occupation_number_s1_dist(
			    new Utility::TabularDistribution<Utility::LinLin>(
						    occupation_number_grid_s1,
						    occupation_number_s1 ) );

    // Create the subshell incoherent distributions
    distribution.reset(
		new MonteCarlo::SubshellIncoherentPhotonScatteringDistribution(
			  MonteCarlo::convertENDFDesignatorToSubshellEnum( 1 ),
			  data_container.getSubshellOccupancy( 1 ),
			  data_container.getSubshellBindingEnergy( 1 ),
			  occupation_number_s1_dist,
			  3.0 ) );
  }

  // Initialize the random number generator
  Utility::RandomNumberGenerator::createStreams();

  // Run the unit tests
  Teuchos::GlobalMPISession mpiSession( &argc, &argv );

  const bool success = Teuchos::UnitTestRepository::runUnitTests( *out );

  if (success)
    *out << "\nEnd Result: TEST PASSED" << std::endl;
  else
    *out << "\nEnd Result: TEST FAILED" << std::endl;

  clp.printFinalTimerSummary(out.ptr());

  return (success ? 0 : 1);
}

//---------------------------------------------------------------------------//
// end tstSubshellIncoherentPhotonScatteringDistribution.cpp
//---------------------------------------------------------------------------//
<commit_msg>Added tests to the subshell incoherent photon scattering distribution.<commit_after>//---------------------------------------------------------------------------//
//!
//! \file   tstSubshellIncoherentPhotonScatteringDistribution.cpp
//! \author Alex Robinson
//! \brier  Subshell incoherent photon scattering distribution unit tests
//!
//---------------------------------------------------------------------------//

// Std Lib Includes
#include <iostream>
#include <limits>

// Trilinos Includes
#include <Teuchos_UnitTestHarness.hpp>
#include <Teuchos_RCP.hpp>
#include <Teuchos_VerboseObject.hpp>
#include <Teuchos_Array.hpp>

// FRENSIE Includes
#include "MonteCarlo_UnitTestHarnessExtensions.hpp"
#include "MonteCarlo_SubshellIncoherentPhotonScatteringDistribution.hpp"
#include "MonteCarlo_SubshellType.hpp"
#include "Data_ElectronPhotonRelaxationDataContainer.hpp"
#include "Utility_TabularDistribution.hpp"
#include "Utility_RandomNumberGenerator.hpp"
#include "Utility_DirectionHelpers.hpp"

//---------------------------------------------------------------------------//
// Testing Variables
//---------------------------------------------------------------------------//

Teuchos::RCP<MonteCarlo::PhotonScatteringDistribution>
  distribution;

//---------------------------------------------------------------------------//
// Tests.
//---------------------------------------------------------------------------//
// Check that the distribution can be evaluated
TEUCHOS_UNIT_TEST( SubshellIncoherentPhotonScatteringDistribution,
		   evaluate )
{
  double dist_value = distribution->evaluate(
			 Utility::PhysicalConstants::electron_rest_mass_energy,
			 1.0 );

  TEST_FLOATING_EQUALITY( dist_value, 0.0, 1e-15 );

  dist_value = distribution->evaluate(
			 Utility::PhysicalConstants::electron_rest_mass_energy,
			 -1.0 );

  TEST_FLOATING_EQUALITY( dist_value, 0.18204031443868224, 1e-6 );

  dist_value = distribution->evaluate( 1.0, 1.0 );

  TEST_FLOATING_EQUALITY( dist_value, 0.0, 1e-15 );

  dist_value = distribution->evaluate( 1.0, 0.0 );

  TEST_FLOATING_EQUALITY( dist_value, 0.1309675807668618, 1e-15 );

  dist_value = distribution->evaluate( 1.0, -1.0 );

  TEST_FLOATING_EQUALITY( dist_value, 0.10574024270641422, 1e-15 );
}

//---------------------------------------------------------------------------//
// Check that the distribution pdf can be evaluated
TEUCHOS_UNIT_TEST( SubshellIncoherentPhotonScatteringDistribution,
		   evaluatePDF )
{
  double pdf_value = distribution->evaluatePDF(
			 Utility::PhysicalConstants::electron_rest_mass_energy,
			 1.0 );

  TEST_FLOATING_EQUALITY( pdf_value, 0.0, 1e-15 );

  pdf_value = distribution->evaluatePDF(
			 Utility::PhysicalConstants::electron_rest_mass_energy,
			 -1.0 );

  TEST_FLOATING_EQUALITY( pdf_value, 0.23410347913716015, 1e-6 );

  pdf_value = distribution->evaluatePDF( 1.0, 1.0 );

  TEST_FLOATING_EQUALITY( pdf_value, 0.0, 1e-15 );

  pdf_value = distribution->evaluatePDF( 1.0, 0.0 );

  TEST_FLOATING_EQUALITY( pdf_value, 0.18052763492462426, 1e-15 );

  pdf_value = distribution->evaluatePDF( 1.0, -1.0 );

  TEST_FLOATING_EQUALITY( pdf_value, 0.14575390199904137, 1e-15 );
}

//---------------------------------------------------------------------------//
// Check that the integrated cross section can be evaluated
TEUCHOS_UNIT_TEST( SubshellIncoherentPhotonScatteringDistribution,
		   evaluateIntegratedCrossSection )
{
  double cross_section = distribution->evaluateIntegratedCrossSection(
			 Utility::PhysicalConstants::electron_rest_mass_energy,
			 1e-3 );

  TEST_FLOATING_EQUALITY( cross_section, 0.777606189833794259, 1e-15 );

  cross_section = distribution->evaluateIntegratedCrossSection( 1.0, 1e-3 );

  TEST_FLOATING_EQUALITY( cross_section, 0.725471093783202292, 1e-15 );

  cross_section = distribution->evaluateIntegratedCrossSection( 20.0, 1e-3 );

  TEST_FLOATING_EQUALITY( cross_section, 0.120620123031366933, 1e-15 );
}

//---------------------------------------------------------------------------//
// Check that the subshell can be returned
TEUCHOS_UNIT_TEST( SubshellIncoherentPhotonScatteringDistribution,
		   getSubshell )
{
  Teuchos::RCP<MonteCarlo::SubshellIncoherentPhotonScatteringDistribution>
    derived_dist = Teuchos::rcp_dynamic_cast<MonteCarlo::SubshellIncoherentPhotonScatteringDistribution>( distribution );

  TEST_EQUALITY_CONST( derived_dist->getSubshell(), MonteCarlo::K_SUBSHELL);
}

//---------------------------------------------------------------------------//
// Check that the binding energy can be returned
TEUCHOS_UNIT_TEST( SubshellIncoherentPhotonScatteringDistribution,
		   getBindingEnergy )
{
  Teuchos::RCP<MonteCarlo::SubshellIncoherentPhotonScatteringDistribution>
    derived_dist = Teuchos::rcp_dynamic_cast<MonteCarlo::SubshellIncoherentPhotonScatteringDistribution>( distribution );

  TEST_EQUALITY_CONST( derived_dist->getBindingEnergy(),
		       8.82899999999999935e-02 );
}

//---------------------------------------------------------------------------//
// Check that a photon can be scattered incoherently without Doppler broadening
TEUCHOS_UNIT_TEST( SubshellIncoherentPhotonScatteringDistribution,
		   scatterPhoton )
{
  MonteCarlo::ParticleBank bank;

  MonteCarlo::PhotonState photon( 0 );
  photon.setEnergy( 20.0 );
  photon.setDirection( 0.0, 0.0, 1.0 );

  MonteCarlo::SubshellType shell_of_interaction;

  // Set up the random number stream
  std::vector<double> fake_stream( 4 );
  fake_stream[0] = 0.001; // sample from first term of koblinger's method
  fake_stream[1] = 0.5; // x = 40.13902672495315, mu = 0.0
  fake_stream[2] = 1.0-1e-15; // accept x in occupation number rejection loop
  fake_stream[3] = 0.5; // azimuthal_angle = pi

  Utility::RandomNumberGenerator::setFakeStream( fake_stream );

  distribution->scatterPhoton( photon,
			       bank,
			       shell_of_interaction);

  Utility::RandomNumberGenerator::unsetFakeStream();

  TEST_EQUALITY_CONST( bank.size(), 1 );
  TEST_EQUALITY_CONST( bank.top()->getParticleType(), MonteCarlo::ELECTRON );
  TEST_FLOATING_EQUALITY( bank.top()->getEnergy(),
			  19.50173181484825,
			  1e-15 );
  TEST_FLOATING_EQUALITY( bank.top()->getZDirection(),
			  0.9996898054103247,
			  1e-15 );
  TEST_FLOATING_EQUALITY( bank.top()->getYDirection(),
			  -0.024905681252821114,
			  1e-12 );
  UTILITY_TEST_FLOATING_EQUALITY( bank.top()->getXDirection(), 0.0, 1e-15 );
  TEST_FLOATING_EQUALITY( photon.getEnergy(), 0.4982681851517501, 1e-15 );
  UTILITY_TEST_FLOATING_EQUALITY( photon.getZDirection(), 0.0, 1e-15 );
  TEST_FLOATING_EQUALITY( photon.getYDirection(), 1.0, 1e-15 );
  UTILITY_TEST_FLOATING_EQUALITY( photon.getXDirection(), 0.0, 1e-15 );
  TEST_EQUALITY_CONST( shell_of_interaction, MonteCarlo::K_SUBSHELL );
}

//---------------------------------------------------------------------------//
// Custom main function
//---------------------------------------------------------------------------//
int main( int argc, char** argv )
{
  std::string test_native_file_name;

  Teuchos::CommandLineProcessor& clp = Teuchos::UnitTestRepository::getCLP();

  clp.setOption( "test_native_file",
		 &test_native_file_name,
		 "Test Native file name" );

  const Teuchos::RCP<Teuchos::FancyOStream> out =
    Teuchos::VerboseObjectBase::getDefaultOStream();

  Teuchos::CommandLineProcessor::EParseCommandLineReturn parse_return =
    clp.parse(argc,argv);

  if ( parse_return != Teuchos::CommandLineProcessor::PARSE_SUCCESSFUL )
  {
    *out << "\nEnd Result: TEST FAILED" << std::endl;
    return parse_return;
  }

  {
    // Create the native data file container
    Data::ElectronPhotonRelaxationDataContainer
      data_container( test_native_file_name );

    // Extract the occupation number for the first subshell
    const std::vector<double>& occupation_number_grid_s1 =
      data_container.getOccupationNumberMomentumGrid( 1 );

    const std::vector<double>& occupation_number_s1 =
      data_container.getOccupationNumber( 1 );

    for( unsigned i = 0; i < occupation_number_grid_s1.size(); ++i )
    {
      std::cout << occupation_number_grid_s1[i] << " "
		<< occupation_number_s1[i] << std::endl;
    }

    // Create the occupation number distributions
    Teuchos::RCP<const Utility::OneDDistribution> occupation_number_s1_dist(
			    new Utility::TabularDistribution<Utility::LinLin>(
						    occupation_number_grid_s1,
						    occupation_number_s1 ) );

    // Create the subshell incoherent distributions
    distribution.reset(
		new MonteCarlo::SubshellIncoherentPhotonScatteringDistribution(
			  MonteCarlo::convertENDFDesignatorToSubshellEnum( 1 ),
			  data_container.getSubshellOccupancy( 1 ),
			  data_container.getSubshellBindingEnergy( 1 ),
			  occupation_number_s1_dist,
			  3.0 ) );
  }

  // Initialize the random number generator
  Utility::RandomNumberGenerator::createStreams();

  // Run the unit tests
  Teuchos::GlobalMPISession mpiSession( &argc, &argv );

  const bool success = Teuchos::UnitTestRepository::runUnitTests( *out );

  if (success)
    *out << "\nEnd Result: TEST PASSED" << std::endl;
  else
    *out << "\nEnd Result: TEST FAILED" << std::endl;

  clp.printFinalTimerSummary(out.ptr());

  return (success ? 0 : 1);
}

//---------------------------------------------------------------------------//
// end tstSubshellIncoherentPhotonScatteringDistribution.cpp
//---------------------------------------------------------------------------//
<|endoftext|>