<commit_before>#include "client/keyboard/keytable.h"
#include "client/ui/event.hpp"
#include "client/ui/menu.hpp"
#include "client/ui/window.hpp"
#include "sys/path.hpp"
#include "sys/rand.hpp"
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")

class WinWindow : public UI::Window {
public:
    virtual void close();
};

void WinWindow::close()
{
    PostQuitMessage(0);
}

static HDC hDC;
static HGLRC hRC;
static HWND hWnd;
static HINSTANCE hInstance;

static bool inactive;

static WinWindow *gWindow;

static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);

static void errorBox(const char *str)
{
    MessageBox(NULL, str, "ERROR", MB_OK | MB_ICONEXCLAMATION);
}

static void serrorBox(const char *str)
{
    MessageBox(NULL, str, "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}

static void handleResize(int width, int height)
{
    if (!height)
        height = 1;
    if (!width)
        width = 1;
    glViewport(0, 0, width, height);
    gWindow->setSize(width, height);
}

static void killGLWindow()
{
    if (hRC) {
        if (!wglMakeCurrent(NULL, NULL))
            serrorBox("Release of DC and RC failed.");
        if (!wglDeleteContext(hRC))
            serrorBox("Release rendering context failed.");
    }
    hRC = NULL;

    if (hDC && !ReleaseDC(hWnd, hDC))
        serrorBox("Release device context failed.");
    hDC = NULL;

    if (hWnd && !DestroyWindow(hWnd))
        serrorBox("Could not release window.");
    hWnd = NULL;

    if (!UnregisterClass("OpenGL", hInstance))
        serrorBox("Could not unregister class");
    hInstance = NULL;
}

BOOL createWindow(const char *title, int width, int height)
{
    GLuint pixelFormat;
    WNDCLASS wc;
    DWORD dwExStyle;
    DWORD dwStyle;
    RECT windowRect;

    windowRect.left = 0;
    windowRect.right = width;
    windowRect.top = 0;
    windowRect.bottom = height;

    hInstance = GetModuleHandle(NULL);
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = (WNDPROC) wndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "OpenGL";

    if (!RegisterClass(&wc)) {
        errorBox("Failed to register window class.");
        return FALSE;
    }

    dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    dwStyle = WS_OVERLAPPEDWINDOW;
    dwStyle |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

    AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
    hWnd = CreateWindowEx(dwExStyle, "OpenGL", title, dwStyle, 0, 0,
                          windowRect.right - windowRect.left,
                          windowRect.bottom - windowRect.top,
                          NULL, NULL, hInstance, NULL);
    if (!hWnd) {
        killGLWindow();
        errorBox("Window creation error.");
        return FALSE;
    }

    static PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,
        32,
        0, 0, 0, 0, 0, 0, // color bits
        0, // alpha
        0, // shift
        0, 0, 0, 0, 0, // accumulation
        16, // depth buffer
        0, // stencil
        0, // auxiliary
        PFD_MAIN_PLANE, // drawing layer
        0, //reserved
        0, 0, 0 // layer masks
    };

    hDC = GetDC(hWnd);
    if (!hDC) {
        killGLWindow();
        errorBox("Can't create OpenGL device context.");
        return FALSE;
    }

    pixelFormat = ChoosePixelFormat(hDC, &pfd);
    if (!pixelFormat) {
        killGLWindow();
        errorBox("Can't find a suitable pixel format.");
        return FALSE;
    }

    if (!SetPixelFormat(hDC, pixelFormat, &pfd)) {
        killGLWindow();
        errorBox("Can't set pixel format.");
        return FALSE;
    }

    hRC = wglCreateContext(hDC);
    if (!hRC) {
        killGLWindow();
        errorBox("Can't create OpenGL rendering context.");
        return FALSE;
    }

    if (!wglMakeCurrent(hDC, hRC)) {
        killGLWindow();
        errorBox("Can't activate OpenGL rendering context.");
        return FALSE;
    }

    ShowWindow(hWnd, SW_SHOW);
    SetForegroundWindow(hWnd);
    SetFocus(hWnd);
    handleResize(width, height);

    return TRUE;
}

static void handleKey(int code, UI::EventType t)
{
    if (code < 0 || code > 255)
        return;
    int hcode = WIN_NATIVE_TO_HID[code];
    if (hcode == 255)
        return;
    UI::KeyEvent e(t, hcode);
    gWindow->handleEvent(e);
}

static void handleMouse(int param, UI::EventType t, int button)
{
    int x = LOWORD(param), y = HIWORD(param);
    UI::MouseEvent e(t, button, x, gWindow->height() - 1 - y);
    gWindow->handleEvent(e);
}

static LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
    case WM_ACTIVATE:
        inactive = HIWORD(wParam) != 0;
        return 0;

    case WM_SYSCOMMAND:
        switch (wParam) {
        case SC_SCREENSAVE:
        case SC_MONITORPOWER:
            return 0;
        }
        break;

    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;

    case WM_KEYDOWN:
        handleKey(wParam, UI::KeyDown);
        return 0;

    case WM_KEYUP:
        handleKey(wParam, UI::KeyUp);
        return 0;

    case WM_LBUTTONDOWN:
        handleMouse(lParam, UI::MouseDown, UI::ButtonLeft);
        return 0;

    case WM_RBUTTONDOWN:
        handleMouse(lParam, UI::MouseDown, UI::ButtonRight);
        return 0;

    case WM_MBUTTONDOWN:
        handleMouse(lParam, UI::MouseDown, UI::ButtonMiddle);
        return 0;

    case WM_LBUTTONUP:
        handleMouse(lParam, UI::MouseUp, UI::ButtonLeft);
        return 0;

    case WM_RBUTTONUP:
        handleMouse(lParam, UI::MouseUp, UI::ButtonRight);
        return 0;

    case WM_MBUTTONUP:
        handleMouse(lParam, UI::MouseUp, UI::ButtonMiddle);
        return 0;

    case WM_MOUSEMOVE:
        handleMouse(lParam, UI::MouseMove, -1);
        return 0;

    case WM_SIZE:
        handleResize(LOWORD(lParam), HIWORD(lParam));
        return 0;
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int iCmdShow)
{
    MSG msg;
    WinWindow w;
    gWindow = &w;

    Path::init();
    Rand::global.seed();
    w.setScreen(new UI::Menu);

    if (!createWindow("Game", 768, 480))
        return 0;

    while (1) {
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT)
                goto done;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if (!inactive) {
            w.draw();
            SwapBuffers(hDC);
        }
    }
done:

    killGLWindow();
    return 0;
}
<commit_msg>Convert Windows main to Unicode<commit_after>#define UNICODE

#include "client/keyboard/keytable.h"
#include "client/ui/event.hpp"
#include "client/ui/menu.hpp"
#include "client/ui/window.hpp"
#include "sys/path.hpp"
#include "sys/rand.hpp"
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")

class WinWindow : public UI::Window {
public:
    virtual void close();
};

void WinWindow::close()
{
    PostQuitMessage(0);
}

static HDC hDC;
static HGLRC hRC;
static HWND hWnd;
static HINSTANCE hInstance;

static bool inactive;

static WinWindow *gWindow;

static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);

static void errorBox(const char *str)
{
    MessageBoxA(NULL, str, "ERROR", MB_OK | MB_ICONEXCLAMATION);
}

static void serrorBox(const char *str)
{
    MessageBoxA(NULL, str, "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
}

static void handleResize(int width, int height)
{
    if (!height)
        height = 1;
    if (!width)
        width = 1;
    glViewport(0, 0, width, height);
    gWindow->setSize(width, height);
}

static void killGLWindow()
{
    if (hRC) {
        if (!wglMakeCurrent(NULL, NULL))
            serrorBox("Release of DC and RC failed.");
        if (!wglDeleteContext(hRC))
            serrorBox("Release rendering context failed.");
    }
    hRC = NULL;

    if (hDC && !ReleaseDC(hWnd, hDC))
        serrorBox("Release device context failed.");
    hDC = NULL;

    if (hWnd && !DestroyWindow(hWnd))
        serrorBox("Could not release window.");
    hWnd = NULL;

    if (!UnregisterClass(L"OpenGL", hInstance))
        serrorBox("Could not unregister class");
    hInstance = NULL;
}

BOOL createWindow(LPCWSTR title, int width, int height)
{
    GLuint pixelFormat;
    WNDCLASS wc;
    DWORD dwExStyle;
    DWORD dwStyle;
    RECT windowRect;

    windowRect.left = 0;
    windowRect.right = width;
    windowRect.top = 0;
    windowRect.bottom = height;

    hInstance = GetModuleHandle(NULL);
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = (WNDPROC) wndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"OpenGL";

    if (!RegisterClass(&wc)) {
        errorBox("Failed to register window class.");
        return FALSE;
    }

    dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    dwStyle = WS_OVERLAPPEDWINDOW;
    dwStyle |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

    AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);
    hWnd = CreateWindowEx(dwExStyle, L"OpenGL", title, dwStyle, 0, 0,
                          windowRect.right - windowRect.left,
                          windowRect.bottom - windowRect.top,
                          NULL, NULL, hInstance, NULL);
    if (!hWnd) {
        killGLWindow();
        errorBox("Window creation error.");
        return FALSE;
    }

    static PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,
        32,
        0, 0, 0, 0, 0, 0, // color bits
        0, // alpha
        0, // shift
        0, 0, 0, 0, 0, // accumulation
        16, // depth buffer
        0, // stencil
        0, // auxiliary
        PFD_MAIN_PLANE, // drawing layer
        0, //reserved
        0, 0, 0 // layer masks
    };

    hDC = GetDC(hWnd);
    if (!hDC) {
        killGLWindow();
        errorBox("Can't create OpenGL device context.");
        return FALSE;
    }

    pixelFormat = ChoosePixelFormat(hDC, &pfd);
    if (!pixelFormat) {
        killGLWindow();
        errorBox("Can't find a suitable pixel format.");
        return FALSE;
    }

    if (!SetPixelFormat(hDC, pixelFormat, &pfd)) {
        killGLWindow();
        errorBox("Can't set pixel format.");
        return FALSE;
    }

    hRC = wglCreateContext(hDC);
    if (!hRC) {
        killGLWindow();
        errorBox("Can't create OpenGL rendering context.");
        return FALSE;
    }

    if (!wglMakeCurrent(hDC, hRC)) {
        killGLWindow();
        errorBox("Can't activate OpenGL rendering context.");
        return FALSE;
    }

    ShowWindow(hWnd, SW_SHOW);
    SetForegroundWindow(hWnd);
    SetFocus(hWnd);
    handleResize(width, height);

    return TRUE;
}

static void handleKey(int code, UI::EventType t)
{
    if (code < 0 || code > 255)
        return;
    int hcode = WIN_NATIVE_TO_HID[code];
    if (hcode == 255)
        return;
    UI::KeyEvent e(t, hcode);
    gWindow->handleEvent(e);
}

static void handleMouse(int param, UI::EventType t, int button)
{
    int x = LOWORD(param), y = HIWORD(param);
    UI::MouseEvent e(t, button, x, gWindow->height() - 1 - y);
    gWindow->handleEvent(e);
}

static LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg) {
    case WM_ACTIVATE:
        inactive = HIWORD(wParam) != 0;
        return 0;

    case WM_SYSCOMMAND:
        switch (wParam) {
        case SC_SCREENSAVE:
        case SC_MONITORPOWER:
            return 0;
        }
        break;

    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;

    case WM_KEYDOWN:
        handleKey(wParam, UI::KeyDown);
        return 0;

    case WM_KEYUP:
        handleKey(wParam, UI::KeyUp);
        return 0;

    case WM_LBUTTONDOWN:
        handleMouse(lParam, UI::MouseDown, UI::ButtonLeft);
        return 0;

    case WM_RBUTTONDOWN:
        handleMouse(lParam, UI::MouseDown, UI::ButtonRight);
        return 0;

    case WM_MBUTTONDOWN:
        handleMouse(lParam, UI::MouseDown, UI::ButtonMiddle);
        return 0;

    case WM_LBUTTONUP:
        handleMouse(lParam, UI::MouseUp, UI::ButtonLeft);
        return 0;

    case WM_RBUTTONUP:
        handleMouse(lParam, UI::MouseUp, UI::ButtonRight);
        return 0;

    case WM_MBUTTONUP:
        handleMouse(lParam, UI::MouseUp, UI::ButtonMiddle);
        return 0;

    case WM_MOUSEMOVE:
        handleMouse(lParam, UI::MouseMove, -1);
        return 0;

    case WM_SIZE:
        handleResize(LOWORD(lParam), HIWORD(lParam));
        return 0;
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int iCmdShow)
{
    MSG msg;
    WinWindow w;
    gWindow = &w;

    Path::init();
    Rand::global.seed();
    w.setScreen(new UI::Menu);

    if (!createWindow(L"Game", 768, 480))
        return 0;

    while (1) {
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT)
                goto done;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if (!inactive) {
            w.draw();
            SwapBuffers(hDC);
        }
    }
done:

    killGLWindow();
    return 0;
}
<|endoftext|>


<commit_before>#include "rubymotion.h"
#include <dlfcn.h>

/// @class Scene < Node
/// This class represents a scene, an independent screen or stage of the
/// application workflow. A scene is responsible for handling events from the
/// device, providing a physics world for the sprites, and also starting the
/// game loop.
/// An application must have at least one scene, and the +Scene+ class is
/// designed to be subclassed.

VALUE rb_cScene = Qnil;

#if CC_TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
extern "C" {
    void rb_repl_new(VALUE);
}
#endif

class mc_Scene : public cocos2d::LayerColor {
    public:
	cocos2d::Scene *scene;
	VALUE obj;
	SEL update_sel;

    mc_Scene() {
	obj = Qnil;
#if CC_TARGET_OS_IPHONE || CC_TARGET_OS_APPLETV
	update_sel = rb_selector("update:");
#else
	update_sel = rb_selector("update");
#endif
    }

    static mc_Scene *create(void) {
	auto scene = new mc_Scene();
	scene->initWithColor(cocos2d::Color4B::BLACK);
	return scene;
    }

    virtual void update(float delta) {
	LayerColor::update(delta);
	VALUE arg = DBL2NUM(delta);
	rb_send(obj, update_sel, 1, &arg);
    }

    void setBackgroundColor(cocos2d::Color3B color) {
	setColor(color);
	updateColor();
    }

#if CC_TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
    virtual void onEnter() {
	cocos2d::LayerColor::onEnter();
	rb_repl_new(this->obj);
    }
#endif
};

#define SCENE(obj) _COCOS_WRAP_GET(obj, mc_Scene)

extern "C"
cocos2d::Scene *
rb_any_to_scene(VALUE obj)
{
    if (rb_obj_is_kind_of(obj, rb_cScene)) {
	return SCENE(obj)->scene;
    }
    rb_raise(rb_eArgError, "expected Scene object");
}

static VALUE
scene_alloc(VALUE rcv, SEL sel)
{
    auto layer = mc_Scene::create();

    auto scene = cocos2d::Scene::createWithPhysics();
    scene->addChild(layer);
    layer->scene = scene;

    VALUE obj = rb_class_wrap_new((void *)layer, rcv);
    layer->obj = rb_retain(obj);
    return obj;
}

/// @group Constructors

/// @method #initialize
/// The default initializer. Subclasses can construct the scene interface in
/// this method, as well as providing an implementation for {#update}, then
/// run the update loop by calling {#start_update}.
/// @return [Scene] the receiver.

static VALUE
scene_initialize(VALUE rcv, SEL sel)
{
    return rcv;
}

/// @group Update Loop

/// @method #start_update
/// Starts the update loop. The +#update+ method will be called on this object
/// for every frame.
/// @return [Scene] the receiver.

static VALUE
scene_start_update(VALUE rcv, SEL sel)
{
    SCENE(rcv)->scheduleUpdate();
    return rcv;
}

/// @method #stop_update
/// Stops the update loop. The +#update+ method will no longer be called on
/// this object.
/// @return [Scene] the receiver.

static VALUE
scene_stop_update(VALUE rcv, SEL sel)
{
    SCENE(rcv)->unscheduleUpdate();
    return rcv;
}

/// @method #update(delta)
/// The update loop method. Subclasses can provide a custom implementation of
/// this method. The default implementation is empty.
/// @param delta [Float] a value representing the amount of time, in seconds,
///   since the last time this method was called.
/// @return [Scene] the receiver.

static VALUE
scene_update(VALUE rcv, SEL sel, VALUE delta)
{
    // Do nothing.
    return rcv;
}

/// @method #schedule(delay, repeat=0, interval=0)
/// Schedules a given block for execution.
/// @param delay [Float] the duration of the block, in seconds.
/// @param repeat [Integer] the number of times the block should be repeated.
/// @param interval [Float] the interval between repetitions, in seconds.
/// @yield [Float] the given block will be yield with the delta value,
///   in seconds.
/// @return [String] a token representing the task that can be passed to
///   {#unschedule} when needed.

static VALUE
scene_schedule(VALUE rcv, SEL sel, int argc, VALUE *argv)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    VALUE interval = Qnil, repeat = Qnil, delay = Qnil;
    rb_scan_args(argc, argv, "12", &delay, &repeat, &interval);

    float interval_c  = RTEST(interval) ? NUM2DBL(interval) : 0;
    unsigned int repeat_c = RTEST(repeat) ? NUM2LONG(repeat) : 0;
    float delay_c = NUM2DBL(delay);
    char key[100];
    snprintf(key, sizeof key, "schedule_lambda_%p", (void *)block);

    SCENE(rcv)->schedule(
	    [block](float delta) {
		VALUE delta_obj = DBL2NUM(delta);
		rb_block_call(block, 1, &delta_obj);
	    },
	    interval_c, repeat_c, delay_c, key);

    return rb_str_new2(key);
}

/// @method #unschedule(key)
/// Unschedules a task that's currently running.
/// @param key [String] a token representing the task to unschedule,
///   returned by {#schedule}.
/// @return [Scene] the receiver.

static VALUE
scene_unschedule(VALUE rcv, SEL sel, VALUE key)
{
    SCENE(rcv)->unschedule(RSTRING_PTR(key));
    return rcv;
}

/// @group Events

static VALUE
scene_add_listener(VALUE rcv, cocos2d::EventListener *listener)
{
    auto scene = SCENE(rcv);
    scene->getEventDispatcher()->addEventListenerWithSceneGraphPriority(
	    listener, scene);
    return rcv;
}

/// @method #on_touch_begin
/// Starts listening for touch begin events on the receiver.
/// @yield [Events::Touch] the given block will be yield when a touch begin
///   event is received.
/// @return [Scene] the receiver.

static VALUE
scene_on_touch_begin(VALUE rcv, SEL sel)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    auto listener = cocos2d::EventListenerTouchOneByOne::create();
    listener->onTouchBegan = [block](cocos2d::Touch *touch,
		cocos2d::Event *event) -> bool {
	VALUE touch_obj = rb_class_wrap_new((void *)touch,
		rb_cTouch);
	return RTEST(rb_block_call(block, 1, &touch_obj));
    };

    return scene_add_listener(rcv, listener);
}

/// @method #on_accelerate
/// Starts listening for accelerometer events on the receiver.
/// @yield [Events::Acceleration] the given block will be yield when an
///   accelerometer event is received from the device.
/// @return [Scene] the receiver.

static VALUE
scene_on_accelerate(VALUE rcv, SEL sel)
{
#if CC_TARGET_OS_APPLETV
    rb_raise(rb_eRuntimeError, "Not supported in tvOS");
#else
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    cocos2d::Device::setAccelerometerEnabled(true);
    auto listener = cocos2d::EventListenerAcceleration::create(
	    [block](cocos2d::Acceleration *acc, cocos2d::Event *event) {
	    VALUE acc_obj = rb_class_wrap_new((void *)acc,
		    rb_cAcceleration);
	    rb_block_call(block, 1, &acc_obj);
	});

    return scene_add_listener(rcv, listener);
#endif
}

/// @method #on_contact_begin
/// Starts listening for contact begin events from the physics engine.
/// @yield [Events::PhysicsContact] the given block will be yield when a
///   contact event is received from the physics engine.
/// @return [Scene] the receiver.

static VALUE
scene_on_contact_begin(VALUE rcv, SEL sel)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    auto listener = cocos2d::EventListenerPhysicsContact::create();
    listener->onContactBegin = [block](cocos2d::PhysicsContact &contact)
	    -> bool {
//	VALUE touch_obj = rb_class_wrap_new((void *)touch,
//		rb_cTouch);
	return RTEST(rb_block_call(block, 0, NULL));
    };

    return scene_add_listener(rcv, listener);
}

/// @group Properties

/// @property #gravity
/// @return [Point] the gravity of the scene's physics world.

static VALUE
scene_gravity(VALUE rcv, SEL sel)
{
    return rb_ccvec2_to_obj(SCENE(rcv)->scene->getPhysicsWorld()->getGravity());
}

static VALUE
scene_gravity_set(VALUE rcv, SEL sel, VALUE arg)
{
    SCENE(rcv)->scene->getPhysicsWorld()->setGravity(rb_any_to_ccvec2(arg));
    return rcv;
}

/// @property #debug_physics?
/// @return [Boolean] whether the physics engine should draw debug lines.

static VALUE
scene_debug_physics(VALUE rcv, SEL sel)
{
    return SCENE(rcv)->scene->getPhysicsWorld()->getDebugDrawMask()
	== cocos2d::PhysicsWorld::DEBUGDRAW_NONE ? Qfalse : Qtrue;
}

static VALUE
scene_debug_physics_set(VALUE rcv, SEL sel, VALUE arg)
{
    SCENE(rcv)->scene->getPhysicsWorld()->setDebugDrawMask(RTEST(arg)
	    ? cocos2d::PhysicsWorld::DEBUGDRAW_ALL
	    : cocos2d::PhysicsWorld::DEBUGDRAW_NONE);
    return arg;
}

static VALUE
scene_color_set(VALUE rcv, SEL sel, VALUE val)
{
    SCENE(rcv)->setBackgroundColor(rb_any_to_cccolor3(val));
    return val;
}

extern "C"
void
Init_Layer(void)
{
    rb_cScene = rb_define_class_under(rb_mMC, "Scene", rb_cNode);

    rb_define_singleton_method(rb_cScene, "alloc", scene_alloc, 0);
    rb_define_method(rb_cScene, "initialize", scene_initialize, 0);
    rb_define_method(rb_cScene, "start_update", scene_start_update, 0);
    rb_define_method(rb_cScene, "stop_update", scene_stop_update, 0);
    rb_define_method(rb_cScene, "update", scene_update, 1);
    rb_define_method(rb_cScene, "schedule", scene_schedule, -1);
    rb_define_method(rb_cScene, "unschedule", scene_unschedule, 1);
    rb_define_method(rb_cScene, "on_touch_begin", scene_on_touch_begin, 0);
    rb_define_method(rb_cScene, "on_accelerate", scene_on_accelerate, 0);
    rb_define_method(rb_cScene, "on_contact_begin", scene_on_contact_begin, 0);
    rb_define_method(rb_cScene, "gravity", scene_gravity, 0);
    rb_define_method(rb_cScene, "gravity=", scene_gravity_set, 1);
    rb_define_method(rb_cScene, "debug_physics?", scene_debug_physics, 0);
    rb_define_method(rb_cScene, "debug_physics=", scene_debug_physics_set, 1);
    rb_define_method(rb_cScene, "color=", scene_color_set, 1);
}
<commit_msg>replace with macro<commit_after>#include "rubymotion.h"
#include <dlfcn.h>

/// @class Scene < Node
/// This class represents a scene, an independent screen or stage of the
/// application workflow. A scene is responsible for handling events from the
/// device, providing a physics world for the sprites, and also starting the
/// game loop.
/// An application must have at least one scene, and the +Scene+ class is
/// designed to be subclassed.

VALUE rb_cScene = Qnil;

#if CC_TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
extern "C" {
    void rb_repl_new(VALUE);
}
#endif

class mc_Scene : public cocos2d::LayerColor {
    public:
	cocos2d::Scene *scene;
	VALUE obj;
	SEL update_sel;

    mc_Scene() {
	obj = Qnil;
#if CC_TARGET_OS_IPHONE || CC_TARGET_OS_APPLETV
	update_sel = rb_selector("update:");
#else
	update_sel = rb_selector("update");
#endif
    }

    static mc_Scene *create(void) {
	auto scene = new mc_Scene();
	scene->initWithColor(cocos2d::Color4B::BLACK);
	return scene;
    }

    virtual void update(float delta) {
	LayerColor::update(delta);
	VALUE arg = DBL2NUM(delta);
	rb_send(obj, update_sel, 1, &arg);
    }

    void setBackgroundColor(cocos2d::Color3B color) {
	setColor(color);
	updateColor();
    }

#if CC_TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR
    virtual void onEnter() {
	cocos2d::LayerColor::onEnter();
	rb_repl_new(this->obj);
    }
#endif
};

#define SCENE(obj) _COCOS_WRAP_GET(obj, mc_Scene)

extern "C"
cocos2d::Scene *
rb_any_to_scene(VALUE obj)
{
    if (rb_obj_is_kind_of(obj, rb_cScene)) {
	return SCENE(obj)->scene;
    }
    rb_raise(rb_eArgError, "expected Scene object");
}

static VALUE
scene_alloc(VALUE rcv, SEL sel)
{
    auto layer = mc_Scene::create();

    auto scene = cocos2d::Scene::createWithPhysics();
    scene->addChild(layer);
    layer->scene = scene;

    VALUE obj = rb_class_wrap_new((void *)layer, rcv);
    layer->obj = rb_retain(obj);
    return obj;
}

/// @group Constructors

/// @method #initialize
/// The default initializer. Subclasses can construct the scene interface in
/// this method, as well as providing an implementation for {#update}, then
/// run the update loop by calling {#start_update}.
/// @return [Scene] the receiver.

static VALUE
scene_initialize(VALUE rcv, SEL sel)
{
    return rcv;
}

/// @group Update Loop

/// @method #start_update
/// Starts the update loop. The +#update+ method will be called on this object
/// for every frame.
/// @return [Scene] the receiver.

static VALUE
scene_start_update(VALUE rcv, SEL sel)
{
    SCENE(rcv)->scheduleUpdate();
    return rcv;
}

/// @method #stop_update
/// Stops the update loop. The +#update+ method will no longer be called on
/// this object.
/// @return [Scene] the receiver.

static VALUE
scene_stop_update(VALUE rcv, SEL sel)
{
    SCENE(rcv)->unscheduleUpdate();
    return rcv;
}

/// @method #update(delta)
/// The update loop method. Subclasses can provide a custom implementation of
/// this method. The default implementation is empty.
/// @param delta [Float] a value representing the amount of time, in seconds,
///   since the last time this method was called.
/// @return [Scene] the receiver.

static VALUE
scene_update(VALUE rcv, SEL sel, VALUE delta)
{
    // Do nothing.
    return rcv;
}

/// @method #schedule(delay, repeat=0, interval=0)
/// Schedules a given block for execution.
/// @param delay [Float] the duration of the block, in seconds.
/// @param repeat [Integer] the number of times the block should be repeated.
/// @param interval [Float] the interval between repetitions, in seconds.
/// @yield [Float] the given block will be yield with the delta value,
///   in seconds.
/// @return [String] a token representing the task that can be passed to
///   {#unschedule} when needed.

static VALUE
scene_schedule(VALUE rcv, SEL sel, int argc, VALUE *argv)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    VALUE interval = Qnil, repeat = Qnil, delay = Qnil;
    rb_scan_args(argc, argv, "12", &delay, &repeat, &interval);

    float interval_c  = RTEST(interval) ? NUM2DBL(interval) : 0;
    unsigned int repeat_c = RTEST(repeat) ? NUM2LONG(repeat) : 0;
    float delay_c = NUM2DBL(delay);
    char key[100];
    snprintf(key, sizeof key, "schedule_lambda_%p", (void *)block);

    SCENE(rcv)->schedule(
	    [block](float delta) {
		VALUE delta_obj = DBL2NUM(delta);
		rb_block_call(block, 1, &delta_obj);
	    },
	    interval_c, repeat_c, delay_c, key);

    return RSTRING_NEW(key);
}

/// @method #unschedule(key)
/// Unschedules a task that's currently running.
/// @param key [String] a token representing the task to unschedule,
///   returned by {#schedule}.
/// @return [Scene] the receiver.

static VALUE
scene_unschedule(VALUE rcv, SEL sel, VALUE key)
{
    SCENE(rcv)->unschedule(RSTRING_PTR(key));
    return rcv;
}

/// @group Events

static VALUE
scene_add_listener(VALUE rcv, cocos2d::EventListener *listener)
{
    auto scene = SCENE(rcv);
    scene->getEventDispatcher()->addEventListenerWithSceneGraphPriority(
	    listener, scene);
    return rcv;
}

/// @method #on_touch_begin
/// Starts listening for touch begin events on the receiver.
/// @yield [Events::Touch] the given block will be yield when a touch begin
///   event is received.
/// @return [Scene] the receiver.

static VALUE
scene_on_touch_begin(VALUE rcv, SEL sel)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    auto listener = cocos2d::EventListenerTouchOneByOne::create();
    listener->onTouchBegan = [block](cocos2d::Touch *touch,
		cocos2d::Event *event) -> bool {
	VALUE touch_obj = rb_class_wrap_new((void *)touch,
		rb_cTouch);
	return RTEST(rb_block_call(block, 1, &touch_obj));
    };

    return scene_add_listener(rcv, listener);
}

/// @method #on_accelerate
/// Starts listening for accelerometer events on the receiver.
/// @yield [Events::Acceleration] the given block will be yield when an
///   accelerometer event is received from the device.
/// @return [Scene] the receiver.

static VALUE
scene_on_accelerate(VALUE rcv, SEL sel)
{
#if CC_TARGET_OS_APPLETV
    rb_raise(rb_eRuntimeError, "Not supported in tvOS");
#else
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    cocos2d::Device::setAccelerometerEnabled(true);
    auto listener = cocos2d::EventListenerAcceleration::create(
	    [block](cocos2d::Acceleration *acc, cocos2d::Event *event) {
	    VALUE acc_obj = rb_class_wrap_new((void *)acc,
		    rb_cAcceleration);
	    rb_block_call(block, 1, &acc_obj);
	});

    return scene_add_listener(rcv, listener);
#endif
}

/// @method #on_contact_begin
/// Starts listening for contact begin events from the physics engine.
/// @yield [Events::PhysicsContact] the given block will be yield when a
///   contact event is received from the physics engine.
/// @return [Scene] the receiver.

static VALUE
scene_on_contact_begin(VALUE rcv, SEL sel)
{
    VALUE block = rb_current_block();
    if (block == Qnil) {
	rb_raise(rb_eArgError, "block not given");
    }
    block = rb_retain(block); // FIXME need release...

    auto listener = cocos2d::EventListenerPhysicsContact::create();
    listener->onContactBegin = [block](cocos2d::PhysicsContact &contact)
	    -> bool {
//	VALUE touch_obj = rb_class_wrap_new((void *)touch,
//		rb_cTouch);
	return RTEST(rb_block_call(block, 0, NULL));
    };

    return scene_add_listener(rcv, listener);
}

/// @group Properties

/// @property #gravity
/// @return [Point] the gravity of the scene's physics world.

static VALUE
scene_gravity(VALUE rcv, SEL sel)
{
    return rb_ccvec2_to_obj(SCENE(rcv)->scene->getPhysicsWorld()->getGravity());
}

static VALUE
scene_gravity_set(VALUE rcv, SEL sel, VALUE arg)
{
    SCENE(rcv)->scene->getPhysicsWorld()->setGravity(rb_any_to_ccvec2(arg));
    return rcv;
}

/// @property #debug_physics?
/// @return [Boolean] whether the physics engine should draw debug lines.

static VALUE
scene_debug_physics(VALUE rcv, SEL sel)
{
    return SCENE(rcv)->scene->getPhysicsWorld()->getDebugDrawMask()
	== cocos2d::PhysicsWorld::DEBUGDRAW_NONE ? Qfalse : Qtrue;
}

static VALUE
scene_debug_physics_set(VALUE rcv, SEL sel, VALUE arg)
{
    SCENE(rcv)->scene->getPhysicsWorld()->setDebugDrawMask(RTEST(arg)
	    ? cocos2d::PhysicsWorld::DEBUGDRAW_ALL
	    : cocos2d::PhysicsWorld::DEBUGDRAW_NONE);
    return arg;
}

static VALUE
scene_color_set(VALUE rcv, SEL sel, VALUE val)
{
    SCENE(rcv)->setBackgroundColor(rb_any_to_cccolor3(val));
    return val;
}

extern "C"
void
Init_Layer(void)
{
    rb_cScene = rb_define_class_under(rb_mMC, "Scene", rb_cNode);

    rb_define_singleton_method(rb_cScene, "alloc", scene_alloc, 0);
    rb_define_method(rb_cScene, "initialize", scene_initialize, 0);
    rb_define_method(rb_cScene, "start_update", scene_start_update, 0);
    rb_define_method(rb_cScene, "stop_update", scene_stop_update, 0);
    rb_define_method(rb_cScene, "update", scene_update, 1);
    rb_define_method(rb_cScene, "schedule", scene_schedule, -1);
    rb_define_method(rb_cScene, "unschedule", scene_unschedule, 1);
    rb_define_method(rb_cScene, "on_touch_begin", scene_on_touch_begin, 0);
    rb_define_method(rb_cScene, "on_accelerate", scene_on_accelerate, 0);
    rb_define_method(rb_cScene, "on_contact_begin", scene_on_contact_begin, 0);
    rb_define_method(rb_cScene, "gravity", scene_gravity, 0);
    rb_define_method(rb_cScene, "gravity=", scene_gravity_set, 1);
    rb_define_method(rb_cScene, "debug_physics?", scene_debug_physics, 0);
    rb_define_method(rb_cScene, "debug_physics=", scene_debug_physics_set, 1);
    rb_define_method(rb_cScene, "color=", scene_color_set, 1);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2011, Paul Tagliamonte
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <vector>
#include <fstream>
#include <iostream>
#include <dirent.h>

#include <string.h>

#include "machine.hh"
#include "xdg_model.hh"
#include "exceptions.hh"
#include "xdg_autostart.hh"

/**
 * XXX: Document me
 */
void parse_file ( std::string file ) {
	std::ifstream xdg_file(file.c_str(), std::ios::in | std::ios::binary);

	if ( ! xdg_file ) {
		std::cerr << "Crapola file." << std::endl;
		// XXX: Raise exception
	}

	xdg_machine_next_state = &xdg_entry;
	xdg_machine_turnkey(); // Vvvrooom!

	char c;

	int row = 1;
	int col = 0; // some cosmetics

	do {
		xdg_file.read(&c, 1);
		col++;

		if ( c == '\n' ) { col = 0; row++; }

		try {
			xdg_machine_process( c );
		} catch ( parser_exception * fu ) {
			parser_exception ex(row, col);
			std::cerr << "Error parsing: R/C: " << row << ", " << col << std::endl;
			throw &ex; // XXX: Fix this fucking hack
		}
	} while ( ! xdg_file.eof() );
	/* In the event that they don't newline the end of the
	   file */
	xdg_machine_process( '\n' );
}

/**
 * XXX: Document me
 */
void parse_folder ( xdg_autostart_map * binaries, std::string folder ) {
	DIR * dir;
	struct dirent * ent;
        dir = opendir (folder.c_str());

	if (dir != NULL) {
		/* print all the files and directories within directory */
		while ((ent = readdir (dir)) != NULL) {
			if (
				strcmp (ent->d_name, ".")  != 0 &&
				strcmp (ent->d_name, "..") != 0
			) {
				// std::cout << "Loading: " << folder + "/" + ent->d_name << " ";
				try {
					parse_file(folder + "/" + ent->d_name);
					xdg_autostart_pair r = xdg_autostart_last_parsed();
					xdg_parsed_file.clear();
					binaries->insert(xdg_autostart_pair(ent->d_name, r.second));
				} catch ( parser_exception * ex ) {
					std::cerr << "Exception parsing " << ent->d_name << std::endl;
					std::cerr << " in " << folder << std::endl;
				}
			}
		}
		closedir (dir);
	} else {
		// XXX: Exceptionize this
	}
}

<commit_msg>Commit from the Acetarium<commit_after>/*
 * Copyright (C) 2011, Paul Tagliamonte
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <vector>
#include <fstream>
#include <iostream>
#include <dirent.h>

#include <string.h>

#include "machine.hh"
#include "xdg_model.hh"
#include "exceptions.hh"
#include "xdg_autostart.hh"

/**
 * XXX: Document me
 */
void parse_file ( std::string file ) {
	std::ifstream fd(file.c_str(), std::ios::in | std::ios::binary);

	if ( ! fd ) {
		std::cerr << "Crapola file." << std::endl;
		// XXX: Raise exception
	}

	xdg_machine_next_state = &xdg_entry;
	xdg_machine_turnkey(); // Vvvrooom!

	char c;

	int row = 1;
	int col = 0; // some cosmetics

	do {
		fd.read(&c, 1);
		col++;

		if ( c == '\n' ) { col = 0; row++; }

		try {
			xdg_machine_process( c );
		} catch ( parser_exception * fu ) {
			parser_exception ex(row, col);
			std::cerr << "Error parsing: R/C: " << row << ", " << col << std::endl;
			throw &ex; // XXX: Fix this fucking hack
		}
	} while ( ! fd.eof() );
	/* In the event that they don't newline the end of the
	   file */
	xdg_machine_process( '\n' );
}

/**
 * XXX: Document me
 */
void parse_folder ( xdg_autostart_map * binaries, std::string folder ) {
	DIR * dir;
	struct dirent * ent;
        dir = opendir (folder.c_str());

	if (dir != NULL) {
		/* print all the files and directories within directory */
		while ((ent = readdir (dir)) != NULL) {
			if (
				strcmp (ent->d_name, ".")  != 0 &&
				strcmp (ent->d_name, "..") != 0
			) {
				// std::cout << "Loading: " << folder + "/" + ent->d_name << " ";
				try {
					parse_file(folder + "/" + ent->d_name);
					xdg_autostart_pair r = xdg_autostart_last_parsed();
					xdg_parsed_file.clear();
					binaries->insert(xdg_autostart_pair(ent->d_name, r.second));
				} catch ( parser_exception * ex ) {
					std::cerr << "Exception parsing " << ent->d_name << std::endl;
					std::cerr << " in " << folder << std::endl;
				}
			}
		}
		closedir (dir);
	} else {
		// XXX: Exceptionize this
	}
}

<|endoftext|>


<commit_before>#include <stdexcept>
#include <iostream>
#include <string.h>

#include <algorithm>
#include <thread>

#include <libshm.hpp>

namespace shm{
  template <key_t KEY, typename T>
  int Shm<KEY, T>::shmid_ = -1;

  template <key_t KEY, typename T>
  Shm<KEY, T>::Shm():shm_(0){
    get();
    attach();
  }

  template <key_t KEY, typename T>
  Shm<KEY, T>::~Shm(){
    if(shm_ != nullptr){
      shmdt(shm_);
      shm_ = 0;
    }
  }

  template <key_t KEY, typename T>
  void Shm<KEY, T>::setElement(const T *data){

    std::lock_guard<std::mutex> lock(shm_mutex);
    std::copy(data, data+sizeof(T),shm_);

  }

  template <key_t KEY, typename T>
  const T* Shm<KEY, T>::getElement(){

    std::lock_guard<std::mutex> lock(shm_mutex);

    T *ptr = new T;
    T *tmp = (new(shm_) T);
    std::copy(tmp, tmp + sizeof(T), ptr);

    return ptr;
  }

  template <key_t KEY, typename T>
  void Shm<KEY, T>::attach(){
    if ((shm_ = shmat(shmid_, NULL, 0)) == (char *) -1){
      throw std::runtime_error("Failed attach shm");
    }

  }
}
<commit_msg>not important<commit_after>#include <stdexcept>
#include <iostream>
#include <string.h>

#include <algorithm>
#include <thread>

#include <libshm.hpp>

namespace shm{
  template <key_t KEY, typename T>
  int Shm<KEY, T>::shmid_ = -1;

  template <key_t KEY, typename T>
  Shm<KEY, T>::Shm():shm_(0){
    get();
    attach();
  }

  template <key_t KEY, typename T>
  Shm<KEY, T>::~Shm(){
    if(shm_ != nullptr){
      shmdt(shm_);
      shm_ = 0;
    }
  }

  template <key_t KEY, typename T>
  void Shm<KEY, T>::setElement(const T *data){

    std::lock_guard<std::mutex> lock(shm_mutex);
    std::copy(data, data+sizeof(T),shm_);

  }

  template <key_t KEY, typename T>
  const T* Shm<KEY, T>::getElement(){

    std::lock_guard<std::mutex> lock(shm_mutex);
    T *ptr = new T;
    T *tmp = (new(shm_) T);
    std::copy(tmp, tmp + sizeof(T), ptr);

    return ptr;
  }

  template <key_t KEY, typename T>
  void Shm<KEY, T>::attach(){
    if ((shm_ = shmat(shmid_, NULL, 0)) == (char *) -1){
      throw std::runtime_error("Failed attach shm");
    }

  }
}
<|endoftext|>


<commit_before>#include "../vendor/gc/include/gc.h"
#include "../vendor/gc/include/gc_cpp.h"
#include "../vendor/gc/include/gc_allocator.h"

#include <sstream>

#include "method.h"
#include "block.h"
#include "utils.h"
#include "bootstrap/core_classes.h"
#include "parser/nodes/return.h"

namespace fancy {

  Method::Method(Identifier* op_name, Identifier* op_argname, ExpressionList* body) :
    FancyObject(MethodClass),
    _body(body),
    _is_operator(true),
    _body_block(NULL)
  {
    _argnames.push_back(pair<Identifier*, Identifier*>(op_name, op_argname));
    init_method_ident();
    init_docstring();
    _body->set_enclosing_method(this);
  }

  Method::Method(const list< pair<Identifier*, Identifier*> > argnames,
                 ExpressionList* body) :
    FancyObject(MethodClass),
    _argnames(argnames),
    _body(body),
    _is_operator(false),
    _body_block(NULL)
  {
    init_method_ident();
    init_docstring();
    _body->set_enclosing_method(this);
  }

  Method::Method() :
    FancyObject(MethodClass),
    _is_operator(false),
    _body_block(NULL)
  {
  }

  Method::Method(Block* body) :
    FancyObject(MethodClass),
    _body(NULL),
    _is_operator(false),
    _body_block(body)
  {
    _body_block->set_enclosing_method(this);
  }

  FancyObject* Method::call(FancyObject* self, FancyObject* *args, int argc, Scope *scope, FancyObject* sender)
  {
    Callable::check_sender_access(_method_ident, self, sender, scope);
    if(_body_block) {
      try {
        return _body_block->call(self, args, argc, scope, sender);
      } catch(return_value& rv) {
        if(rv.enclosing_method == this) {
          return rv.return_value;
        } else {
          throw rv;
        }
      }
    } else {
      // check if method is empty
      if(_body->size() == 0)
        return nil;

      Scope* call_scope = new Scope(self, scope);

      // check amount of given arguments
      if(_argnames.size() != (unsigned int)argc) {
        error("Given amount of arguments (")
          << argc
          << ") doesn't match expected amount ("
          << _argnames.size()
          << ")";
      } else {
        // if amount ok, set the parameters to the given arguments
        list< pair<Identifier*, Identifier*> >::iterator name_it = _argnames.begin();
        // list<FancyObject*>::iterator arg_it = args.begin();
        int i = 0;

        while(name_it != _argnames.end() && i < argc) {
          // name_it->second holds the name of the actual param name
          // (the first is part of the method name)
          call_scope->define(name_it->second->name(), args[i]);
          name_it++;
          i++;
        }

        // finally, eval the methods body expression
        FancyObject* val;
        try {
          val = _body->eval(call_scope);
        } catch(return_value& rv) {
          if(rv.enclosing_method == this) {
            return rv.return_value;
          } else {
            if(!call_scope->is_closed()) {
              delete call_scope;
              call_scope = NULL;
            }
            throw rv;
          }
        }

        if(!call_scope->is_closed()) {
          delete call_scope;
          call_scope = NULL;
        }
        return val;
      }

      return nil;
    }
  }

  FancyObject* Method::call(FancyObject* self, Scope *scope, FancyObject* sender)
  {
    Callable::check_sender_access(_method_ident, self, sender, scope);

    // check if method is empty
    if(_body->size() == 0)
      return nil;

    Scope* call_scope = new Scope(self, scope);

    FancyObject* val;
    try {
      val = _body->eval(call_scope);
    } catch(return_value& rv) {
      if(rv.enclosing_method == this) {
        return rv.return_value;
      } else {
        if(!call_scope->is_closed()) {
          delete call_scope;
          call_scope = NULL;
        }
        throw rv;
      }
    }

    if(!call_scope->is_closed()) {
      delete call_scope;
      call_scope = NULL;
    }

    return val;
  }

  string Method::to_s() const
  {
    return "<Method : '" + _method_ident + "' Doc:'" + _docstring + "'>";
  }

  string Method::to_sexp() const
  {
    stringstream s;

    s << "['method, \"" << _method_ident << "\""
      << ", ";

    // insert argument names
    s << "[";
    int count = 1;
    int size = _argnames.size();
    list< pair<Identifier*, Identifier*> >::const_iterator it;
    for(it = _argnames.begin(); it != _argnames.end(); it++) {
      s << it->second->to_sexp();
      if(count < size) {
        s << ", ";
      }
      count++;
    }
    s << "], ";

    // insert method body
    s << _body->to_sexp();

    s << "]";

    return s.str();
  }

  void Method::init_method_ident()
  {
    stringstream str;
    list< pair<Identifier*, Identifier*> >::iterator it;
    for(it = _argnames.begin(); it != _argnames.end(); it++) {
      str << it->first->name();
      if(!_is_operator) {
        str << ":";
      }
    }
    _method_ident = str.str();
  }

  void Method::init_docstring()
  {
    _docstring = _body->docstring();
  }

}
<commit_msg>Set override_self to true for body_block in Method constructor.<commit_after>#include "../vendor/gc/include/gc.h"
#include "../vendor/gc/include/gc_cpp.h"
#include "../vendor/gc/include/gc_allocator.h"

#include <sstream>

#include "method.h"
#include "block.h"
#include "utils.h"
#include "bootstrap/core_classes.h"
#include "parser/nodes/return.h"

namespace fancy {

  Method::Method(Identifier* op_name, Identifier* op_argname, ExpressionList* body) :
    FancyObject(MethodClass),
    _body(body),
    _is_operator(true),
    _body_block(NULL)
  {
    _argnames.push_back(pair<Identifier*, Identifier*>(op_name, op_argname));
    init_method_ident();
    init_docstring();
    _body->set_enclosing_method(this);
  }

  Method::Method(const list< pair<Identifier*, Identifier*> > argnames,
                 ExpressionList* body) :
    FancyObject(MethodClass),
    _argnames(argnames),
    _body(body),
    _is_operator(false),
    _body_block(NULL)
  {
    init_method_ident();
    init_docstring();
    _body->set_enclosing_method(this);
  }

  Method::Method() :
    FancyObject(MethodClass),
    _is_operator(false),
    _body_block(NULL)
  {
  }

  Method::Method(Block* body) :
    FancyObject(MethodClass),
    _body(NULL),
    _is_operator(false),
    _body_block(body)
  {
    _body_block->set_enclosing_method(this);
    _body_block->override_self(true);
  }

  FancyObject* Method::call(FancyObject* self, FancyObject* *args, int argc, Scope *scope, FancyObject* sender)
  {
    Callable::check_sender_access(_method_ident, self, sender, scope);
    if(_body_block) {
      try {
        return _body_block->call(self, args, argc, scope, sender);
      } catch(return_value& rv) {
        if(rv.enclosing_method == this) {
          return rv.return_value;
        } else {
          throw rv;
        }
      }
    } else {
      // check if method is empty
      if(_body->size() == 0)
        return nil;

      Scope* call_scope = new Scope(self, scope);

      // check amount of given arguments
      if(_argnames.size() != (unsigned int)argc) {
        error("Given amount of arguments (")
          << argc
          << ") doesn't match expected amount ("
          << _argnames.size()
          << ")";
      } else {
        // if amount ok, set the parameters to the given arguments
        list< pair<Identifier*, Identifier*> >::iterator name_it = _argnames.begin();
        // list<FancyObject*>::iterator arg_it = args.begin();
        int i = 0;

        while(name_it != _argnames.end() && i < argc) {
          // name_it->second holds the name of the actual param name
          // (the first is part of the method name)
          call_scope->define(name_it->second->name(), args[i]);
          name_it++;
          i++;
        }

        // finally, eval the methods body expression
        FancyObject* val;
        try {
          val = _body->eval(call_scope);
        } catch(return_value& rv) {
          if(rv.enclosing_method == this) {
            return rv.return_value;
          } else {
            if(!call_scope->is_closed()) {
              delete call_scope;
              call_scope = NULL;
            }
            throw rv;
          }
        }

        if(!call_scope->is_closed()) {
          delete call_scope;
          call_scope = NULL;
        }
        return val;
      }

      return nil;
    }
  }

  FancyObject* Method::call(FancyObject* self, Scope *scope, FancyObject* sender)
  {
    Callable::check_sender_access(_method_ident, self, sender, scope);

    // check if method is empty
    if(_body->size() == 0)
      return nil;

    Scope* call_scope = new Scope(self, scope);

    FancyObject* val;
    try {
      val = _body->eval(call_scope);
    } catch(return_value& rv) {
      if(rv.enclosing_method == this) {
        return rv.return_value;
      } else {
        if(!call_scope->is_closed()) {
          delete call_scope;
          call_scope = NULL;
        }
        throw rv;
      }
    }

    if(!call_scope->is_closed()) {
      delete call_scope;
      call_scope = NULL;
    }

    return val;
  }

  string Method::to_s() const
  {
    return "<Method : '" + _method_ident + "' Doc:'" + _docstring + "'>";
  }

  string Method::to_sexp() const
  {
    stringstream s;

    s << "['method, \"" << _method_ident << "\""
      << ", ";

    // insert argument names
    s << "[";
    int count = 1;
    int size = _argnames.size();
    list< pair<Identifier*, Identifier*> >::const_iterator it;
    for(it = _argnames.begin(); it != _argnames.end(); it++) {
      s << it->second->to_sexp();
      if(count < size) {
        s << ", ";
      }
      count++;
    }
    s << "], ";

    // insert method body
    s << _body->to_sexp();

    s << "]";

    return s.str();
  }

  void Method::init_method_ident()
  {
    stringstream str;
    list< pair<Identifier*, Identifier*> >::iterator it;
    for(it = _argnames.begin(); it != _argnames.end(); it++) {
      str << it->first->name();
      if(!_is_operator) {
        str << ":";
      }
    }
    _method_ident = str.str();
  }

  void Method::init_docstring()
  {
    _docstring = _body->docstring();
  }

}
<|endoftext|>


<commit_before>#include "taichi_grid.h"
#include <taichi/visual/texture.h>
#include <taichi/system/threading.h>
#include <taichi/util.h>
#include <taichi/math/svd.h>

TC_NAMESPACE_BEGIN

struct NodeFlags : public bit::Bits<32> {
  using Base = bit::Bits<32>;
  TC_BIT_FIELD(uint8, num_effective_neighbours, 0);
  TC_BIT_FIELD(bool, effective, 8);
};

struct Node {
  real channels[16];

  real &operator[](int i) {
    return channels[i];
  }

  NodeFlags &flags() {
    return bit::reinterpret_bits<NodeFlags>(channels[15]);
  }
};

using Block = TBlock<Node, char, TSize3D<8>, 0>;

class MGPCGTest {
 public:
  static constexpr auto dim = Block::dim;
  using Vector = TVector<real, dim>;
  using VectorP = TVector<real, dim + 1>;
  using Matrix = TMatrix<real, dim>;
  using Grid = TaichiGrid<Block>;

  std::vector<std::unique_ptr<Grid>> grids;
  static constexpr int mg_lv = 3;

  using VectorI = Vector3i;
  using Vectori = VectorI;
  using GridScratchPad = TGridScratchPad<Block>;

  enum { CH_R, CH_Z, CH_X, CH_B, CH_TMP, CH_P, CH_MG_U, CH_MG_B, CH_MG_R };

  MGPCGTest() {
    // Span a region in
    grids.resize(mg_lv);
    for (int i = 0; i < mg_lv; i++) {
      grids[i] = std::make_unique<Grid>();
    }
    TC_ASSERT(mg_lv >= 1);
    constexpr int n = 32;
    TC_ASSERT_INFO(bit::is_power_of_two(n), "Only POT grid sizes supported");
    Region3D active_region(VectorI(-n, -n, -n * 2), VectorI(n, n, n * 2));
    for (auto &ind : active_region) {
      grids[0]->touch(ind.get_ipos());
      grids[0]->node(ind.get_ipos()).flags().set_effective(true);
      if (ind.get_ipos() == VectorI(0)) {
        grids[0]->node(ind.get_ipos())[CH_B] = 1;
      }
    }
    set_up_hierechy();
  }

  void set_up_hierechy() {
    int total_blocks = grids[0]->num_active_blocks();
    for (int i = 0; i < mg_lv - 1; i++) {
      grids[i]->coarsen_to(
          *grids[i + 1], [&](Block &b, Grid::PyramidAncestors &an) {
            for (auto ind : b.get_local_region()) {
              b.node_local(ind.get_ipos()).flags().set_effective(true);
            }
          });
      total_blocks /= 8;
      TC_ASSERT(grids[i + 1]->num_active_blocks() == total_blocks);
    }
  }

  void residual(int level, int U, int B, int R) {
    grids[level]->advance(
        [&](Block &b, Grid::Ancestors &an) {
          GridScratchPad scratch(an);
          std::memcpy(&b.nodes[0], &an[VectorI(0)]->nodes[0], sizeof(b.nodes));
          // 6 neighbours
          for (int i = 0; i < Block::size[0]; i++) {
            for (int j = 0; j < Block::size[1]; j++) {
              for (int k = 0; k < Block::size[2]; k++) {
                auto rhs = b.get_node_volume()[i][j][k][B];
                auto c = b.get_node_volume()[i][j][k][U];
                auto fetch = [&](int ii, int jj, int kk) {
                  rhs += (scratch.data[i + ii][j + jj][k + kk][U] - c);
                };
                fetch(0, 0, 1);
                fetch(0, 0, -1);
                fetch(0, 1, 0);
                fetch(0, -1, 0);
                fetch(1, 0, 0);
                fetch(-1, 0, 0);
                b.get_node_volume()[i][j][k][R] = rhs;
              }
            }
          }
        },
        false);
  }

  void multiply(int channel_out, int channel_in) {
    // TODO: this supports zero-Dirichlet BC only!
    grids[0]->advance(
        [&](Block &b, Grid::Ancestors &an) {
          GridScratchPad scratch(an);
          std::memcpy(&b.nodes[0], &an[VectorI(0)]->nodes[0], sizeof(b.nodes));
          // 6 neighbours
          for (int i = 0; i < Block::size[0]; i++) {
            for (int j = 0; j < Block::size[1]; j++) {
              for (int k = 0; k < Block::size[2]; k++) {
                int count = 0;
                real tmp = 0;
                auto &o = b.get_node_volume()[i][j][k][channel_out];
                auto fetch = [&](int ii, int jj, int kk) {
                  auto &n = scratch.data[i + (ii)][j + (jj)][k + (kk)];
                  count++;
                  tmp += n[channel_in];
                };
                fetch(0, 0, 1);
                fetch(0, 0, -1);
                fetch(0, 1, 0);
                fetch(0, -1, 0);
                fetch(1, 0, 0);
                fetch(-1, 0, 0);
                o = count * scratch.data[i][j][k][channel_in] - tmp;
                if (o != o) {
                  TC_P(b.base_coord);
                  TC_P(o);
                  TC_P(i);
                  TC_P(j);
                  TC_P(k);
                  Time::sleep(0.01);
                }
              }
            }
          }
        },
        false);
  }

  // out += a + scale * b
  void saxpy(int channel_out, int channel_a, int channel_b, real scale) {
    TC_ASSERT(!with_mpi());
    grids[0]->map([&](Block &b) {
      for (auto &n : b.nodes) {
        n[channel_out] = n[channel_a] + scale * n[channel_b];
      }
    });
  }

  // out += a + scale * b
  void copy(int channel_out, int channel_a) {
    grids[0]->map([&](Block &b) {
      for (auto &n : b.nodes) {
        n[channel_out] = n[channel_a];
      }
    });
  }

  float64 dot_product(int channel_a, int channel_b) {
    return grids[0]->reduce([&](Block &b) -> float64 {
      float64 sum = 0;
      for (auto &n : b.nodes) {
        sum += n[channel_a] * n[channel_b];
      }
      return sum;
    });
  }

  void smooth(int level, int U, int B) {
    // TODO: this supports zero-Dirichlet BC only!
    grids[level]->advance(
        [&](Grid::Block &b, Grid::Ancestors &an) {
          GridScratchPad scratch(an);
          std::memcpy(&b.nodes[0], &an[VectorI(0)]->nodes[0], sizeof(b.nodes));
          // 6 neighbours
          for (int i = 0; i < Block::size[0]; i++) {
            for (int j = 0; j < Block::size[1]; j++) {
              for (int k = 0; k < Block::size[2]; k++) {
                TC_ASSERT(scratch.data[i][j][k].flags().get_effective());
                int count = 0;
                // (B - Lu) / Diag
                real tmp = scratch.data[i][j][k][B];
                auto fetch = [&](int ii, int jj, int kk) {
                  count += 1;
                  tmp += scratch.data[i + ii][j + jj][k + kk][U];
                };
                fetch(0, 0, 1);
                fetch(0, 0, -1);
                fetch(0, 1, 0);
                fetch(0, -1, 0);
                fetch(1, 0, 0);
                fetch(-1, 0, 0);
                auto original = scratch.data[i][j][k][U];
                TC_ASSERT(count != 0);
                auto &o = b.get_node_volume()[i][j][k][U];
                o = original + (tmp / count - original) * 1;  // 0.666667_f;
                /*
                if (tmp != 0) {
                  TC_P(b.base_coord + Vector3i(i, j, k));
                  TC_P(o);
                  TC_P(tmp);
                  TC_P(count);
                }
                */
              }
            }
          }
        },
        false);
  }

  void clear(int level, int channel) {
    grids[level]->for_each_node([&](Block::Node &n) { n[channel] = 0; });
  }

  // B[level + 1] = coarsened(R[level])
  void restrict(int level, int R_in, int B_out) {
    // NOTE: supports POT grids only
    // sum residual
    clear(level + 1, B_out);
    grids[level]->coarsen_to(
        *grids[level + 1], [&](Block &block, Grid::PyramidAncestors &an) {
          for (auto ind : Region3D(Vector3i(0), Vector3i(2))) {
            if (!an[ind.get_ipos()]) {
              TC_NOT_IMPLEMENTED
              continue;
            }
            Block &ab = *an[ind.get_ipos()];
            for (auto j : ab.get_local_region()) {
              auto coarse_coord = div_floor(
                  ind.get_ipos() * Vector3i(Block::size) + j.get_ipos(),
                  Vector3i(2));
              block.node_local(coarse_coord).channels[B_out] +=
                  ab.node_local(j.get_ipos())[R_in];
            }
          }
        });
  }

  // U[level] += refined(U]level + 1]);
  void prolongate(int level, int U) {
    real scale = 0.5_f;
    // upsample and apply correction
    grids[level]->refine_from(
        *grids[level + 1], [&](Block &block, Block &ancestor) {
          for (auto ind : block.get_global_region()) {
            auto correction =
                scale *
                ancestor.node_global(div_floor(ind.get_ipos(), Vector3i(2)))[U];
            block.node_global(ind.get_ipos())[U] += correction;
          }
        });
  }

  real norm(int channel) {
    return (real)std::sqrt(dot_product(channel, channel));
  }

  void V_cycle(int channel_in,
               int channel_out,
               bool use_as_preconditioner = true) {
    copy(CH_MG_B, channel_in);
    constexpr int U = CH_MG_U, B = CH_MG_B, R = CH_MG_R;
    constexpr int smoothing_iters = 4, bottom_smoothing_iter = 100;
    if (use_as_preconditioner) {
      clear(0, U);
    }
    for (int i = 0; i < mg_lv - 1; i++) {
      // pre-smoothing
      for (int j = 0; j < smoothing_iters; j++) {
        smooth(i, U, B);
      }
      residual(i, U, B, R);
      restrict(i, R, B);
      clear(i + 1, U);
    }

    // Bottom solve
    for (int j = 0; j < bottom_smoothing_iter; j++) {
      smooth(mg_lv - 1, U, B);
    }

    for (int i = mg_lv - 2; i >= 0; i--) {
      prolongate(i, U);
      // post-smoothing
      for (int j = 0; j < smoothing_iters; j++) {
        smooth(i, U, B);
      }
    }
    copy(channel_out, CH_MG_U);
  }

  void run() {
    for (int i = 0; i < 1000000; i++) {
      V_cycle(CH_B, CH_X, false);
      residual(0, CH_X, CH_B, CH_R);
      /*
      grids[0]->for_each_block([&](Block &b) {
        for (auto ind : b.get_global_region())  {
          auto val = b.node_global(ind.get_ipos())[CH_B];
          if ()
        }
      });
      */
      auto residual_l2 = norm(CH_R);
      TC_TRACE("iter {}, residual {}", i, residual_l2);
      if (residual_l2 < 1e-7) {
        break;
      }
    }
  }

  // https://en.wikipedia.org/wiki/Conjugate_gradient_method
  void run_pcg() {
    bool use_preconditioner = true;
    TC_P(norm(CH_B));
    // r = b - Ax
    residual(0, CH_X, CH_B, CH_R);
    // z = M^-1 r
    if (use_preconditioner) {
      V_cycle(CH_R, CH_Z);
    } else {
      copy(CH_Z, CH_R);
    }
    // p = z
    copy(CH_P, CH_Z);
    auto old_zr = dot_product(CH_Z, CH_R);
    for (int i = 0; i < 100000; i++) {
      multiply(CH_Z, CH_P);
      real alpha = old_zr / dot_product(CH_P, CH_Z);

      saxpy(CH_X, CH_X, CH_P, alpha);
      saxpy(CH_R, CH_R, CH_Z, -alpha);

      auto residual_l2 = norm(CH_R);
      TC_TRACE("iter {}, residual {}", i, residual_l2);
      if (residual_l2 < 1e-7) {
        break;
      }

      if (use_preconditioner) {
        V_cycle(CH_R, CH_Z);
      } else {
        copy(CH_Z, CH_R);
      }

      auto new_zr = dot_product(CH_Z, CH_R);
      auto beta = new_zr / old_zr;
      old_zr = new_zr;
      saxpy(CH_P, CH_Z, CH_P, beta);
    }
  }
};

auto mgpcg = [](const std::vector<std::string> &params) {
  //ThreadedTaskManager::TbbParallelismControl _(1);
  std::unique_ptr<MGPCGTest> mgpcg;
  mgpcg = std::make_unique<MGPCGTest>();
  TC_TIME(mgpcg->run_pcg());
};

TC_REGISTER_TASK(mgpcg);

TC_NAMESPACE_END
<commit_msg>2/3 damped jacobi seems necessary...<commit_after>#include "taichi_grid.h"
#include <taichi/visual/texture.h>
#include <taichi/system/threading.h>
#include <taichi/util.h>
#include <taichi/math/svd.h>

TC_NAMESPACE_BEGIN

struct NodeFlags : public bit::Bits<32> {
  using Base = bit::Bits<32>;
  TC_BIT_FIELD(uint8, num_effective_neighbours, 0);
  TC_BIT_FIELD(bool, effective, 8);
};

struct Node {
  real channels[16];

  real &operator[](int i) {
    return channels[i];
  }

  NodeFlags &flags() {
    return bit::reinterpret_bits<NodeFlags>(channels[15]);
  }
};

using Block = TBlock<Node, char, TSize3D<8>, 0>;

class MGPCGTest {
 public:
  static constexpr auto dim = Block::dim;
  using Vector = TVector<real, dim>;
  using VectorP = TVector<real, dim + 1>;
  using Matrix = TMatrix<real, dim>;
  using Grid = TaichiGrid<Block>;

  std::vector<std::unique_ptr<Grid>> grids;
  static constexpr int mg_lv = 3;

  using VectorI = Vector3i;
  using Vectori = VectorI;
  using GridScratchPad = TGridScratchPad<Block>;

  enum { CH_R, CH_Z, CH_X, CH_B, CH_P, CH_MG_U, CH_MG_B, CH_MG_R };

  MGPCGTest() {
    // Span a region in
    grids.resize(mg_lv);
    for (int i = 0; i < mg_lv; i++) {
      grids[i] = std::make_unique<Grid>();
    }
    TC_ASSERT(mg_lv >= 1);
    constexpr int n = 32;
    TC_ASSERT_INFO(bit::is_power_of_two(n), "Only POT grid sizes supported");
    Region3D active_region(VectorI(-n, -n, -n * 2), VectorI(n, n, n * 2));
    for (auto &ind : active_region) {
      grids[0]->touch(ind.get_ipos());
      grids[0]->node(ind.get_ipos()).flags().set_effective(true);
      if (ind.get_ipos() == VectorI(0)) {
        grids[0]->node(ind.get_ipos())[CH_B] = 1;
      }
    }
    set_up_hierechy();
  }

  void set_up_hierechy() {
    int total_blocks = grids[0]->num_active_blocks();
    for (int i = 0; i < mg_lv - 1; i++) {
      grids[i]->coarsen_to(
          *grids[i + 1], [&](Block &b, Grid::PyramidAncestors &an) {
            for (auto ind : b.get_local_region()) {
              b.node_local(ind.get_ipos()).flags().set_effective(true);
            }
          });
      total_blocks /= 8;
      TC_ASSERT(grids[i + 1]->num_active_blocks() == total_blocks);
    }
  }

  void residual(int level, int U, int B, int R) {
    grids[level]->advance(
        [&](Block &b, Grid::Ancestors &an) {
          GridScratchPad scratch(an);
          std::memcpy(&b.nodes[0], &an[VectorI(0)]->nodes[0], sizeof(b.nodes));
          // 6 neighbours
          for (int i = 0; i < Block::size[0]; i++) {
            for (int j = 0; j < Block::size[1]; j++) {
              for (int k = 0; k < Block::size[2]; k++) {
                auto rhs = b.get_node_volume()[i][j][k][B];
                auto c = b.get_node_volume()[i][j][k][U];
                auto fetch = [&](int ii, int jj, int kk) {
                  rhs += (scratch.data[i + ii][j + jj][k + kk][U] - c);
                };
                fetch(0, 0, 1);
                fetch(0, 0, -1);
                fetch(0, 1, 0);
                fetch(0, -1, 0);
                fetch(1, 0, 0);
                fetch(-1, 0, 0);
                b.get_node_volume()[i][j][k][R] = rhs;
              }
            }
          }
        },
        false);
  }

  void multiply(int channel_out, int channel_in) {
    // TODO: this supports zero-Dirichlet BC only!
    grids[0]->advance(
        [&](Block &b, Grid::Ancestors &an) {
          GridScratchPad scratch(an);
          std::memcpy(&b.nodes[0], &an[VectorI(0)]->nodes[0], sizeof(b.nodes));
          // 6 neighbours
          for (int i = 0; i < Block::size[0]; i++) {
            for (int j = 0; j < Block::size[1]; j++) {
              for (int k = 0; k < Block::size[2]; k++) {
                int count = 0;
                real tmp = 0;
                auto &o = b.get_node_volume()[i][j][k][channel_out];
                auto fetch = [&](int ii, int jj, int kk) {
                  auto &n = scratch.data[i + (ii)][j + (jj)][k + (kk)];
                  count++;
                  tmp += n[channel_in];
                };
                fetch(0, 0, 1);
                fetch(0, 0, -1);
                fetch(0, 1, 0);
                fetch(0, -1, 0);
                fetch(1, 0, 0);
                fetch(-1, 0, 0);
                o = count * scratch.data[i][j][k][channel_in] - tmp;
                if (o != o) {
                  TC_P(b.base_coord);
                  TC_P(o);
                  TC_P(i);
                  TC_P(j);
                  TC_P(k);
                  Time::sleep(0.01);
                }
              }
            }
          }
        },
        false);
  }

  // out += a + scale * b
  void saxpy(int channel_out, int channel_a, int channel_b, real scale) {
    TC_ASSERT(!with_mpi());
    grids[0]->map([&](Block &b) {
      for (auto &n : b.nodes) {
        n[channel_out] = n[channel_a] + scale * n[channel_b];
      }
    });
  }

  // out += a + scale * b
  void copy(int channel_out, int channel_a) {
    grids[0]->map([&](Block &b) {
      for (auto &n : b.nodes) {
        n[channel_out] = n[channel_a];
      }
    });
  }

  float64 dot_product(int channel_a, int channel_b) {
    return grids[0]->reduce([&](Block &b) -> float64 {
      float64 sum = 0;
      for (auto &n : b.nodes) {
        sum += n[channel_a] * n[channel_b];
      }
      return sum;
    });
  }

  void smooth(int level, int U, int B) {
    // TODO: this supports zero-Dirichlet BC only!
    grids[level]->advance(
        [&](Grid::Block &b, Grid::Ancestors &an) {
          GridScratchPad scratch(an);
          std::memcpy(&b.nodes[0], &an[VectorI(0)]->nodes[0], sizeof(b.nodes));
          // 6 neighbours
          for (int i = 0; i < Block::size[0]; i++) {
            for (int j = 0; j < Block::size[1]; j++) {
              for (int k = 0; k < Block::size[2]; k++) {
                TC_ASSERT(scratch.data[i][j][k].flags().get_effective());
                int count = 0;
                // (B - Lu) / Diag
                real tmp = scratch.data[i][j][k][B];
                auto fetch = [&](int ii, int jj, int kk) {
                  count += 1;
                  tmp += scratch.data[i + ii][j + jj][k + kk][U];
                };
                fetch(0, 0, 1);
                fetch(0, 0, -1);
                fetch(0, 1, 0);
                fetch(0, -1, 0);
                fetch(1, 0, 0);
                fetch(-1, 0, 0);
                auto original = scratch.data[i][j][k][U];
                TC_ASSERT(count != 0);
                auto &o = b.get_node_volume()[i][j][k][U];
                //o = original + (tmp / count - original) * 1;  // 0.666667_f;
                o = original + (tmp / count - original) * 0.666667_f;
                /*
                if (tmp != 0) {
                  TC_P(b.base_coord + Vector3i(i, j, k));
                  TC_P(o);
                  TC_P(tmp);
                  TC_P(count);
                }
                */
              }
            }
          }
        },
        false);
  }

  void clear(int level, int channel) {
    grids[level]->for_each_node([&](Block::Node &n) { n[channel] = 0; });
  }

  // B[level + 1] = coarsened(R[level])
  void restrict(int level, int R_in, int B_out) {
    // NOTE: supports POT grids only
    // sum residual
    clear(level + 1, B_out);
    grids[level]->coarsen_to(
        *grids[level + 1], [&](Block &block, Grid::PyramidAncestors &an) {
          for (auto ind : Region3D(Vector3i(0), Vector3i(2))) {
            if (!an[ind.get_ipos()]) {
              TC_NOT_IMPLEMENTED
              continue;
            }
            Block &ab = *an[ind.get_ipos()];
            for (auto j : ab.get_local_region()) {
              auto coarse_coord = div_floor(
                  ind.get_ipos() * Vector3i(Block::size) + j.get_ipos(),
                  Vector3i(2));
              block.node_local(coarse_coord).channels[B_out] +=
                  ab.node_local(j.get_ipos())[R_in];
            }
          }
        });
  }

  // U[level] += refined(U]level + 1]);
  void prolongate(int level, int U) {
    real scale = 0.5_f;
    // upsample and apply correction
    grids[level]->refine_from(
        *grids[level + 1], [&](Block &block, Block &ancestor) {
          for (auto ind : block.get_global_region()) {
            auto correction =
                scale *
                ancestor.node_global(div_floor(ind.get_ipos(), Vector3i(2)))[U];
            block.node_global(ind.get_ipos())[U] += correction;
          }
        });
  }

  real norm(int channel) {
    return (real)std::sqrt(dot_product(channel, channel));
  }

  void V_cycle(int channel_in,
               int channel_out,
               bool use_as_preconditioner = true) {
    copy(CH_MG_B, channel_in);
    constexpr int U = CH_MG_U, B = CH_MG_B, R = CH_MG_R;
    constexpr int smoothing_iters = 3, bottom_smoothing_iter = 100;
    if (use_as_preconditioner) {
      clear(0, U);
    }
    for (int i = 0; i < mg_lv - 1; i++) {
      // pre-smoothing
      for (int j = 0; j < smoothing_iters; j++) {
        smooth(i, U, B);
      }
      residual(i, U, B, R);
      restrict(i, R, B);
      clear(i + 1, U);
    }

    // Bottom solve
    for (int j = 0; j < bottom_smoothing_iter; j++) {
      smooth(mg_lv - 1, U, B);
    }

    for (int i = mg_lv - 2; i >= 0; i--) {
      prolongate(i, U);
      // post-smoothing
      for (int j = 0; j < smoothing_iters; j++) {
        smooth(i, U, B);
      }
    }
    copy(channel_out, CH_MG_U);
  }

  void run() {
    for (int i = 0; i < 1000000; i++) {
      V_cycle(CH_B, CH_X, false);
      residual(0, CH_X, CH_B, CH_R);
      /*
      grids[0]->for_each_block([&](Block &b) {
        for (auto ind : b.get_global_region())  {
          auto val = b.node_global(ind.get_ipos())[CH_B];
          if ()
        }
      });
      */
      auto residual_l2 = norm(CH_R);
      TC_TRACE("iter {}, residual {}", i, residual_l2);
      if (residual_l2 < 1e-7) {
        break;
      }
    }
  }

  // https://en.wikipedia.org/wiki/Conjugate_gradient_method
  void run_pcg() {
    bool use_preconditioner = true;
    TC_P(norm(CH_B));
    // r = b - Ax
    residual(0, CH_X, CH_B, CH_R);
    // z = M^-1 r
    if (use_preconditioner) {
      V_cycle(CH_R, CH_Z);
    } else {
      copy(CH_Z, CH_R);
    }
    // p = z
    copy(CH_P, CH_Z);
    auto old_zr = dot_product(CH_Z, CH_R);
    for (int i = 0; i < 100000; i++) {
      multiply(CH_Z, CH_P);
      real alpha = old_zr / dot_product(CH_P, CH_Z);

      saxpy(CH_X, CH_X, CH_P, alpha);
      saxpy(CH_R, CH_R, CH_Z, -alpha);

      auto residual_l2 = norm(CH_R);
      TC_TRACE("iter {}, residual {}", i, residual_l2);
      if (residual_l2 < 1e-7) {
        break;
      }

      if (use_preconditioner) {
        V_cycle(CH_R, CH_Z);
      } else {
        copy(CH_Z, CH_R);
      }

      auto new_zr = dot_product(CH_Z, CH_R);
      auto beta = new_zr / old_zr;
      old_zr = new_zr;
      saxpy(CH_P, CH_Z, CH_P, beta);
    }
  }
};

auto mgpcg = [](const std::vector<std::string> &params) {
  // ThreadedTaskManager::TbbParallelismControl _(1);
  std::unique_ptr<MGPCGTest> mgpcg;
  mgpcg = std::make_unique<MGPCGTest>();
  TC_TIME(mgpcg->run_pcg());
};

TC_REGISTER_TASK(mgpcg);

TC_NAMESPACE_END
<|endoftext|>