<commit_before>#include "pch.h"
#include "main.h"
#include "services/service_manager.h"
#include "services/render_service.h"
#include "services/input_service.h"
#include "services/tracker_service.h"

#ifdef WIN32
#include <direct.h>
#include <Windows.h>
#else
#include <sys/stat.h>
#endif

#ifdef __APPLE__
#import <Foundation/Foundation.h>
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#endif
#endif

#ifdef __ANDROID__
#include <android_native_app_glue.h>
#include <android/window.h>
extern struct android_app* __state;
#endif

DfgGame::DfgGame()
    : _hyperKeyPressed(false)
{
#ifdef __EMSCRIPTEN__
    _hasIndexedDB = EM_ASM_INT_V({
        return typeof window === 'object' && (window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB) ? 1 : 0;
    });
#endif
}

void DfgGame::initialize()
{
    // create services

    _inputService = ServiceManager::getInstance()->registerService< InputService >(NULL);

    Service * render_dep[] = { _inputService, NULL };
    _renderService = ServiceManager::getInstance()->registerService< RenderService >(render_dep);

    setVsync(false);

    _userFolder = getAppPrivateFolderPath();
#ifdef WIN32
    _mkdir(_userFolder.c_str());
#else
    mkdir( _userFolder.c_str( ), 0777 );
#endif
    _userFolder += "/Dream Farm Games";
#ifdef WIN32
    _mkdir(_userFolder.c_str());
#else
    mkdir( _userFolder.c_str( ), 0777 );
#endif
    _userFolder += "/";
    _userFolder += getConfig()->getNamespace("window", true)->getString("title");
#ifdef WIN32
    _mkdir(_userFolder.c_str());
#else
    mkdir( _userFolder.c_str( ), 0777 );
#endif

    // set default resources locale
    _gameLocale = getConfig()->getString("language");
    setGameLocale();

#ifdef __APPLE__
#if TARGET_OS_IPHONE
    [NSNotificationCenter.defaultCenter addObserverForName:UIKeyboardDidShowNotification object:nil queue:nil usingBlock:^(NSNotification *note)
     {
         CGSize keyboardSize = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
         ServiceManager::getInstance()->signals.virtualKeyboardSizeChanged(keyboardSize.width * [[UIScreen mainScreen] scale], keyboardSize.height * [[UIScreen mainScreen] scale]);
     }];
    [NSNotificationCenter.defaultCenter addObserverForName:UIKeyboardDidHideNotification object:nil queue:nil usingBlock:^(NSNotification *note)
     {
         ServiceManager::getInstance()->signals.virtualKeyboardSizeChanged(0.0f, 0.0f);
     }];
#endif
#endif
}

void DfgGame::setGameLocale(const char * newLocale)
{
    std::string systemLocale(_gameLocale);
    if (newLocale == NULL)
    {
        // get system-wide locale and override _gameLocale if we have resources for it
#ifdef __APPLE__
        NSString* preferredLang = [[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:@"GameLanguage"];
        systemLocale = [preferredLang cStringUsingEncoding : NSASCIIStringEncoding];
#endif

#ifdef __ANDROID__
        android_app* app = __state;
        JNIEnv* env = app->activity->env;
        JavaVM* vm = app->activity->vm;
        vm->AttachCurrentThread(&env, NULL);

        jclass android_content_Context = env->FindClass("android/content/Context");
        jmethodID midGetPackageName = env->GetMethodID(android_content_Context, "getPackageName", "()Ljava/lang/String;");
        jstring packageName = (jstring)env->CallObjectMethod(app->activity->clazz, midGetPackageName);
        jmethodID midGetResources = env->GetMethodID(android_content_Context, "getResources", "()Landroid/content/res/Resources;");
        jobject jResource = env->CallObjectMethod(app->activity->clazz, midGetResources);

        jclass resource_Class = env->GetObjectClass(jResource);
        jmethodID midGetIdentifier = env->GetMethodID(resource_Class, "getIdentifier", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I");
        jstring languageVar = env->NewStringUTF("GameLanguage");
        jstring TypeName = env->NewStringUTF("string");
        jint id = env->CallIntMethod(jResource, midGetIdentifier, languageVar, TypeName, packageName);

        jmethodID midGetAppName = env->GetMethodID(resource_Class, "getString", "(I)Ljava/lang/String;");
        jstring language = (jstring)env->CallObjectMethod(jResource, midGetAppName, id);

        const char * lng = env->GetStringUTFChars(language, NULL);
        if (lng)
            systemLocale = lng;
        env->ReleaseStringUTFChars(language, lng);

        vm->DetachCurrentThread();
#endif

        newLocale = systemLocale.c_str();
    }

    std::string aliasesName("aliases_");
    if (getConfig()->getNamespace((aliasesName + newLocale).c_str(), true))
        _gameLocale = newLocale;

    gameplay::FileSystem::loadResourceAliases(getConfig()->getNamespace((aliasesName + _gameLocale).c_str(), true));
}

void DfgGame::finalize()
{
    ServiceManager::getInstance()->shutdown();
    Caches::getInstance()->destroyAll();
}

void DfgGame::update(float elapsedTime)
{
    ServiceManager::getInstance()->update(elapsedTime);
}

void DfgGame::render(float /*elapsedTime*/)
{
    if (_renderService)
    {
        ServiceManager::getInstance()->signals.framePreRender();
        _renderService->renderFrame();
        ServiceManager::getInstance()->signals.framePostRender();
    }
}

void DfgGame::keyEvent(gameplay::Keyboard::KeyEvent evt, int key, bool processed)
{
    if (_inputService)
        if (processed)
            _inputService->injectKeyGlobalEvent(evt, key);
        else
            _inputService->injectKeyEvent(evt, key);

    if (key == gameplay::Keyboard::KEY_HYPER)
        _hyperKeyPressed = evt == gameplay::Keyboard::KEY_PRESS;
    else if (_hyperKeyPressed && evt == gameplay::Keyboard::KEY_PRESS &&
        (key == gameplay::Keyboard::KEY_CAPITAL_Q || key == gameplay::Keyboard::KEY_Q))
    {
        exit();
    }
}

void DfgGame::touchEvent(gameplay::Touch::TouchEvent evt, int x, int y, unsigned int contactIndex, bool processed)
{
    if (_inputService)
        if (processed)
            _inputService->injectTouchGlobalEvent(evt, x, y, contactIndex);
        else
            _inputService->injectTouchEvent(evt, x, y, contactIndex);
}

bool DfgGame::mouseEvent(gameplay::Mouse::MouseEvent evt, int x, int y, float wheelDelta, bool processed)
{
    if (_inputService)
        if (processed)
            return _inputService->injectMouseGlobalEvent(evt, x, y, wheelDelta);
        else
            return _inputService->injectMouseEvent(evt, x, y, wheelDelta);
    return false;
}

void DfgGame::gesturePinchEvent(int x, int y, float scale, int numberOfTouches)
{
    if (_inputService)
        _inputService->injectGesturePinchEvent(x, y, scale, numberOfTouches);
}

void DfgGame::gestureSwipeEvent(int x, int y, int direction)
{
    if (_inputService)
        _inputService->injectGestureSwipeEvent(x, y, direction);
}

void DfgGame::pause()
{
    ServiceManager::getInstance()->signals.pauseEvent();

#ifndef _DEBUG
    TrackerService * trackerService = ServiceManager::getInstance()->findService< TrackerService >();
    if (trackerService)
        trackerService->endSession("Pause");

    gameplay::Game::pause();
#endif
}

void DfgGame::resume()
{
#ifndef _DEBUG
    gameplay::Game::resume();
#endif

    ServiceManager::getInstance()->signals.resumeEvent();
}

void DfgGame::reportError(bool isFatal, const char * errorMessage, ...)
{
    va_list args;
    va_start(args, errorMessage);

    char exceptionDesc[1024];
    vsnprintf(exceptionDesc, 1023, errorMessage, args);
    exceptionDesc[1023] = '\0';

    if (isFatal)
    {
        // notify user
#ifdef WIN32
        MessageBoxA(NULL, exceptionDesc, "Critical Error", MB_OK | MB_ICONERROR);
#endif
    }

    TrackerService * trackerService = ServiceManager::getInstance()->findService< TrackerService >();
    if (!trackerService)
        return;

    exceptionDesc[149] = '\0';
    trackerService->sendException(exceptionDesc, isFatal);

    if (isFatal)
        trackerService->endSession();
}

void DfgGame::scheduleLocalNotification(time_t datetime, const char * utf8Body, const char * utf8ActionButton, int badgeNumber, const std::unordered_map< std::string, std::string >& userDictionary)
{
#ifdef __APPLE__
#if TARGET_OS_IPHONE

    if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)])
    {
        [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
    }

    UILocalNotification *localNotif = [[UILocalNotification alloc] init];
    if (localNotif == nil)
        return;

    localNotif.fireDate = [NSDate dateWithTimeIntervalSince1970:datetime];
    localNotif.timeZone = [NSTimeZone defaultTimeZone];

    localNotif.alertBody = [NSString stringWithUTF8String:utf8Body];
    localNotif.alertAction = [NSString stringWithUTF8String:utf8ActionButton];

    localNotif.soundName = UILocalNotificationDefaultSoundName;
    localNotif.applicationIconBadgeNumber = badgeNumber;

    NSMutableDictionary *infoDict = [NSMutableDictionary dictionaryWithCapacity:userDictionary.size( )];

    for( std::unordered_map< std::string, std::string >::const_iterator it = userDictionary.begin( ), end_it = userDictionary.end( ); it != end_it; it++ )
        [infoDict setObject:[NSString stringWithUTF8String:( *it ).second.c_str( )] forKey:[NSString stringWithUTF8String:( *it ).first.c_str( )]];

    localNotif.userInfo = infoDict;

    [[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
#endif
#endif
}

void DfgGame::cancelAllLocalNotifications()
{
#ifdef __APPLE__
#if TARGET_OS_IPHONE
    [[UIApplication sharedApplication] cancelAllLocalNotifications];
    [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
#endif
#endif
}

void DfgGame::preventFromSleeping(bool prevent)
{
#if defined(__APPLE__)
#if TARGET_OS_IPHONE
    [UIApplication sharedApplication].idleTimerDisabled = prevent ? YES : NO;
#endif
#elif defined(__ANDROID__)
    ANativeActivity* activity = __state->activity;
    if (prevent)
        ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0);
    else
        ANativeActivity_setWindowFlags(activity, 0, AWINDOW_FLAG_KEEP_SCREEN_ON);
#endif
}

void DfgGame::resizeEvent(unsigned int width, unsigned int height)
{
    ServiceManager::getInstance()->signals.resizeEvent(width, height);
}

void DfgGame::copyToClipboard(const char * textUTF8) const
{
#if defined(WIN32)

    const wchar_t * wstring = Utils::UTF8ToWCS(textUTF8);
    const size_t len = wcslen(wstring) + 1;
    HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len * sizeof(wchar_t));
    memcpy(GlobalLock(hMem), wstring, len * sizeof(wchar_t));
    GlobalUnlock(hMem);
    OpenClipboard(0);
    EmptyClipboard();
    SetClipboardData(CF_UNICODETEXT, hMem);
    CloseClipboard();

#elif defined(__APPLE__)

    UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
    pasteboard.string = [NSString stringWithUTF8String:textUTF8];

#elif defined(__ANDROID__)

    android_app* app = __state;
    JNIEnv* env = app->activity->env;
    JavaVM* vm = app->activity->vm;
    vm->AttachCurrentThread(&env, NULL);

    jclass looperClass = env->FindClass("android/os/Looper");
    jmethodID prepareMethodID = env->GetStaticMethodID(looperClass, "prepare", "()V");
    env->CallStaticVoidMethod(looperClass, prepareMethodID);

    jclass clsContext = env->FindClass("android/content/Context");
    jmethodID getSystemService = env->GetMethodID(clsContext, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
    jfieldID CLIPBOARD_SERVICE_ID = env->GetStaticFieldID(clsContext, "CLIPBOARD_SERVICE", "Ljava/lang/String;");
    jstring CLIPBOARD_SERVICE = (jstring)env->GetStaticObjectField(clsContext, CLIPBOARD_SERVICE_ID);
    jobject clipboard = env->CallObjectMethod(app->activity->clazz, getSystemService, CLIPBOARD_SERVICE);

    jclass clsClipData = env->FindClass("android/content/ClipData");
    jmethodID methodNewPlainText = env->GetStaticMethodID(clsClipData, "newPlainText", "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;");
    jstring text = env->NewStringUTF(textUTF8);
    jobject clipData = env->CallStaticObjectMethod(clsClipData, methodNewPlainText, text, text);

    jclass clsClipboardManager = env->FindClass("android/content/ClipboardManager");
    jmethodID methodSetPrimaryClip = env->GetMethodID(clsClipboardManager, "setPrimaryClip", "(Landroid/content/ClipData;)V");
    env->CallVoidMethod(clipboard, methodSetPrimaryClip, clipData);

    vm->DetachCurrentThread();

#endif
}<commit_msg>proper cleanup of curl library<commit_after>#include "pch.h"
#include "main.h"
#include "services/service_manager.h"
#include "services/render_service.h"
#include "services/input_service.h"
#include "services/tracker_service.h"
#include <curl/curl.h>

#ifdef WIN32
#include <direct.h>
#include <Windows.h>
#else
#include <sys/stat.h>
#endif

#ifdef __APPLE__
#import <Foundation/Foundation.h>
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#endif
#endif

#ifdef __ANDROID__
#include <android_native_app_glue.h>
#include <android/window.h>
extern struct android_app* __state;
#endif

DfgGame::DfgGame()
    : _hyperKeyPressed(false)
{
#ifdef __EMSCRIPTEN__
    _hasIndexedDB = EM_ASM_INT_V({
        return typeof window === 'object' && (window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB) ? 1 : 0;
    });
#endif
}

void DfgGame::initialize()
{
    // initialize curl before any servers get created
    curl_global_init(CURL_GLOBAL_ALL);

    // create services
    _inputService = ServiceManager::getInstance()->registerService< InputService >(NULL);

    Service * render_dep[] = { _inputService, NULL };
    _renderService = ServiceManager::getInstance()->registerService< RenderService >(render_dep);

    setVsync(false);

    _userFolder = getAppPrivateFolderPath();
#ifdef WIN32
    _mkdir(_userFolder.c_str());
#else
    mkdir( _userFolder.c_str( ), 0777 );
#endif
    _userFolder += "/Dream Farm Games";
#ifdef WIN32
    _mkdir(_userFolder.c_str());
#else
    mkdir( _userFolder.c_str( ), 0777 );
#endif
    _userFolder += "/";
    _userFolder += getConfig()->getNamespace("window", true)->getString("title");
#ifdef WIN32
    _mkdir(_userFolder.c_str());
#else
    mkdir( _userFolder.c_str( ), 0777 );
#endif

    // set default resources locale
    _gameLocale = getConfig()->getString("language");
    setGameLocale();

#ifdef __APPLE__
#if TARGET_OS_IPHONE
    [NSNotificationCenter.defaultCenter addObserverForName:UIKeyboardDidShowNotification object:nil queue:nil usingBlock:^(NSNotification *note)
     {
         CGSize keyboardSize = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
         ServiceManager::getInstance()->signals.virtualKeyboardSizeChanged(keyboardSize.width * [[UIScreen mainScreen] scale], keyboardSize.height * [[UIScreen mainScreen] scale]);
     }];
    [NSNotificationCenter.defaultCenter addObserverForName:UIKeyboardDidHideNotification object:nil queue:nil usingBlock:^(NSNotification *note)
     {
         ServiceManager::getInstance()->signals.virtualKeyboardSizeChanged(0.0f, 0.0f);
     }];
#endif
#endif
}

void DfgGame::setGameLocale(const char * newLocale)
{
    std::string systemLocale(_gameLocale);
    if (newLocale == NULL)
    {
        // get system-wide locale and override _gameLocale if we have resources for it
#ifdef __APPLE__
        NSString* preferredLang = [[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:@"GameLanguage"];
        systemLocale = [preferredLang cStringUsingEncoding : NSASCIIStringEncoding];
#endif

#ifdef __ANDROID__
        android_app* app = __state;
        JNIEnv* env = app->activity->env;
        JavaVM* vm = app->activity->vm;
        vm->AttachCurrentThread(&env, NULL);

        jclass android_content_Context = env->FindClass("android/content/Context");
        jmethodID midGetPackageName = env->GetMethodID(android_content_Context, "getPackageName", "()Ljava/lang/String;");
        jstring packageName = (jstring)env->CallObjectMethod(app->activity->clazz, midGetPackageName);
        jmethodID midGetResources = env->GetMethodID(android_content_Context, "getResources", "()Landroid/content/res/Resources;");
        jobject jResource = env->CallObjectMethod(app->activity->clazz, midGetResources);

        jclass resource_Class = env->GetObjectClass(jResource);
        jmethodID midGetIdentifier = env->GetMethodID(resource_Class, "getIdentifier", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I");
        jstring languageVar = env->NewStringUTF("GameLanguage");
        jstring TypeName = env->NewStringUTF("string");
        jint id = env->CallIntMethod(jResource, midGetIdentifier, languageVar, TypeName, packageName);

        jmethodID midGetAppName = env->GetMethodID(resource_Class, "getString", "(I)Ljava/lang/String;");
        jstring language = (jstring)env->CallObjectMethod(jResource, midGetAppName, id);

        const char * lng = env->GetStringUTFChars(language, NULL);
        if (lng)
            systemLocale = lng;
        env->ReleaseStringUTFChars(language, lng);

        vm->DetachCurrentThread();
#endif

        newLocale = systemLocale.c_str();
    }

    std::string aliasesName("aliases_");
    if (getConfig()->getNamespace((aliasesName + newLocale).c_str(), true))
        _gameLocale = newLocale;

    gameplay::FileSystem::loadResourceAliases(getConfig()->getNamespace((aliasesName + _gameLocale).c_str(), true));
}

void DfgGame::finalize()
{
    ServiceManager::getInstance()->shutdown();
    Caches::getInstance()->destroyAll();

    // free any curl resources after it's no longer used.
    curl_global_cleanup();
}

void DfgGame::update(float elapsedTime)
{
    ServiceManager::getInstance()->update(elapsedTime);
}

void DfgGame::render(float /*elapsedTime*/)
{
    if (_renderService)
    {
        ServiceManager::getInstance()->signals.framePreRender();
        _renderService->renderFrame();
        ServiceManager::getInstance()->signals.framePostRender();
    }
}

void DfgGame::keyEvent(gameplay::Keyboard::KeyEvent evt, int key, bool processed)
{
    if (_inputService)
        if (processed)
            _inputService->injectKeyGlobalEvent(evt, key);
        else
            _inputService->injectKeyEvent(evt, key);

    if (key == gameplay::Keyboard::KEY_HYPER)
        _hyperKeyPressed = evt == gameplay::Keyboard::KEY_PRESS;
    else if (_hyperKeyPressed && evt == gameplay::Keyboard::KEY_PRESS &&
        (key == gameplay::Keyboard::KEY_CAPITAL_Q || key == gameplay::Keyboard::KEY_Q))
    {
        exit();
    }
}

void DfgGame::touchEvent(gameplay::Touch::TouchEvent evt, int x, int y, unsigned int contactIndex, bool processed)
{
    if (_inputService)
        if (processed)
            _inputService->injectTouchGlobalEvent(evt, x, y, contactIndex);
        else
            _inputService->injectTouchEvent(evt, x, y, contactIndex);
}

bool DfgGame::mouseEvent(gameplay::Mouse::MouseEvent evt, int x, int y, float wheelDelta, bool processed)
{
    if (_inputService)
        if (processed)
            return _inputService->injectMouseGlobalEvent(evt, x, y, wheelDelta);
        else
            return _inputService->injectMouseEvent(evt, x, y, wheelDelta);
    return false;
}

void DfgGame::gesturePinchEvent(int x, int y, float scale, int numberOfTouches)
{
    if (_inputService)
        _inputService->injectGesturePinchEvent(x, y, scale, numberOfTouches);
}

void DfgGame::gestureSwipeEvent(int x, int y, int direction)
{
    if (_inputService)
        _inputService->injectGestureSwipeEvent(x, y, direction);
}

void DfgGame::pause()
{
    ServiceManager::getInstance()->signals.pauseEvent();

#ifndef _DEBUG
    TrackerService * trackerService = ServiceManager::getInstance()->findService< TrackerService >();
    if (trackerService)
        trackerService->endSession("Pause");

    gameplay::Game::pause();
#endif
}

void DfgGame::resume()
{
#ifndef _DEBUG
    gameplay::Game::resume();
#endif

    ServiceManager::getInstance()->signals.resumeEvent();
}

void DfgGame::reportError(bool isFatal, const char * errorMessage, ...)
{
    va_list args;
    va_start(args, errorMessage);

    char exceptionDesc[1024];
    vsnprintf(exceptionDesc, 1023, errorMessage, args);
    exceptionDesc[1023] = '\0';

    if (isFatal)
    {
        // notify user
#ifdef WIN32
        MessageBoxA(NULL, exceptionDesc, "Critical Error", MB_OK | MB_ICONERROR);
#endif
    }

    TrackerService * trackerService = ServiceManager::getInstance()->findService< TrackerService >();
    if (!trackerService)
        return;

    exceptionDesc[149] = '\0';
    trackerService->sendException(exceptionDesc, isFatal);

    if (isFatal)
        trackerService->endSession();
}

void DfgGame::scheduleLocalNotification(time_t datetime, const char * utf8Body, const char * utf8ActionButton, int badgeNumber, const std::unordered_map< std::string, std::string >& userDictionary)
{
#ifdef __APPLE__
#if TARGET_OS_IPHONE

    if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)])
    {
        [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
    }

    UILocalNotification *localNotif = [[UILocalNotification alloc] init];
    if (localNotif == nil)
        return;

    localNotif.fireDate = [NSDate dateWithTimeIntervalSince1970:datetime];
    localNotif.timeZone = [NSTimeZone defaultTimeZone];

    localNotif.alertBody = [NSString stringWithUTF8String:utf8Body];
    localNotif.alertAction = [NSString stringWithUTF8String:utf8ActionButton];

    localNotif.soundName = UILocalNotificationDefaultSoundName;
    localNotif.applicationIconBadgeNumber = badgeNumber;

    NSMutableDictionary *infoDict = [NSMutableDictionary dictionaryWithCapacity:userDictionary.size( )];

    for( std::unordered_map< std::string, std::string >::const_iterator it = userDictionary.begin( ), end_it = userDictionary.end( ); it != end_it; it++ )
        [infoDict setObject:[NSString stringWithUTF8String:( *it ).second.c_str( )] forKey:[NSString stringWithUTF8String:( *it ).first.c_str( )]];

    localNotif.userInfo = infoDict;

    [[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
#endif
#endif
}

void DfgGame::cancelAllLocalNotifications()
{
#ifdef __APPLE__
#if TARGET_OS_IPHONE
    [[UIApplication sharedApplication] cancelAllLocalNotifications];
    [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
#endif
#endif
}

void DfgGame::preventFromSleeping(bool prevent)
{
#if defined(__APPLE__)
#if TARGET_OS_IPHONE
    [UIApplication sharedApplication].idleTimerDisabled = prevent ? YES : NO;
#endif
#elif defined(__ANDROID__)
    ANativeActivity* activity = __state->activity;
    if (prevent)
        ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0);
    else
        ANativeActivity_setWindowFlags(activity, 0, AWINDOW_FLAG_KEEP_SCREEN_ON);
#endif
}

void DfgGame::resizeEvent(unsigned int width, unsigned int height)
{
    ServiceManager::getInstance()->signals.resizeEvent(width, height);
}

void DfgGame::copyToClipboard(const char * textUTF8) const
{
#if defined(WIN32)

    const wchar_t * wstring = Utils::UTF8ToWCS(textUTF8);
    const size_t len = wcslen(wstring) + 1;
    HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len * sizeof(wchar_t));
    memcpy(GlobalLock(hMem), wstring, len * sizeof(wchar_t));
    GlobalUnlock(hMem);
    OpenClipboard(0);
    EmptyClipboard();
    SetClipboardData(CF_UNICODETEXT, hMem);
    CloseClipboard();

#elif defined(__APPLE__)

    UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
    pasteboard.string = [NSString stringWithUTF8String:textUTF8];

#elif defined(__ANDROID__)

    android_app* app = __state;
    JNIEnv* env = app->activity->env;
    JavaVM* vm = app->activity->vm;
    vm->AttachCurrentThread(&env, NULL);

    jclass looperClass = env->FindClass("android/os/Looper");
    jmethodID prepareMethodID = env->GetStaticMethodID(looperClass, "prepare", "()V");
    env->CallStaticVoidMethod(looperClass, prepareMethodID);

    jclass clsContext = env->FindClass("android/content/Context");
    jmethodID getSystemService = env->GetMethodID(clsContext, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
    jfieldID CLIPBOARD_SERVICE_ID = env->GetStaticFieldID(clsContext, "CLIPBOARD_SERVICE", "Ljava/lang/String;");
    jstring CLIPBOARD_SERVICE = (jstring)env->GetStaticObjectField(clsContext, CLIPBOARD_SERVICE_ID);
    jobject clipboard = env->CallObjectMethod(app->activity->clazz, getSystemService, CLIPBOARD_SERVICE);

    jclass clsClipData = env->FindClass("android/content/ClipData");
    jmethodID methodNewPlainText = env->GetStaticMethodID(clsClipData, "newPlainText", "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;");
    jstring text = env->NewStringUTF(textUTF8);
    jobject clipData = env->CallStaticObjectMethod(clsClipData, methodNewPlainText, text, text);

    jclass clsClipboardManager = env->FindClass("android/content/ClipboardManager");
    jmethodID methodSetPrimaryClip = env->GetMethodID(clsClipboardManager, "setPrimaryClip", "(Landroid/content/ClipData;)V");
    env->CallVoidMethod(clipboard, methodSetPrimaryClip, clipData);

    vm->DetachCurrentThread();

#endif
}<|endoftext|>


<commit_before>/* hasher.hh -- k-mer hash functions
 *
 * Copyright (C) 2018 Camille Scott
 * All rights reserved.
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

#ifndef HASHING_HH
#define HASHING_HH

#include "boink/boink.hh"
#include "oxli/alphabets.hh"
#include "oxli/kmer_hash.hh"

#include <deque>

# ifdef DEBUG_HASHING
#   define pdebug(x) do { std::cerr << std::endl << "@ " << __FILE__ <<\
                          ":" << __FUNCTION__ << ":" <<\
                          __LINE__  << std::endl << x << std::endl;\
                          } while (0)
# else
#   define pdebug(x) do {} while (0)
# endif

namespace boink {

class KmerClient {
protected:
    const uint16_t _K;
public:
    explicit KmerClient(uint16_t K) : _K(K) {}
    const uint16_t K() const { return _K; }
};

template <class Derived,
          const std::string& Alphabet = oxli::alphabets::DNA_SIMPLE>
class HashShifter : public KmerClient {
public:
    static const std::string symbols;
    std::deque<char> symbol_deque;

    hash_t set_cursor(const std::string& sequence) {
        if (sequence.length() < _K) {
            throw BoinkException("Sequence must at least length K");
        }
        if (!initialized) {
            load(sequence);
            derived().init();
        } else {
            for (auto c : sequence) {
                shift_right(c);
            }
        }
        return get();
    }

    hash_t set_cursor(const char * sequence) {
        // less safe! does not check length
        if(!initialized) {
            load(sequence);
            derived().init();
        } else {
            for (uint16_t i = 0; i < this->_K; ++i) {
                shift_right(sequence[i]);
            }
        }
        return get();
    }

    bool is_valid(const char c) const {
        for (auto symbol : symbols) {
            if (c == symbol) {
                return true;
            }
        }
        return false;
    }

    bool is_valid(const std::string& sequence) const {
        for (auto c : sequence) {
            if(!is_valid(c)) {
                return false;
            }
        }
        return true;
    }

    bool is_valid(const char * sequence) const {
        for (uint16_t i = 0; i < this->_K; ++i) {
            if(!is_valid(sequence[i])) {
                return false;
            }
        }
        return true;
    }

    void _validate(const char c) const {
        if (!this->is_valid(c)) {
            string msg("Invalid symbol: ");
            msg += c;
            throw BoinkException(msg.c_str());
        }
    }

    void _validate(const std::string& sequence) const {
        if (!is_valid(sequence)) {
            string msg("Invalid symbol in ");
            msg += sequence;
            throw BoinkException(msg.c_str());
        }
    }

    // shadowed by derived
    hash_t get() {
        return derived().get();
    }

    hash_t hash(const std::string& sequence) const {
        _validate(sequence);
        return derived()._hash(sequence);
    }

    hash_t hash(const char * sequence) const {
        _validate(sequence);
        return derived()._hash(sequence);
    }

    // shadowed by derived impl
    std::vector<shift_t> gather_left() {
        return derived().gather_left();
    }

    hash_t shift_left(const char c) {
        _validate(c);
        symbol_deque.push_front(c);
        hash_t h = derived().update_left(c);
        symbol_deque.pop_back();
        return h;
    }

    // shadowed by derived impl
    std::vector<shift_t> gather_right() {
        return derived().gather_right();
    }

    hash_t shift_right(const char c) {
        _validate(c);
        symbol_deque.push_back(c);
        hash_t h = derived().update_right(c);
        symbol_deque.pop_front();
        return h;
    }

    std::string get_cursor() const {
        return std::string(symbol_deque.begin(), symbol_deque.end());
    }

    void get_cursor(std::deque<char>& d) const {
        d.insert(d.end(), symbol_deque.begin(), symbol_deque.end());
    }

private:

    HashShifter(const std::string& start, uint16_t K) :
        KmerClient(K), initialized(false) {

        load(start);
    }

    HashShifter(uint16_t K) :
        KmerClient(K), initialized(false) {}

    friend Derived;

    Derived& derived() {
        return *static_cast<Derived*>(this);
    }

    const Derived& derived() const {
        return *static_cast<const Derived*>(this);
    }

protected:

    bool initialized;

    void load(const std::string& sequence) {
        symbol_deque.clear();
        symbol_deque.insert(symbol_deque.begin(),
                            sequence.begin(),
                            sequence.begin()+_K);
    }

    void load(const char * sequence) {
        symbol_deque.clear();
        for (uint16_t i = 0; i < this->_K; ++i) {
            symbol_deque.push_back(sequence[i]);
        }
    }

};

template<class Derived, const std::string& Alphabet>
const std::string HashShifter<Derived, Alphabet>::symbols = Alphabet;

template <const std::string& Alphabet = oxli::alphabets::DNA_SIMPLE>
class RollingHashShifter : public HashShifter<RollingHashShifter<Alphabet>,
                                              Alphabet> {
protected:
    typedef HashShifter<RollingHashShifter<Alphabet>, Alphabet> BaseShifter;

    CyclicHash<hash_t> hasher;
    using BaseShifter::_K;

public:

    //using BaseShifter::HashShifter;

    RollingHashShifter(const std::string& start, uint16_t K)
        : BaseShifter(start, K), hasher(K)
    {
        init();
    }

    RollingHashShifter(uint16_t K) :
        BaseShifter(K), hasher(K) {}

    RollingHashShifter(const RollingHashShifter& other)
        : BaseShifter(other.K()),
          hasher(other.K())
    {
        other.get_cursor(this->symbol_deque);
        init();
    }

    void init() {
        if (this->initialized) {
            return;
        }
        for (auto c : this->symbol_deque) {
            this->_validate(c);
            hasher.eat(c);
        }
        this->initialized = true;
    }

    hash_t get() {
        return hasher.hashvalue;
    }

    hash_t _hash(const std::string& sequence) const {
        return oxli::_hash_cyclic_forward(sequence, this->_K);
    }

    hash_t _hash(const char * sequence) const {
        CyclicHash<hash_t> hasher(this->_K);
        for (uint16_t i = 0; i < this->_K; ++i) {
            hasher.eat(sequence[i]);
        }
        return hasher.hashvalue;
    }

    hash_t update_left(const char c) {
        hasher.reverse_update(c, this->symbol_deque.back());
        return get();
    }

    std::vector<shift_t> gather_right() {
        std::vector<shift_t> hashes;
        const char front = this->symbol_deque.front();
        for (auto symbol : Alphabet) {
            hasher.update(front, symbol);
            hashes.push_back(shift_t(hasher.hashvalue, symbol));
            hasher.reverse_update(front, symbol);
        }
        return hashes;
    }

    hash_t update_right(const char c) {
        hasher.update(this->symbol_deque.front(), c);
        return get();
    }

    std::vector<shift_t> gather_left() {
        std::vector<shift_t> hashes;
        const char back = this->symbol_deque.back();
        for (auto symbol : Alphabet) {
            hasher.reverse_update(symbol, back);
            shift_t result(hasher.hashvalue, symbol);
            hashes.push_back(result);
            hasher.update(symbol, back);
        }

        return hashes;
    }
};

typedef RollingHashShifter<oxli::alphabets::DNA_SIMPLE> DefaultShifter;

template <class ShifterType>
class KmerIterator : public KmerClient {
    const std::string _seq;
    unsigned int index;
    unsigned int length;
    bool _initialized, _shifter_owner;

public:

    ShifterType * shifter;

    KmerIterator(const std::string& seq, uint16_t K) :
        KmerClient(K), _seq(seq),
        index(0), _initialized(false), _shifter_owner(true) {

        shifter = new ShifterType(seq, K);

        if (_seq.length() < _K) {
            throw BoinkException("Sequence must have length >= K");
        }
    }

    KmerIterator(const std::string& seq, ShifterType * shifter) :
        KmerClient(shifter->K()), _seq(seq),
        index(0), _initialized(false),
        _shifter_owner(false), shifter(shifter) {

        if(_seq.length() < _K) {
            throw BoinkException("Sequence must have length >= K");
        }

        shifter->set_cursor(seq);
    }

    hash_t first() {
        _initialized = true;

        index += 1;
        return shifter->get();
    }

    hash_t next() {
        if (!_initialized) {
            return first();
        }

        if (done()) {
            throw BoinkException("past end of iterator");
        }

        shifter->shift_right(_seq[index + _K - 1]);
        index += 1;

        return shifter->get();
    }

    bool done() const {
        return (index + _K > _seq.length());
    }

    unsigned int get_start_pos() const {
        if (!_initialized) { return 0; }
        return index - 1;
    }

    unsigned int get_end_pos() const {
        if (!_initialized) { return _K; }
        return index + _K - 1;
    }
};

/*
class FullRollingHasher {
    const char * _seq;
    const std::string _rev;
    const char _ksize;
    unsigned int index;
    unsigned int length;
    bool _initialized;
    oxli::CyclicHash<uint64_t> fwd_hasher;
    oxli::CyclicHash<uint64_t> bwd_hasher;

public:
    FullRollingHasher(const char * seq, unsigned char k) :
        _seq(seq), _rev(oxli::_revcomp(seq)), _ksize(k), index(0),
        _initialized(false), fwd_hasher(k), bwd_hasher(k)
    {
        length = strlen(_seq);
    };

    full_hash_t first() {
        _initialized = true;

        for (char i = 0; i < _ksize; ++i) {
            fwd_hasher.eat(*(_seq + i));
            bwd_hasher.eat(_rev[length - _ksize + i]);
        }
        index += 1;
        return std::make_pair(fwd_hasher.hashvalue, bwd_hasher.hashvalue);
    }

    full_hash_t next() {
        if (!_initialized) {
            return first();
        }

        if (done()) {
            throw oxli_exception("past end of iterator");
        }
        fwd_hasher.update(*(_seq + index - 1), *(_seq + index + _ksize - 1));

        // first argument is added, second is removed from the hash
        bwd_hasher.reverse_update(
          _rev[length - _ksize - index], _rev[length - index]);

        index += 1;

        return std::make_pair(fwd_hasher.hashvalue, bwd_hasher.hashvalue);
    }

    bool done() const {
        return (index + _ksize > length);
    }

    unsigned int get_start_pos() const {
        if (!_initialized) { return 0; }
        return index - 1;
    }

    unsigned int get_end_pos() const {
        if (!_initialized) { return _ksize; }
        return index + _ksize - 1;
    }
};
*/

}

#undef pdebug
#endif
<commit_msg>style change<commit_after>/* hasher.hh -- k-mer hash functions
 *
 * Copyright (C) 2018 Camille Scott
 * All rights reserved.
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

#ifndef HASHING_HH
#define HASHING_HH

#include "boink/boink.hh"
#include "oxli/alphabets.hh"
#include "oxli/kmer_hash.hh"

#include <deque>

# ifdef DEBUG_HASHING
#   define pdebug(x) do { std::cerr << std::endl << "@ " << __FILE__ <<\
                          ":" << __FUNCTION__ << ":" <<\
                          __LINE__  << std::endl << x << std::endl;\
                          } while (0)
# else
#   define pdebug(x) do {} while (0)
# endif

namespace boink {

class KmerClient {
protected:
    const uint16_t _K;
public:
    explicit KmerClient(uint16_t K) : _K(K) {}
    const uint16_t K() const { return _K; }
};

template <class Derived,
          const std::string& Alphabet = oxli::alphabets::DNA_SIMPLE>
class HashShifter : public KmerClient {
public:
    static const std::string symbols;
    std::deque<char> symbol_deque;

    hash_t set_cursor(const std::string& sequence) {
        if (sequence.length() < _K) {
            throw BoinkException("Sequence must at least length K");
        }
        if (!initialized) {
            load(sequence);
            derived().init();
        } else {
            for (auto c : sequence) {
                shift_right(c);
            }
        }
        return get();
    }

    hash_t set_cursor(const char * sequence) {
        // less safe! does not check length
        if(!initialized) {
            load(sequence);
            derived().init();
        } else {
            for (uint16_t i = 0; i < this->_K; ++i) {
                shift_right(sequence[i]);
            }
        }
        return get();
    }

    bool is_valid(const char c) const {
        for (auto symbol : symbols) {
            if (c == symbol) {
                return true;
            }
        }
        return false;
    }

    bool is_valid(const std::string& sequence) const {
        for (auto c : sequence) {
            if(!is_valid(c)) {
                return false;
            }
        }
        return true;
    }

    bool is_valid(const char * sequence) const {
        for (uint16_t i = 0; i < this->_K; ++i) {
            if(!is_valid(sequence[i])) {
                return false;
            }
        }
        return true;
    }

    void _validate(const char c) const {
        if (!this->is_valid(c)) {
            string msg("Invalid symbol: ");
            msg += c;
            throw BoinkException(msg.c_str());
        }
    }

    void _validate(const std::string& sequence) const {
        if (!is_valid(sequence)) {
            string msg("Invalid symbol in ");
            msg += sequence;
            throw BoinkException(msg.c_str());
        }
    }

    // shadowed by derived
    hash_t get() {
        return derived().get();
    }

    hash_t hash(const std::string& sequence) const {
        _validate(sequence);
        return derived()._hash(sequence);
    }

    hash_t hash(const char * sequence) const {
        _validate(sequence);
        return derived()._hash(sequence);
    }

    // shadowed by derived impl
    std::vector<shift_t> gather_left() {
        return derived().gather_left();
    }

    hash_t shift_left(const char c) {
        _validate(c);
        symbol_deque.push_front(c);
        hash_t h = derived().update_left(c);
        symbol_deque.pop_back();
        return h;
    }

    // shadowed by derived impl
    std::vector<shift_t> gather_right() {
        return derived().gather_right();
    }

    hash_t shift_right(const char c) {
        _validate(c);
        symbol_deque.push_back(c);
        hash_t h = derived().update_right(c);
        symbol_deque.pop_front();
        return h;
    }

    std::string get_cursor() const {
        return std::string(symbol_deque.begin(), symbol_deque.end());
    }

    void get_cursor(std::deque<char>& d) const {
        d.insert(d.end(), symbol_deque.begin(), symbol_deque.end());
    }

private:

    HashShifter(const std::string& start, uint16_t K) :
        KmerClient(K), initialized(false) {

        load(start);
    }

    HashShifter(uint16_t K) :
        KmerClient(K), initialized(false) {}

    friend Derived;

    Derived& derived() {
        return *static_cast<Derived*>(this);
    }

    const Derived& derived() const {
        return *static_cast<const Derived*>(this);
    }

protected:

    bool initialized;

    void load(const std::string& sequence) {
        symbol_deque.clear();
        symbol_deque.insert(symbol_deque.begin(),
                            sequence.begin(),
                            sequence.begin()+_K);
    }

    void load(const char * sequence) {
        symbol_deque.clear();
        for (uint16_t i = 0; i < this->_K; ++i) {
            symbol_deque.push_back(sequence[i]);
        }
    }

};

template<class Derived, const std::string& Alphabet>
const std::string HashShifter<Derived, Alphabet>::symbols = Alphabet;

template <const std::string& Alphabet = oxli::alphabets::DNA_SIMPLE>
class RollingHashShifter : public HashShifter<RollingHashShifter<Alphabet>,
                                              Alphabet> {
protected:
    typedef HashShifter<RollingHashShifter<Alphabet>, Alphabet> BaseShifter;

    CyclicHash<hash_t> hasher;
    using BaseShifter::_K;

public:

    //using BaseShifter::HashShifter;

    RollingHashShifter(const std::string& start, uint16_t K)
        : BaseShifter(start, K), hasher(K)
    {
        init();
    }

    RollingHashShifter(uint16_t K)
        : BaseShifter(K),
          hasher(K)
    {
    }

    RollingHashShifter(const RollingHashShifter& other)
        : BaseShifter(other.K()),
          hasher(other.K())
    {
        other.get_cursor(this->symbol_deque);
        init();
    }

    void init() {
        if (this->initialized) {
            return;
        }
        for (auto c : this->symbol_deque) {
            this->_validate(c);
            hasher.eat(c);
        }
        this->initialized = true;
    }

    hash_t get() {
        return hasher.hashvalue;
    }

    hash_t _hash(const std::string& sequence) const {
        return oxli::_hash_cyclic_forward(sequence, this->_K);
    }

    hash_t _hash(const char * sequence) const {
        CyclicHash<hash_t> hasher(this->_K);
        for (uint16_t i = 0; i < this->_K; ++i) {
            hasher.eat(sequence[i]);
        }
        return hasher.hashvalue;
    }

    hash_t update_left(const char c) {
        hasher.reverse_update(c, this->symbol_deque.back());
        return get();
    }

    std::vector<shift_t> gather_right() {
        std::vector<shift_t> hashes;
        const char front = this->symbol_deque.front();
        for (auto symbol : Alphabet) {
            hasher.update(front, symbol);
            hashes.push_back(shift_t(hasher.hashvalue, symbol));
            hasher.reverse_update(front, symbol);
        }
        return hashes;
    }

    hash_t update_right(const char c) {
        hasher.update(this->symbol_deque.front(), c);
        return get();
    }

    std::vector<shift_t> gather_left() {
        std::vector<shift_t> hashes;
        const char back = this->symbol_deque.back();
        for (auto symbol : Alphabet) {
            hasher.reverse_update(symbol, back);
            shift_t result(hasher.hashvalue, symbol);
            hashes.push_back(result);
            hasher.update(symbol, back);
        }

        return hashes;
    }
};

typedef RollingHashShifter<oxli::alphabets::DNA_SIMPLE> DefaultShifter;

template <class ShifterType>
class KmerIterator : public KmerClient {
    const std::string _seq;
    unsigned int index;
    unsigned int length;
    bool _initialized, _shifter_owner;

public:

    ShifterType * shifter;

    KmerIterator(const std::string& seq, uint16_t K) :
        KmerClient(K), _seq(seq),
        index(0), _initialized(false), _shifter_owner(true) {

        shifter = new ShifterType(seq, K);

        if (_seq.length() < _K) {
            throw BoinkException("Sequence must have length >= K");
        }
    }

    KmerIterator(const std::string& seq, ShifterType * shifter) :
        KmerClient(shifter->K()), _seq(seq),
        index(0), _initialized(false),
        _shifter_owner(false), shifter(shifter) {

        if(_seq.length() < _K) {
            throw BoinkException("Sequence must have length >= K");
        }

        shifter->set_cursor(seq);
    }

    hash_t first() {
        _initialized = true;

        index += 1;
        return shifter->get();
    }

    hash_t next() {
        if (!_initialized) {
            return first();
        }

        if (done()) {
            throw BoinkException("past end of iterator");
        }

        shifter->shift_right(_seq[index + _K - 1]);
        index += 1;

        return shifter->get();
    }

    bool done() const {
        return (index + _K > _seq.length());
    }

    unsigned int get_start_pos() const {
        if (!_initialized) { return 0; }
        return index - 1;
    }

    unsigned int get_end_pos() const {
        if (!_initialized) { return _K; }
        return index + _K - 1;
    }
};

/*
class FullRollingHasher {
    const char * _seq;
    const std::string _rev;
    const char _ksize;
    unsigned int index;
    unsigned int length;
    bool _initialized;
    oxli::CyclicHash<uint64_t> fwd_hasher;
    oxli::CyclicHash<uint64_t> bwd_hasher;

public:
    FullRollingHasher(const char * seq, unsigned char k) :
        _seq(seq), _rev(oxli::_revcomp(seq)), _ksize(k), index(0),
        _initialized(false), fwd_hasher(k), bwd_hasher(k)
    {
        length = strlen(_seq);
    };

    full_hash_t first() {
        _initialized = true;

        for (char i = 0; i < _ksize; ++i) {
            fwd_hasher.eat(*(_seq + i));
            bwd_hasher.eat(_rev[length - _ksize + i]);
        }
        index += 1;
        return std::make_pair(fwd_hasher.hashvalue, bwd_hasher.hashvalue);
    }

    full_hash_t next() {
        if (!_initialized) {
            return first();
        }

        if (done()) {
            throw oxli_exception("past end of iterator");
        }
        fwd_hasher.update(*(_seq + index - 1), *(_seq + index + _ksize - 1));

        // first argument is added, second is removed from the hash
        bwd_hasher.reverse_update(
          _rev[length - _ksize - index], _rev[length - index]);

        index += 1;

        return std::make_pair(fwd_hasher.hashvalue, bwd_hasher.hashvalue);
    }

    bool done() const {
        return (index + _ksize > length);
    }

    unsigned int get_start_pos() const {
        if (!_initialized) { return 0; }
        return index - 1;
    }

    unsigned int get_end_pos() const {
        if (!_initialized) { return _ksize; }
        return index + _ksize - 1;
    }
};
*/

}

#undef pdebug
#endif
<|endoftext|>


<commit_before><commit_msg>Reduce max load size from 200 to 50MB (fix #954)<commit_after><|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (C) 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 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 OWNER 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 esc.cpp
 *
 * @author Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#include "esc.hpp"
#include <systemlib/err.h>
#include <drivers/drv_hrt.h>

#define MOTOR_BIT(x) (1<<(x))

using namespace time_literals;

UavcanEscController::UavcanEscController(uavcan::INode &node) :
	_node(node),
	_uavcan_pub_raw_cmd(node),
	_uavcan_sub_status(node),
	_orb_timer(node)
{
	_uavcan_pub_raw_cmd.setPriority(UAVCAN_COMMAND_TRANSFER_PRIORITY);

	if (_perfcnt_invalid_input == nullptr) {
		errx(1, "uavcan: couldn't allocate _perfcnt_invalid_input");
	}

	if (_perfcnt_scaling_error == nullptr) {
		errx(1, "uavcan: couldn't allocate _perfcnt_scaling_error");
	}
}

UavcanEscController::~UavcanEscController()
{
	perf_free(_perfcnt_invalid_input);
	perf_free(_perfcnt_scaling_error);
}

int UavcanEscController::init()
{
	// ESC status subscription
	int res = _uavcan_sub_status.start(StatusCbBinder(this, &UavcanEscController::esc_status_sub_cb));

	if (res < 0) {
		warnx("ESC status sub failed %i", res);
		return res;
	}

	// ESC status will be relayed from UAVCAN bus into ORB at this rate
	_orb_timer.setCallback(TimerCbBinder(this, &UavcanEscController::orb_pub_timer_cb));
	_orb_timer.startPeriodic(uavcan::MonotonicDuration::fromMSec(1000 / ESC_STATUS_UPDATE_RATE_HZ));

	return res;
}

void UavcanEscController::update_outputs(float *outputs, unsigned num_outputs)
{
	if ((outputs == nullptr) ||
	    (num_outputs > uavcan::equipment::esc::RawCommand::FieldTypes::cmd::MaxSize) ||
	    (num_outputs > esc_status_s::CONNECTED_ESC_MAX)) {
		perf_count(_perfcnt_invalid_input);
		return;
	}

	/*
	 * Rate limiting - we don't want to congest the bus
	 */
	const auto timestamp = _node.getMonotonicTime();

	if ((timestamp - _prev_cmd_pub).toUSec() < (1000000 / MAX_RATE_HZ)) {
		return;
	}

	_prev_cmd_pub = timestamp;

	/*
	 * Fill the command message
	 * If unarmed, we publish an empty message anyway
	 */
	uavcan::equipment::esc::RawCommand msg;

	actuator_outputs_s actuator_outputs = {};
	actuator_outputs.noutputs = num_outputs;
	actuator_outputs.timestamp = hrt_absolute_time();

	static const int cmd_max = uavcan::equipment::esc::RawCommand::FieldTypes::cmd::RawValueType::max();
	const float cmd_min = _run_at_idle_throttle_when_armed ? 1.0F : 0.0F;

	for (unsigned i = 0; i < num_outputs; i++) {
		if (_armed_mask & MOTOR_BIT(i)) {
			float scaled = (outputs[i] + 1.0F) * 0.5F * cmd_max;

			// trim negative values back to minimum
			if (scaled < cmd_min) {
				scaled = cmd_min;
				perf_count(_perfcnt_scaling_error);
			}

			if (scaled > cmd_max) {
				scaled = cmd_max;
				perf_count(_perfcnt_scaling_error);
			}

			msg.cmd.push_back(static_cast<int>(scaled));

			_esc_status.esc[i].esc_setpoint_raw = abs(static_cast<int>(scaled));
			actuator_outputs.output[i] = scaled;

		} else {
			msg.cmd.push_back(static_cast<unsigned>(0));
			actuator_outputs.output[i] = 0.0f;
		}
	}

	/*
	 * Remove channels that are always zero.
	 * The objective of this optimization is to avoid broadcasting multi-frame transfers when a single frame
	 * transfer would be enough. This is a valid optimization as the UAVCAN specification implies that all
	 * non-specified ESC setpoints should be considered zero.
	 * The positive outcome is a (marginally) lower bus traffic and lower CPU load.
	 *
	 * From the standpoint of the PX4 architecture, however, this is a hack. It should be investigated why
	 * the mixer returns more outputs than are actually used.
	 */
	for (int index = int(msg.cmd.size()) - 1; index >= _max_number_of_nonzero_outputs; index--) {
		if (msg.cmd[index] != 0) {
			_max_number_of_nonzero_outputs = index + 1;
			break;
		}
	}

	msg.cmd.resize(_max_number_of_nonzero_outputs);

	/*
	 * Publish the command message to the bus
	 * Note that for a quadrotor it takes one CAN frame
	 */
	(void)_uavcan_pub_raw_cmd.broadcast(msg);

	// Publish actuator outputs
	if (_actuator_outputs_pub != nullptr) {
		orb_publish(ORB_ID(actuator_outputs), _actuator_outputs_pub, &actuator_outputs);

	} else {
		int instance;
		_actuator_outputs_pub = orb_advertise_multi(ORB_ID(actuator_outputs), &actuator_outputs,
					&instance, ORB_PRIO_DEFAULT);
	}

}

void UavcanEscController::arm_all_escs(bool arm)
{
	if (arm) {
		_armed_mask = -1;

	} else {
		_armed_mask = 0;
	}
}

void UavcanEscController::arm_single_esc(int num, bool arm)
{
	if (arm) {
		_armed_mask = MOTOR_BIT(num);

	} else {
		_armed_mask = 0;
	}
}

void UavcanEscController::esc_status_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::esc::Status> &msg)
{
	if (msg.esc_index < esc_status_s::CONNECTED_ESC_MAX) {
		auto &ref = _esc_status.esc[msg.esc_index];

		ref.esc_address = msg.getSrcNodeID().get();
		ref.timestamp       = hrt_absolute_time();
		ref.esc_voltage     = msg.voltage;
		ref.esc_current     = msg.current;
		ref.esc_temperature = msg.temperature;
		ref.esc_setpoint    = msg.power_rating_pct;
		ref.esc_rpm         = msg.rpm;
		ref.esc_errorcount  = msg.error_count;
	}
}

void UavcanEscController::orb_pub_timer_cb(const uavcan::TimerEvent &)
{
	_esc_status.timestamp = hrt_absolute_time();
	_esc_status.esc_count = _rotor_count;
	_esc_status.counter += 1;
	_esc_status.esc_connectiontype = esc_status_s::ESC_CONNECTION_TYPE_CAN;
	_esc_status.esc_online_flags = UavcanEscController::check_escs_status();

	if (_esc_status_pub != nullptr) {
		(void)orb_publish(ORB_ID(esc_status), _esc_status_pub, &_esc_status);

	} else {
		_esc_status_pub = orb_advertise(ORB_ID(esc_status), &_esc_status);
	}
}

uint8_t UavcanEscController::check_escs_status()
{
	int esc_status_flags = 0;
	hrt_abstime now = hrt_absolute_time();

	for (int index = 0; index < esc_status_s::CONNECTED_ESC_MAX; index++) {

		if (_esc_status.esc[index].timestamp > 0 && now - _esc_status.esc[index].timestamp < 800_ms) {
			esc_status_flags |= (1 << index);
		}

	}

	return esc_status_flags;
}
<commit_msg>UAVCAN esc: relax threshold for detecting offline escs.<commit_after>/****************************************************************************
 *
 *   Copyright (C) 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 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 OWNER 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 esc.cpp
 *
 * @author Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#include "esc.hpp"
#include <systemlib/err.h>
#include <drivers/drv_hrt.h>

#define MOTOR_BIT(x) (1<<(x))

using namespace time_literals;

UavcanEscController::UavcanEscController(uavcan::INode &node) :
	_node(node),
	_uavcan_pub_raw_cmd(node),
	_uavcan_sub_status(node),
	_orb_timer(node)
{
	_uavcan_pub_raw_cmd.setPriority(UAVCAN_COMMAND_TRANSFER_PRIORITY);

	if (_perfcnt_invalid_input == nullptr) {
		errx(1, "uavcan: couldn't allocate _perfcnt_invalid_input");
	}

	if (_perfcnt_scaling_error == nullptr) {
		errx(1, "uavcan: couldn't allocate _perfcnt_scaling_error");
	}
}

UavcanEscController::~UavcanEscController()
{
	perf_free(_perfcnt_invalid_input);
	perf_free(_perfcnt_scaling_error);
}

int UavcanEscController::init()
{
	// ESC status subscription
	int res = _uavcan_sub_status.start(StatusCbBinder(this, &UavcanEscController::esc_status_sub_cb));

	if (res < 0) {
		warnx("ESC status sub failed %i", res);
		return res;
	}

	// ESC status will be relayed from UAVCAN bus into ORB at this rate
	_orb_timer.setCallback(TimerCbBinder(this, &UavcanEscController::orb_pub_timer_cb));
	_orb_timer.startPeriodic(uavcan::MonotonicDuration::fromMSec(1000 / ESC_STATUS_UPDATE_RATE_HZ));

	return res;
}

void UavcanEscController::update_outputs(float *outputs, unsigned num_outputs)
{
	if ((outputs == nullptr) ||
	    (num_outputs > uavcan::equipment::esc::RawCommand::FieldTypes::cmd::MaxSize) ||
	    (num_outputs > esc_status_s::CONNECTED_ESC_MAX)) {
		perf_count(_perfcnt_invalid_input);
		return;
	}

	/*
	 * Rate limiting - we don't want to congest the bus
	 */
	const auto timestamp = _node.getMonotonicTime();

	if ((timestamp - _prev_cmd_pub).toUSec() < (1000000 / MAX_RATE_HZ)) {
		return;
	}

	_prev_cmd_pub = timestamp;

	/*
	 * Fill the command message
	 * If unarmed, we publish an empty message anyway
	 */
	uavcan::equipment::esc::RawCommand msg;

	actuator_outputs_s actuator_outputs = {};
	actuator_outputs.noutputs = num_outputs;
	actuator_outputs.timestamp = hrt_absolute_time();

	static const int cmd_max = uavcan::equipment::esc::RawCommand::FieldTypes::cmd::RawValueType::max();
	const float cmd_min = _run_at_idle_throttle_when_armed ? 1.0F : 0.0F;

	for (unsigned i = 0; i < num_outputs; i++) {
		if (_armed_mask & MOTOR_BIT(i)) {
			float scaled = (outputs[i] + 1.0F) * 0.5F * cmd_max;

			// trim negative values back to minimum
			if (scaled < cmd_min) {
				scaled = cmd_min;
				perf_count(_perfcnt_scaling_error);
			}

			if (scaled > cmd_max) {
				scaled = cmd_max;
				perf_count(_perfcnt_scaling_error);
			}

			msg.cmd.push_back(static_cast<int>(scaled));

			_esc_status.esc[i].esc_setpoint_raw = abs(static_cast<int>(scaled));
			actuator_outputs.output[i] = scaled;

		} else {
			msg.cmd.push_back(static_cast<unsigned>(0));
			actuator_outputs.output[i] = 0.0f;
		}
	}

	/*
	 * Remove channels that are always zero.
	 * The objective of this optimization is to avoid broadcasting multi-frame transfers when a single frame
	 * transfer would be enough. This is a valid optimization as the UAVCAN specification implies that all
	 * non-specified ESC setpoints should be considered zero.
	 * The positive outcome is a (marginally) lower bus traffic and lower CPU load.
	 *
	 * From the standpoint of the PX4 architecture, however, this is a hack. It should be investigated why
	 * the mixer returns more outputs than are actually used.
	 */
	for (int index = int(msg.cmd.size()) - 1; index >= _max_number_of_nonzero_outputs; index--) {
		if (msg.cmd[index] != 0) {
			_max_number_of_nonzero_outputs = index + 1;
			break;
		}
	}

	msg.cmd.resize(_max_number_of_nonzero_outputs);

	/*
	 * Publish the command message to the bus
	 * Note that for a quadrotor it takes one CAN frame
	 */
	(void)_uavcan_pub_raw_cmd.broadcast(msg);

	// Publish actuator outputs
	if (_actuator_outputs_pub != nullptr) {
		orb_publish(ORB_ID(actuator_outputs), _actuator_outputs_pub, &actuator_outputs);

	} else {
		int instance;
		_actuator_outputs_pub = orb_advertise_multi(ORB_ID(actuator_outputs), &actuator_outputs,
					&instance, ORB_PRIO_DEFAULT);
	}

}

void UavcanEscController::arm_all_escs(bool arm)
{
	if (arm) {
		_armed_mask = -1;

	} else {
		_armed_mask = 0;
	}
}

void UavcanEscController::arm_single_esc(int num, bool arm)
{
	if (arm) {
		_armed_mask = MOTOR_BIT(num);

	} else {
		_armed_mask = 0;
	}
}

void UavcanEscController::esc_status_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::esc::Status> &msg)
{
	if (msg.esc_index < esc_status_s::CONNECTED_ESC_MAX) {
		auto &ref = _esc_status.esc[msg.esc_index];

		ref.esc_address = msg.getSrcNodeID().get();
		ref.timestamp       = hrt_absolute_time();
		ref.esc_voltage     = msg.voltage;
		ref.esc_current     = msg.current;
		ref.esc_temperature = msg.temperature;
		ref.esc_setpoint    = msg.power_rating_pct;
		ref.esc_rpm         = msg.rpm;
		ref.esc_errorcount  = msg.error_count;
	}
}

void UavcanEscController::orb_pub_timer_cb(const uavcan::TimerEvent &)
{
	_esc_status.timestamp = hrt_absolute_time();
	_esc_status.esc_count = _rotor_count;
	_esc_status.counter += 1;
	_esc_status.esc_connectiontype = esc_status_s::ESC_CONNECTION_TYPE_CAN;
	_esc_status.esc_online_flags = UavcanEscController::check_escs_status();

	if (_esc_status_pub != nullptr) {
		(void)orb_publish(ORB_ID(esc_status), _esc_status_pub, &_esc_status);

	} else {
		_esc_status_pub = orb_advertise(ORB_ID(esc_status), &_esc_status);
	}
}

uint8_t UavcanEscController::check_escs_status()
{
	int esc_status_flags = 0;
	hrt_abstime now = hrt_absolute_time();

	for (int index = 0; index < esc_status_s::CONNECTED_ESC_MAX; index++) {

		if (_esc_status.esc[index].timestamp > 0 && now - _esc_status.esc[index].timestamp < 1200_ms) {
			esc_status_flags |= (1 << index);
		}

	}

	return esc_status_flags;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef DLL_CONV_RBM_INL
#define DLL_CONV_RBM_INL

#include <cstddef>
#include <ctime>
#include <random>

#include "cpp_utils/assert.hpp"             //Assertions
#include "cpp_utils/stop_watch.hpp"         //Performance counter

#include "etl/etl.hpp"
#include "etl/convolution.hpp"

#include "standard_conv_rbm.hpp"           //The base class
#include "math.hpp"               //Logistic sigmoid
#include "io.hpp"                 //Binary load/store functions
#include "tmp.hpp"
#include "checks.hpp"

namespace dll {

/*!
 * \brief Convolutional Restricted Boltzmann Machine
 *
 * This follows the definition of a CRBM by Honglak Lee.
 */
template<typename Desc>
struct conv_rbm final : public standard_conv_rbm<conv_rbm<Desc>, Desc> {
    using desc = Desc;
    using weight = typename desc::weight;
    using this_type = conv_rbm<desc>;
    using base_type = standard_conv_rbm<this_type, desc>;

    static constexpr const unit_type visible_unit = desc::visible_unit;
    static constexpr const unit_type hidden_unit = desc::hidden_unit;

    static constexpr const std::size_t NV = desc::NV;
    static constexpr const std::size_t NH = desc::NH;
    static constexpr const std::size_t NC = desc::NC;
    static constexpr const std::size_t K = desc::K;

    static constexpr const std::size_t NW = NV - NH + 1; //By definition

    etl::fast_matrix<weight, NC, K, NW, NW> w;      //shared weights
    etl::fast_vector<weight, K> b;                  //hidden biases bk
    etl::fast_vector<weight, NC> c;                 //visible single bias c

    etl::fast_matrix<weight, NC, NV, NV> v1;        //visible units

    etl::fast_matrix<weight, K, NH, NH> h1_a;       //Activation probabilities of reconstructed hidden units
    etl::fast_matrix<weight, K, NH, NH> h1_s;       //Sampled values of reconstructed hidden units

    etl::fast_matrix<weight, NC, NV, NV> v2_a;      //Activation probabilities of reconstructed visible units
    etl::fast_matrix<weight, NC, NV, NV> v2_s;      //Sampled values of reconstructed visible units

    etl::fast_matrix<weight, K, NH, NH> h2_a;       //Activation probabilities of reconstructed hidden units
    etl::fast_matrix<weight, K, NH, NH> h2_s;       //Sampled values of reconstructed hidden units

    //Convolution data

    etl::fast_matrix<weight, NC+1, K, NH, NH> v_cv; //Temporary convolution
    etl::fast_matrix<weight, K+1, NV, NV> h_cv;     //Temporary convolution

    conv_rbm() : base_type() {
        //Initialize the weights with a zero-mean and unit variance Gaussian distribution
        w = 0.01 * etl::normal_generator();
        b = -0.1;
        c = 0.0;
    }

    static constexpr std::size_t input_size(){
        return NV * NV * NC;
    }

    static constexpr std::size_t output_size(){
        return NH * NH * K;
    }

    static std::string to_short_string(){
        char buffer[1024];
        snprintf(buffer, 1024, "CRBM: %lux%lux%lu -> (%lux%lu) -> %lux%lux%lu", NV, NV, NC, NW, NW, NH, NH, K);
        return {buffer};
    }

    void display() const {
        std::cout << to_short_string() << std::endl;
    }

    template<typename H1, typename H2, typename V1, typename V2>
    void activate_hidden(H1&& h_a, H2&& h_s, const V1& v_a, const V2& v_s){
        activate_hidden(std::forward<H1>(h_a), std::forward<H2>(h_s), v_a, v_s, v_cv);
    }

    template<typename H1, typename H2, typename V1, typename V2>
    void activate_visible(const H1& h_a, const H2& h_s, V1&& v_a, V2&& v_s){
        activate_visible(h_a, h_s, std::forward<V1>(v_a), std::forward<V2>(v_s), h_cv);
    }

    template<typename H1, typename H2, typename V1, typename V2, typename VCV>
    void activate_hidden(H1&& h_a, H2&& h_s, const V1& v_a, const V2&, VCV&& v_cv){
        using namespace etl;

        v_cv(NC) = 0;

        for(std::size_t channel = 0; channel < NC; ++channel){
            for(size_t k = 0; k < K; ++k){
                etl::convolve_2d_valid(v_a(channel), fflip(w(channel)(k)), v_cv(channel)(k));
            }

            v_cv(NC) += v_cv(channel);
        }

        if(hidden_unit == unit_type::BINARY){
            h_a = sigmoid(etl::rep<NH, NH>(b) + v_cv(NC));
            h_s = bernoulli(h_a);
        } else if(hidden_unit == unit_type::RELU){
            h_a = max(etl::rep<NH, NH>(b) + v_cv(NC), 0.0);
            h_s = logistic_noise(h_a);
        } else if(hidden_unit == unit_type::RELU6){
            h_a = min(max(etl::rep<NH, NH>(b) + v_cv(NC), 0.0), 6.0);
            h_s = ranged_noise(h_a, 6.0);
        } else if(hidden_unit == unit_type::RELU1){
            h_a = min(max(etl::rep<NH, NH>(b) + v_cv(NC), 0.0), 1.0);
            h_s = ranged_noise(h_a, 1.0);
        } else {
            cpp_unreachable("Invalid path");
        }

        nan_check_deep(h_a);
        nan_check_deep(h_s);
    }

    template<typename H1, typename H2, typename V1, typename V2, typename HCV>
    void activate_visible(const H1&, const H2& h_s, V1&& v_a, V2&& v_s, HCV&& h_cv){
        using namespace etl;

        for(std::size_t channel = 0; channel < NC; ++channel){
            h_cv(K) = 0.0;

            for(std::size_t k = 0; k < K; ++k){
                etl::convolve_2d_full(h_s(k), w(channel)(k), h_cv(k));
                h_cv(K) += h_cv(k);
            }

            if(visible_unit == unit_type::BINARY){
                v_a(channel) = sigmoid(c(channel) + h_cv(K));
                v_s(channel) = bernoulli(v_a(channel));
            } else if(visible_unit == unit_type::GAUSSIAN){
                v_a(channel) = c(channel) + h_cv(K);
                v_s(channel) = normal_noise(v_a(channel));
            } else {
                cpp_unreachable("Invalid path");
            }
        }

        nan_check_deep(v_a);
        nan_check_deep(v_s);
    }

    template<typename V, typename H, cpp::enable_if_u<etl::is_etl_expr<V>::value> = cpp::detail::dummy>
    weight energy(const V& v, const H& h){
        if(desc::visible_unit == unit_type::BINARY && desc::hidden_unit == unit_type::BINARY){
            //Definition according to Honglak Lee
            //E(v,h) = - sum_k hk . (Wk*v) - sum_k bk sum_h hk - c sum_v v

            v_cv(NC) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                for(size_t k = 0; k < K; ++k){
                    etl::convolve_2d_valid(v(channel), fflip(w(channel)(k)), v_cv(channel)(k));
                }

                v_cv(NC) += v_cv(channel);
            }

            return - etl::sum(c * etl::sum_r(v)) - etl::sum(b * etl::sum_r(h)) - etl::sum(h * v_cv(NC));
        } else if(desc::visible_unit == unit_type::GAUSSIAN && desc::hidden_unit == unit_type::BINARY){
            //Definition according to Honglak Lee / Mixed with Gaussian
            //E(v,h) = - sum_k hk . (Wk*v) - sum_k bk sum_h hk - sum_v ((v - c) ^ 2 / 2)

            v_cv(NC) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                for(size_t k = 0; k < K; ++k){
                    etl::convolve_2d_valid(v(channel), fflip(w(channel)(k)), v_cv(channel)(k));
                }

                v_cv(NC) += v_cv(channel);
            }

            return -sum(etl::pow(v - etl::rep<NV, NV>(c), 2) / 2.0) - etl::sum(b * etl::sum_r(h)) - etl::sum(h * v_cv(NC));
        } else {
            return 0.0;
        }
    }

    template<typename V, typename H, cpp::disable_if_u<etl::is_etl_expr<V>::value> = cpp::detail::dummy>
    weight energy(const V& v, const H& h){
        static etl::fast_matrix<weight, NC, NV, NV> ev;
        static etl::fast_matrix<weight, K, NH, NH> eh;

        ev = v;
        eh = h;

        return energy(ev, eh);
    }

    template<typename V>
    weight free_energy_impl(const V& v){
        if(desc::visible_unit == unit_type::BINARY && desc::hidden_unit == unit_type::BINARY){
            //Definition computed from E(v,h)

            v_cv(NC) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                for(size_t k = 0; k < K; ++k){
                    etl::convolve_2d_valid(v(channel), fflip(w(channel)(k)), v_cv(channel)(k));
                }

                v_cv(NC) += v_cv(channel);
            }

            auto x = etl::rep<NH, NH>(b) + v_cv(NC);

            return - etl::sum(c * etl::sum_r(v)) - etl::sum(etl::log(1.0 + etl::exp(x)));
        } else if(desc::visible_unit == unit_type::GAUSSIAN && desc::hidden_unit == unit_type::BINARY){
            //Definition computed from E(v,h)

            v_cv(NC) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                for(size_t k = 0; k < K; ++k){
                    etl::convolve_2d_valid(v(channel), fflip(w(channel)(k)), v_cv(channel)(k));
                }

                v_cv(NC) += v_cv(channel);
            }

            auto x = etl::rep<NH, NH>(b) + v_cv(NC);

            return -sum(etl::pow(v - etl::rep<NV, NV>(c), 2) / 2.0) - etl::sum(etl::log(1.0 + etl::exp(x)));
        } else {
            return 0.0;
        }
    }

    template<typename V>
    weight free_energy(const V& v){
        static etl::fast_matrix<weight, NC, NV, NV> ev;
        ev = v;
        return free_energy_impl(ev);
    }

    weight free_energy() const {
        return free_energy_impl(v1);
    }

    //Utilities for DBNs

    using input_one_t = etl::dyn_matrix<weight, 3>;
    using output_one_t = etl::dyn_matrix<weight, 3>;
    using input_t = std::vector<input_one_t>;
    using output_t = std::vector<output_one_t>;

    template<typename Iterator>
    static auto convert_input(Iterator&& first, Iterator&& last){
        input_t input;
        input.reserve(std::distance(std::forward<Iterator>(first), std::forward<Iterator>(last)));

        std::for_each(std::forward<Iterator>(first), std::forward<Iterator>(last), [&input](auto& sample){
            input.emplace_back(NC, NV, NV);
            input.back() = sample;
        });

        return input;
    }

    void prepare_output(output_t& output, std::size_t samples){
        output.clear();
        output.reserve(samples);

        for(std::size_t i = 0; i < samples; ++i){
            output.emplace_back(K, NH, NH);
        }
    }

    void activate_one(const input_one_t& input, output_one_t& h_a, output_one_t& h_s){
        v1 = input;
        activate_hidden(h_a, h_s, v1, v1);
    }

    void activate_many(const input_t& input, output_t& h_a, output_t& h_s){
        for(std::size_t i = 0; i < input.size(); ++i){
            activate_one(input[i], h_a[i], h_s[i]);
        }
    }
};

//Allow odr-use of the constexpr static members

template<typename Desc>
const std::size_t conv_rbm<Desc>::NV;

template<typename Desc>
const std::size_t conv_rbm<Desc>::NH;

template<typename Desc>
const std::size_t conv_rbm<Desc>::NC;

template<typename Desc>
const std::size_t conv_rbm<Desc>::NW;

template<typename Desc>
const std::size_t conv_rbm<Desc>::K;

} //end of dll namespace

#endif
<commit_msg>Port changes<commit_after>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef DLL_CONV_RBM_INL
#define DLL_CONV_RBM_INL

#include <cstddef>
#include <ctime>
#include <random>

#include "cpp_utils/assert.hpp"             //Assertions
#include "cpp_utils/stop_watch.hpp"         //Performance counter

#include "etl/etl.hpp"
#include "etl/convolution.hpp"

#include "standard_conv_rbm.hpp"           //The base class
#include "math.hpp"               //Logistic sigmoid
#include "io.hpp"                 //Binary load/store functions
#include "tmp.hpp"
#include "checks.hpp"

namespace dll {

/*!
 * \brief Convolutional Restricted Boltzmann Machine
 *
 * This follows the definition of a CRBM by Honglak Lee.
 */
template<typename Desc>
struct conv_rbm final : public standard_conv_rbm<conv_rbm<Desc>, Desc> {
    using desc = Desc;
    using weight = typename desc::weight;
    using this_type = conv_rbm<desc>;
    using base_type = standard_conv_rbm<this_type, desc>;

    static constexpr const unit_type visible_unit = desc::visible_unit;
    static constexpr const unit_type hidden_unit = desc::hidden_unit;

    static constexpr const std::size_t NV = desc::NV;
    static constexpr const std::size_t NH = desc::NH;
    static constexpr const std::size_t NC = desc::NC;
    static constexpr const std::size_t K = desc::K;

    static constexpr const std::size_t NW = NV - NH + 1; //By definition

    etl::fast_matrix<weight, NC, K, NW, NW> w;      //shared weights
    etl::fast_vector<weight, K> b;                  //hidden biases bk
    etl::fast_vector<weight, NC> c;                 //visible single bias c

    etl::fast_matrix<weight, NC, NV, NV> v1;        //visible units

    etl::fast_matrix<weight, K, NH, NH> h1_a;       //Activation probabilities of reconstructed hidden units
    etl::fast_matrix<weight, K, NH, NH> h1_s;       //Sampled values of reconstructed hidden units

    etl::fast_matrix<weight, NC, NV, NV> v2_a;      //Activation probabilities of reconstructed visible units
    etl::fast_matrix<weight, NC, NV, NV> v2_s;      //Sampled values of reconstructed visible units

    etl::fast_matrix<weight, K, NH, NH> h2_a;       //Activation probabilities of reconstructed hidden units
    etl::fast_matrix<weight, K, NH, NH> h2_s;       //Sampled values of reconstructed hidden units

    //Convolution data

    etl::fast_matrix<weight, NC+1, K, NH, NH> v_cv; //Temporary convolution
    etl::fast_matrix<weight, K+1, NV, NV> h_cv;     //Temporary convolution

    conv_rbm() : base_type() {
        //Initialize the weights with a zero-mean and unit variance Gaussian distribution
        w = 0.01 * etl::normal_generator();
        b = -0.1;
        c = 0.0;
    }

    static constexpr std::size_t input_size(){
        return NV * NV * NC;
    }

    static constexpr std::size_t output_size(){
        return NH * NH * K;
    }

    static std::string to_short_string(){
        char buffer[1024];
        snprintf(buffer, 1024, "CRBM: %lux%lux%lu -> (%lux%lu) -> %lux%lux%lu", NV, NV, NC, NW, NW, NH, NH, K);
        return {buffer};
    }

    void display() const {
        std::cout << to_short_string() << std::endl;
    }

    template<typename H1, typename H2, typename V1, typename V2>
    void activate_hidden(H1&& h_a, H2&& h_s, const V1& v_a, const V2& v_s){
        activate_hidden(std::forward<H1>(h_a), std::forward<H2>(h_s), v_a, v_s, v_cv);
    }

    template<typename H1, typename H2, typename V1, typename V2>
    void activate_visible(const H1& h_a, const H2& h_s, V1&& v_a, V2&& v_s){
        activate_visible(h_a, h_s, std::forward<V1>(v_a), std::forward<V2>(v_s), h_cv);
    }

    template<typename H1, typename H2, typename V1, typename V2, typename VCV>
    void activate_hidden(H1&& h_a, H2&& h_s, const V1& v_a, const V2&, VCV&& v_cv){
        using namespace etl;

        v_cv(NC) = 0;

        for(std::size_t channel = 0; channel < NC; ++channel){
            for(size_t k = 0; k < K; ++k){
                etl::convolve_2d_valid(v_a(channel), fflip(w(channel)(k)), v_cv(channel)(k));
            }

            v_cv(NC) += v_cv(channel);
        }

        if(hidden_unit == unit_type::BINARY){
            h_a = sigmoid(etl::rep<NH, NH>(b) + v_cv(NC));
            h_s = bernoulli(h_a);
        } else if(hidden_unit == unit_type::RELU){
            h_a = max(etl::rep<NH, NH>(b) + v_cv(NC), 0.0);
            h_s = logistic_noise(h_a);
        } else if(hidden_unit == unit_type::RELU6){
            h_a = min(max(etl::rep<NH, NH>(b) + v_cv(NC), 0.0), 6.0);
            h_s = ranged_noise(h_a, 6.0);
        } else if(hidden_unit == unit_type::RELU1){
            h_a = min(max(etl::rep<NH, NH>(b) + v_cv(NC), 0.0), 1.0);
            h_s = ranged_noise(h_a, 1.0);
        } else {
            cpp_unreachable("Invalid path");
        }

        nan_check_deep(h_a);
        nan_check_deep(h_s);
    }

    template<typename H1, typename H2, typename V1, typename V2, typename HCV>
    void activate_visible(const H1&, const H2& h_s, V1&& v_a, V2&& v_s, HCV&& h_cv){
        using namespace etl;

        for(std::size_t channel = 0; channel < NC; ++channel){
            h_cv(K) = 0.0;

            for(std::size_t k = 0; k < K; ++k){
                etl::convolve_2d_full(h_s(k), w(channel)(k), h_cv(k));
                h_cv(K) += h_cv(k);
            }

            if(visible_unit == unit_type::BINARY){
                v_a(channel) = sigmoid(c(channel) + h_cv(K));
                v_s(channel) = bernoulli(v_a(channel));
            } else if(visible_unit == unit_type::GAUSSIAN){
                v_a(channel) = c(channel) + h_cv(K);
                v_s(channel) = normal_noise(v_a(channel));
            } else {
                cpp_unreachable("Invalid path");
            }
        }

        nan_check_deep(v_a);
        nan_check_deep(v_s);
    }

    template<typename V, typename H, cpp::enable_if_u<etl::is_etl_expr<V>::value> = cpp::detail::dummy>
    weight energy(const V& v, const H& h){
        if(desc::visible_unit == unit_type::BINARY && desc::hidden_unit == unit_type::BINARY){
            //Definition according to Honglak Lee
            //E(v,h) = - sum_k hk . (Wk*v) - sum_k bk sum_h hk - c sum_v v

            v_cv(NC) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                for(size_t k = 0; k < K; ++k){
                    etl::convolve_2d_valid(v(channel), fflip(w(channel)(k)), v_cv(channel)(k));
                }

                v_cv(NC) += v_cv(channel);
            }

            return - etl::sum(c * etl::sum_r(v)) - etl::sum(b * etl::sum_r(h)) - etl::sum(h * v_cv(NC));
        } else if(desc::visible_unit == unit_type::GAUSSIAN && desc::hidden_unit == unit_type::BINARY){
            //Definition according to Honglak Lee / Mixed with Gaussian
            //E(v,h) = - sum_k hk . (Wk*v) - sum_k bk sum_h hk - sum_v ((v - c) ^ 2 / 2)

            v_cv(NC) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                for(size_t k = 0; k < K; ++k){
                    etl::convolve_2d_valid(v(channel), fflip(w(channel)(k)), v_cv(channel)(k));
                }

                v_cv(NC) += v_cv(channel);
            }

            return -sum(etl::pow(v - etl::rep<NV, NV>(c), 2) / 2.0) - etl::sum(b * etl::sum_r(h)) - etl::sum(h * v_cv(NC));
        } else {
            return 0.0;
        }
    }

    template<typename V, typename H, cpp::disable_if_u<etl::is_etl_expr<V>::value> = cpp::detail::dummy>
    weight energy(const V& v, const H& h){
        static etl::fast_matrix<weight, NC, NV, NV> ev;
        static etl::fast_matrix<weight, K, NH, NH> eh;

        ev = v;
        eh = h;

        return energy(ev, eh);
    }

    template<typename V>
    weight free_energy_impl(const V& v){
        if(desc::visible_unit == unit_type::BINARY && desc::hidden_unit == unit_type::BINARY){
            //Definition computed from E(v,h)

            v_cv(NC) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                for(size_t k = 0; k < K; ++k){
                    etl::convolve_2d_valid(v(channel), fflip(w(channel)(k)), v_cv(channel)(k));
                }

                v_cv(NC) += v_cv(channel);
            }

            auto x = etl::rep<NH, NH>(b) + v_cv(NC);

            return - etl::sum(c * etl::sum_r(v)) - etl::sum(etl::log(1.0 + etl::exp(x)));
        } else if(desc::visible_unit == unit_type::GAUSSIAN && desc::hidden_unit == unit_type::BINARY){
            //Definition computed from E(v,h)

            v_cv(NC) = 0;

            for(std::size_t channel = 0; channel < NC; ++channel){
                for(size_t k = 0; k < K; ++k){
                    etl::convolve_2d_valid(v(channel), fflip(w(channel)(k)), v_cv(channel)(k));
                }

                v_cv(NC) += v_cv(channel);
            }

            auto x = etl::rep<NH, NH>(b) + v_cv(NC);

            return -sum(etl::pow(v - etl::rep<NV, NV>(c), 2) / 2.0) - etl::sum(etl::log(1.0 + etl::exp(x)));
        } else {
            return 0.0;
        }
    }

    template<typename V>
    weight free_energy(const V& v){
        static etl::fast_matrix<weight, NC, NV, NV> ev;
        ev = v;
        return free_energy_impl(ev);
    }

    weight free_energy() const {
        return free_energy_impl(v1);
    }

    //Utilities for DBNs

    using input_one_t = etl::dyn_matrix<weight, 3>;
    using output_one_t = etl::dyn_matrix<weight, 3>;
    using input_t = std::vector<input_one_t>;
    using output_t = std::vector<output_one_t>;

    template<typename Iterator>
    static auto convert_input(Iterator&& first, Iterator&& last){
        input_t input;
        input.reserve(std::distance(std::forward<Iterator>(first), std::forward<Iterator>(last)));

        std::for_each(std::forward<Iterator>(first), std::forward<Iterator>(last), [&input](auto& sample){
            input.emplace_back(NC, NV, NV);
            input.back() = sample;
        });

        return input;
    }

    template<typename Sample>
    static input_one_t convert_sample(const Sample& sample){
        input_one_t result(NC, NV, NV);
        result = sample;
        return result;
    }

    void prepare_output(output_t& output, std::size_t samples){
        output.clear();
        output.reserve(samples);

        for(std::size_t i = 0; i < samples; ++i){
            output.emplace_back(K, NH, NH);
        }
    }

    static output_one_t prepare_one_output(){
        return output_one_t(K, NH, NH);
    }

    void activate_one(const input_one_t& input, output_one_t& h_a, output_one_t& h_s){
        v1 = input;
        activate_hidden(h_a, h_s, v1, v1);
    }

    void activate_many(const input_t& input, output_t& h_a, output_t& h_s){
        for(std::size_t i = 0; i < input.size(); ++i){
            activate_one(input[i], h_a[i], h_s[i]);
        }
    }
};

//Allow odr-use of the constexpr static members

template<typename Desc>
const std::size_t conv_rbm<Desc>::NV;

template<typename Desc>
const std::size_t conv_rbm<Desc>::NH;

template<typename Desc>
const std::size_t conv_rbm<Desc>::NC;

template<typename Desc>
const std::size_t conv_rbm<Desc>::NW;

template<typename Desc>
const std::size_t conv_rbm<Desc>::K;

} //end of dll namespace

#endif
<|endoftext|>


<commit_before><commit_msg>libTidyEngine: removed ambiguous constructor<commit_after><|endoftext|>