<commit_before>#include "defs.h"

#include "client/keyboard/keytable.h"
#include "client/ui/event.hpp"
// #include "client/ui/menu.hpp"
#include "game/ld22/screen.hpp"
#include "client/ui/window.hpp"
#include "sys/path.hpp"
#include "sys/rand.hpp"
#include "sys/error_win.hpp"
#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);
}

static void init()
{
    exe_options opts;
    opts.alt_data_dir= NULL;
    LPWSTR cmdLine = GetCommandLine();
    if (cmdLine && *cmdLine) {
        int argc, i;
        LPWSTR *argv = CommandLineToArgvW(cmdLine, &argc);
        if (!argv)
            throw error_win(GetLastError());
        for (i = 1; i < argc; ++i) {
            wchar_t *s = argv[i];
            if (s[0] == L'/') {
                switch (s[1]) {
                case L'I':
                    if (!s[2]) {
                        if (i + 1 >= argc)
                            goto parseError;
                        i++;
                        s = argv[i];
                    } else {
                        s += 2;
                    }
                    opts.alt_data_dir = s;
                    break;

                default:
                    goto parseError;
                }
            } else {
                goto parseError;
            }
        }
        if (false) {
parseError:
            errorBox("Invalid command line options");
        }
    }

    path_init(&opts);
    Rand::global.seed();
}

void (APIENTRY *glBlendColor)(GLclampf, GLclampf, GLclampf, GLclampf);

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

    init();
    w.setScreen(new LD22::Screen);

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

    glBlendColor = (void (APIENTRY *)(GLclampf, GLclampf, GLclampf, GLclampf))
        wglGetProcAddress("glBlendColor");
    if (!glBlendColor) {
        errorBox("Can't get glBlendColor address.");
        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>Add try/catch block around Windows main<commit_after>#include "defs.h"

#include "client/keyboard/keytable.h"
#include "client/ui/event.hpp"
// #include "client/ui/menu.hpp"
#include "game/ld22/screen.hpp"
#include "client/ui/window.hpp"
#include "sys/path.hpp"
#include "sys/rand.hpp"
#include "sys/error_win.hpp"
#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);
}

static void init()
{
    exe_options opts;
    opts.alt_data_dir= NULL;
    LPWSTR cmdLine = GetCommandLine();
    if (cmdLine && *cmdLine) {
        int argc, i;
        LPWSTR *argv = CommandLineToArgvW(cmdLine, &argc);
        if (!argv)
            throw error_win(GetLastError());
        for (i = 1; i < argc; ++i) {
            wchar_t *s = argv[i];
            if (s[0] == L'/') {
                switch (s[1]) {
                case L'I':
                    if (!s[2]) {
                        if (i + 1 >= argc)
                            goto parseError;
                        i++;
                        s = argv[i];
                    } else {
                        s += 2;
                    }
                    opts.alt_data_dir = s;
                    break;

                default:
                    goto parseError;
                }
            } else {
                goto parseError;
            }
        }
        if (false) {
parseError:
            errorBox("Invalid command line options");
        }
    }

    path_init(&opts);
    Rand::global.seed();
}

void (APIENTRY *glBlendColor)(GLclampf, GLclampf, GLclampf, GLclampf);

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

    try {
        init();
        w.setScreen(new LD22::Screen);

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

        glBlendColor = (void (APIENTRY *)(GLclampf, GLclampf, GLclampf, GLclampf))
            wglGetProcAddress("glBlendColor");
        if (!glBlendColor) {
            errorBox("Can't get glBlendColor address.");
            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);
            }
        }
    } catch (std::exception &ex) {
        killGLWindow();
        char buf[256];
        _snprintf(buf, sizeof(buf), "Exception: %s", ex.what());
        errorBox(buf);
    }
done:

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


<commit_before>
#include "opencv2/objdetect.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/tracking.hpp"

#include "Logger.hpp"
#include "SDLDriver.hpp"
#include "FFmpegDriver.hpp"
#include "FaceTracker.hpp"
#include "FrameDerivatives.hpp"
#include "FaceMapper.hpp"
#include "Metrics.hpp"
#include "Utilities.hpp"
#include "OutputDriver.hpp"
#include "SphinxDriver.hpp"
#include "EventLogger.hpp"

#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdlib>

using namespace std;
using namespace cv;
using namespace YerFace;

String configFile;

String inVideo;
String inVideoFormat;
String inVideoSize;
String inVideoRate;
String inVideoCodec;

String inAudio;
String inAudioFormat;
String inAudioChannels;
String inAudioRate;
String inAudioCodec;

String outAudioChannelMap;

String inEvents;
String outData;
String previewImgSeq;
bool lowLatency = false;
bool audioPreview = false;
bool tryAudioInVideo = false;
bool openInputAudio = false;

double from, until;

String window_name = "Yer Face: A Stupid Facial Performance Capture Engine";

json config = NULL;

SDLWindowRenderer sdlWindowRenderer;
bool windowInitializationFailed;
SDL_Thread *workerThread;

Logger *logger = NULL;
SDLDriver *sdlDriver = NULL;
FFmpegDriver *ffmpegDriver = NULL;
FrameDerivatives *frameDerivatives = NULL;
FaceTracker *faceTracker = NULL;
FaceMapper *faceMapper = NULL;
Metrics *metrics = NULL;
OutputDriver *outputDriver = NULL;
SphinxDriver *sphinxDriver = NULL;
EventLogger *eventLogger = NULL;

unsigned long workingFrameNumber = 0;
SDL_mutex *flipWorkingCompletedMutex;

//VARIABLES PROTECTED BY frameSizeMutex
Size frameSize;
bool frameSizeValid = false;
SDL_mutex *frameSizeMutex;
//END VARIABLES PROTECTED BY frameSizeMutex

static int runCaptureLoop(void *ptr);
void doRenderPreviewFrame(void);
void parseConfigFile(void);

int main(int argc, const char** argv) {
	Logger::setLoggingFilter(SDL_LOG_PRIORITY_VERBOSE, SDL_LOG_CATEGORY_APPLICATION);
	logger = new Logger("YerFace");

	//Command line options.
	CommandLineParser parser(argc, argv,
		"{help h||Usage message.}"
		"{configFile C|data/config.json|Required configuration file.}"
		"{lowLatency||If true, will tweak behavior across the system to minimize latency. (Don't use this if the input is pre-recorded!)}"
		"{inVideo|/dev/video0|Video file, URL, or device to open. (Or '-' for STDIN.)}"
		"{inVideoFormat||Tell libav to use a specific format to interpret the inVideo. Leave blank for auto-detection.}"
		"{inVideoSize||Tell libav to attempt a specific resolution when interpreting inVideo. Leave blank for auto-detection.}"
		"{inVideoRate||Tell libav to attempt a specific framerate when interpreting inVideo. Leave blank for auto-detection.}"
		"{inVideoCodec||Tell libav to attempt a specific codec when interpreting inVideo. Leave blank for auto-detection.}"
		"{inAudio||Audio file, URL, or device to open. (Or '-' for STDIN.) (If you leave this blank, we will try to read the audio from inVideo.)}"
		"{inAudioFormat||Tell libav to use a specific format to interpret the inAudio. Leave blank for auto-detection.}"
		"{inAudioChannels||Tell libav to attempt a specific number of channels when interpreting inAudio. Leave blank for auto-detection.}"
		"{inAudioRate||Tell libav to attempt a specific sample rate when interpreting inAudio. Leave blank for auto-detection.}"
		"{inAudioCodec||Tell libav to attempt a specific codec when interpreting inAudio. Leave blank for auto-detection.}"
		"{outAudioChannelMap||Alter the audio channel mapping. Set to \"left\" to take only the left channel, \"right\" to take only the right channel, and leave blank for the default.}"
		"{from|-1.0|Start processing at \"from\" seconds into the input video. Unit is seconds. Minus one means the beginning of the input.}"
		"{until|-1.0|Stop processing at \"until\" seconds into the input video. Unit is seconds. Minus one means the end of the input.}"
		"{inEvents||Event replay file. (Previously generated outData, for re-processing recorded sessions.)}"
		"{outData||Output file for generated performance capture data.}"
		"{audioPreview||If true, will preview processed audio out the computer's sound device.}"
		"{previewImgSeq||If set, is presumed to be the file name prefix of the output preview image sequence.}"
		);

	parser.about("Yer Face: The butt of all the jokes. (A stupid facial performance capture engine for cartoon animation.)");
	if(parser.get<bool>("help")) {
		parser.printMessage();
		return 1;
	}
	if(parser.check()) {
		parser.printMessage();
		parser.printErrors();
		return 1;
	}
	configFile = parser.get<string>("configFile");
	try {
		parseConfigFile();
	} catch(exception &e) {
		logger->error("Failed to parse configuration file \"%s\". Got exception: %s", configFile.c_str(), e.what());
		return 1;
	}
	inVideo = parser.get<string>("inVideo");
	if(inVideo == "-") {
		inVideo = "pipe:0";
	}
	inVideoFormat = parser.get<string>("inVideoFormat");
	inVideoSize = parser.get<string>("inVideoSize");
	inVideoRate = parser.get<string>("inVideoRate");
	inVideoCodec = parser.get<string>("inVideoCodec");
	inAudio = parser.get<string>("inAudio");
	if(inAudio == "-") {
		inAudio = "pipe:0";
	}
	if(inAudio.length() > 0) {
		openInputAudio = true;
	} else {
		tryAudioInVideo = true;
	}
	inAudioFormat = parser.get<string>("inAudioFormat");
	inAudioChannels = parser.get<string>("inAudioChannels");
	inAudioRate = parser.get<string>("inAudioRate");
	inAudioCodec = parser.get<string>("inAudioCodec");
	outAudioChannelMap = parser.get<string>("outAudioChannelMap");
	from = parser.get<double>("from");
	until = parser.get<double>("until");
	inEvents = parser.get<string>("inEvents");
	outData = parser.get<string>("outData");
	previewImgSeq = parser.get<string>("previewImgSeq");
	lowLatency = parser.get<bool>("lowLatency");
	audioPreview = parser.get<bool>("audioPreview");

	if(from != -1.0 || until != -1.0) {
		if(lowLatency) {
			throw invalid_argument("--lowLatency is incompatible with --from and --until (can't seek in a real time stream)");
		}
		if(from != -1.0 && from < 0.0) {
			throw invalid_argument("If --from is enabled, it can't be negative.");
		}
		if(until != -1.0 && until < 0.0) {
			throw invalid_argument("If --until is enabled, it can't be negative.");
		}
		if(from != -1.0 && until != -1.0 && until <= from) {
			throw invalid_argument("When both are enabled, --from must be less than --until.");
		}
	}

	//Instantiate our classes.
	frameDerivatives = new FrameDerivatives(config, lowLatency);
	ffmpegDriver = new FFmpegDriver(frameDerivatives, lowLatency, lowLatency, from, until, false);
	ffmpegDriver->openInputMedia(inVideo, AVMEDIA_TYPE_VIDEO, inVideoFormat, inVideoSize, "", inVideoRate, inVideoCodec, outAudioChannelMap, tryAudioInVideo);
	if(openInputAudio) {
		ffmpegDriver->openInputMedia(inAudio, AVMEDIA_TYPE_AUDIO, inAudioFormat, "", inAudioChannels, inAudioRate, inAudioCodec, outAudioChannelMap, true);
	}
	sdlDriver = new SDLDriver(config, frameDerivatives, ffmpegDriver, audioPreview && ffmpegDriver->getIsAudioInputPresent());
	faceTracker = new FaceTracker(config, sdlDriver, frameDerivatives, lowLatency);
	faceMapper = new FaceMapper(config, sdlDriver, frameDerivatives, faceTracker);
	metrics = new Metrics(config, "YerFace", frameDerivatives, true);
	outputDriver = new OutputDriver(config, outData, frameDerivatives, faceTracker, sdlDriver);
	if(ffmpegDriver->getIsAudioInputPresent()) {
		sphinxDriver = new SphinxDriver(config, frameDerivatives, ffmpegDriver, sdlDriver, outputDriver, lowLatency);
	}
	eventLogger = new EventLogger(config, inEvents, outputDriver, frameDerivatives, from);

	outputDriver->setEventLogger(eventLogger);

	sdlWindowRenderer.window = NULL;
	sdlWindowRenderer.renderer = NULL;
	windowInitializationFailed = false;

	//Create locks.
	if((frameSizeMutex = SDL_CreateMutex()) == NULL) {
		throw runtime_error("Failed creating mutex!");
	}
	if((flipWorkingCompletedMutex = SDL_CreateMutex()) == NULL) {
		throw runtime_error("Failed creating mutex!");
	}

	//Create worker thread.
	if((workerThread = SDL_CreateThread(runCaptureLoop, "CaptureLoop", (void *)NULL)) == NULL) {
		throw runtime_error("Failed spawning worker thread!");
	}

	//Launch event / rendering loop.
	while(sdlDriver->getIsRunning()) {
		SDL_Delay(10);

		if(sdlWindowRenderer.window == NULL && !windowInitializationFailed) {
			YerFace_MutexLock(frameSizeMutex);
			if(!frameSizeValid) {
				YerFace_MutexUnlock(frameSizeMutex);
				continue;
			}
			try {
				sdlWindowRenderer = sdlDriver->createPreviewWindow(frameSize.width, frameSize.height);
			} catch(exception &e) {
				windowInitializationFailed = true;
				logger->error("Uh oh, failed to create a preview window! Got exception: %s", e.what());
				logger->warn("Continuing despite the lack of a preview window.");
			}
			YerFace_MutexUnlock(frameSizeMutex);
		}

		if(frameDerivatives->getCompletedFrameSet()) {
			doRenderPreviewFrame();
			if(sdlWindowRenderer.window != NULL) {
				sdlDriver->doRenderPreviewFrame();
			}
		}

		sdlDriver->doHandleEvents();
	}

	//Join worker thread.
	SDL_WaitThread(workerThread, NULL);

	//Cleanup.
	SDL_DestroyMutex(frameSizeMutex);
	SDL_DestroyMutex(flipWorkingCompletedMutex);

	delete eventLogger;
	if(sphinxDriver != NULL) {
		delete sphinxDriver;
	}
	delete outputDriver;
	delete metrics;
	delete faceMapper;
	delete faceTracker;
	delete frameDerivatives;
	delete ffmpegDriver;
	delete sdlDriver;
	delete logger;
	return 0;
}

int runCaptureLoop(void *ptr) {
	VideoFrame videoFrame;

	ffmpegDriver->rollDemuxerThreads();

	bool didSetFrameSizeValid = false;
	while(sdlDriver->getIsRunning()) {
		if(!sdlDriver->getIsPaused()) {
			if(!ffmpegDriver->waitForNextVideoFrame(&videoFrame)) {
				logger->info("FFmpeg Demuxer thread finished.");
				sdlDriver->setIsRunning(false);
				continue;
			}
			workingFrameNumber++;

			if(!didSetFrameSizeValid) {
				YerFace_MutexLock(frameSizeMutex);
				if(!frameSizeValid) {
					frameSize = videoFrame.frameCV.size();
					frameSizeValid = true;
					didSetFrameSizeValid = true;
				}
				YerFace_MutexUnlock(frameSizeMutex);
			}

			// Start timer
			metrics->startClock();

			frameDerivatives->setWorkingFrame(&videoFrame);
			ffmpegDriver->releaseVideoFrame(videoFrame);

			eventLogger->startNewFrame();

			faceTracker->processCurrentFrame();
			faceMapper->processCurrentFrame();

			metrics->endClock();

			while(sdlDriver->getIsPaused() && sdlDriver->getIsRunning()) {
				SDL_Delay(100);
			}

			YerFace_MutexLock(flipWorkingCompletedMutex);

			frameDerivatives->advanceWorkingFrameToCompleted();
			faceTracker->advanceWorkingToCompleted();
			faceMapper->advanceWorkingToCompleted();
			if(sphinxDriver != NULL) {
				sphinxDriver->advanceWorkingToCompleted();
			}

			eventLogger->handleCompletedFrame();
			outputDriver->handleCompletedFrame();

			//If requested, write image sequence.
			if(previewImgSeq.length() > 0) {
				doRenderPreviewFrame();

				int filenameLength = previewImgSeq.length() + 32;
				char filename[filenameLength];
				snprintf(filename, filenameLength, "%s-%06lu.png", previewImgSeq.c_str(), workingFrameNumber);
				logger->debug("YerFace writing preview frame to %s ...", filename);
				imwrite(filename, frameDerivatives->getCompletedPreviewFrame());
			}

			YerFace_MutexUnlock(flipWorkingCompletedMutex);
		}
	}

	sdlDriver->stopAudioDriverNow();
	ffmpegDriver->stopAudioCallbacksNow();

	if(sphinxDriver != NULL) {
		sphinxDriver->drainPipelineDataNow();
	}
	outputDriver->drainPipelineDataNow();

	return 0;
}

void doRenderPreviewFrame(void) {
	YerFace_MutexLock(flipWorkingCompletedMutex);

	frameDerivatives->resetCompletedPreviewFrame();

	faceTracker->renderPreviewHUD();
	faceMapper->renderPreviewHUD();
	if(sphinxDriver != NULL) {
		sphinxDriver->renderPreviewHUD();
	}

	Mat previewFrame = frameDerivatives->getCompletedPreviewFrame();

	putText(previewFrame, metrics->getTimesString().c_str(), Point(25,50), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0,0,255), 2);
	putText(previewFrame, metrics->getFPSString().c_str(), Point(25,75), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0,0,255), 2);

	YerFace_MutexUnlock(flipWorkingCompletedMutex);
}

void parseConfigFile(void) {
	logger->verbose("Opening and parsing config file: \"%s\"", configFile.c_str());
	std::ifstream fileStream = std::ifstream(configFile);
	if(fileStream.fail()) {
		throw invalid_argument("Specified config file failed to open.");
	}
	std::stringstream ssBuffer;
	ssBuffer << fileStream.rdbuf();
	config = json::parse(ssBuffer.str());
}
<commit_msg>introduce a command line option to prevent audio processing from taking place. (mainly for troubleshooting and performance testing)<commit_after>
#include "opencv2/objdetect.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/tracking.hpp"

#include "Logger.hpp"
#include "SDLDriver.hpp"
#include "FFmpegDriver.hpp"
#include "FaceTracker.hpp"
#include "FrameDerivatives.hpp"
#include "FaceMapper.hpp"
#include "Metrics.hpp"
#include "Utilities.hpp"
#include "OutputDriver.hpp"
#include "SphinxDriver.hpp"
#include "EventLogger.hpp"

#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdlib>

using namespace std;
using namespace cv;
using namespace YerFace;

String configFile;

String inVideo;
String inVideoFormat;
String inVideoSize;
String inVideoRate;
String inVideoCodec;

String inAudio;
String inAudioFormat;
String inAudioChannels;
String inAudioRate;
String inAudioCodec;

String outAudioChannelMap;

String inEvents;
String outData;
String previewImgSeq;
bool lowLatency = false;
bool audioPreview = false;
bool tryAudioInVideo = false;
bool openInputAudio = false;

double from, until;

String window_name = "Yer Face: A Stupid Facial Performance Capture Engine";

json config = NULL;

SDLWindowRenderer sdlWindowRenderer;
bool windowInitializationFailed;
SDL_Thread *workerThread;

Logger *logger = NULL;
SDLDriver *sdlDriver = NULL;
FFmpegDriver *ffmpegDriver = NULL;
FrameDerivatives *frameDerivatives = NULL;
FaceTracker *faceTracker = NULL;
FaceMapper *faceMapper = NULL;
Metrics *metrics = NULL;
OutputDriver *outputDriver = NULL;
SphinxDriver *sphinxDriver = NULL;
EventLogger *eventLogger = NULL;

unsigned long workingFrameNumber = 0;
SDL_mutex *flipWorkingCompletedMutex;

//VARIABLES PROTECTED BY frameSizeMutex
Size frameSize;
bool frameSizeValid = false;
SDL_mutex *frameSizeMutex;
//END VARIABLES PROTECTED BY frameSizeMutex

static int runCaptureLoop(void *ptr);
void doRenderPreviewFrame(void);
void parseConfigFile(void);

int main(int argc, const char** argv) {
	Logger::setLoggingFilter(SDL_LOG_PRIORITY_VERBOSE, SDL_LOG_CATEGORY_APPLICATION);
	logger = new Logger("YerFace");

	//Command line options.
	CommandLineParser parser(argc, argv,
		"{help h||Usage message.}"
		"{configFile C|data/config.json|Required configuration file.}"
		"{lowLatency||If true, will tweak behavior across the system to minimize latency. (Don't use this if the input is pre-recorded!)}"
		"{inVideo|/dev/video0|Video file, URL, or device to open. (Or '-' for STDIN.)}"
		"{inVideoFormat||Tell libav to use a specific format to interpret the inVideo. Leave blank for auto-detection.}"
		"{inVideoSize||Tell libav to attempt a specific resolution when interpreting inVideo. Leave blank for auto-detection.}"
		"{inVideoRate||Tell libav to attempt a specific framerate when interpreting inVideo. Leave blank for auto-detection.}"
		"{inVideoCodec||Tell libav to attempt a specific codec when interpreting inVideo. Leave blank for auto-detection.}"
		"{inAudio||Audio file, URL, or device to open. Alternatively: '' (blank string, the default) we will try to read the audio from inVideo. '-' we will try to read the audio from STDIN. 'ignore' we will ignore all audio from all sources.}"
		"{inAudioFormat||Tell libav to use a specific format to interpret the inAudio. Leave blank for auto-detection.}"
		"{inAudioChannels||Tell libav to attempt a specific number of channels when interpreting inAudio. Leave blank for auto-detection.}"
		"{inAudioRate||Tell libav to attempt a specific sample rate when interpreting inAudio. Leave blank for auto-detection.}"
		"{inAudioCodec||Tell libav to attempt a specific codec when interpreting inAudio. Leave blank for auto-detection.}"
		"{outAudioChannelMap||Alter the audio channel mapping. Set to \"left\" to take only the left channel, \"right\" to take only the right channel, and leave blank for the default.}"
		"{from|-1.0|Start processing at \"from\" seconds into the input video. Unit is seconds. Minus one means the beginning of the input.}"
		"{until|-1.0|Stop processing at \"until\" seconds into the input video. Unit is seconds. Minus one means the end of the input.}"
		"{inEvents||Event replay file. (Previously generated outData, for re-processing recorded sessions.)}"
		"{outData||Output file for generated performance capture data.}"
		"{audioPreview||If true, will preview processed audio out the computer's sound device.}"
		"{previewImgSeq||If set, is presumed to be the file name prefix of the output preview image sequence.}"
		);

	parser.about("Yer Face: The butt of all the jokes. (A stupid facial performance capture engine for cartoon animation.)");
	if(parser.get<bool>("help")) {
		parser.printMessage();
		return 1;
	}
	if(parser.check()) {
		parser.printMessage();
		parser.printErrors();
		return 1;
	}
	configFile = parser.get<string>("configFile");
	try {
		parseConfigFile();
	} catch(exception &e) {
		logger->error("Failed to parse configuration file \"%s\". Got exception: %s", configFile.c_str(), e.what());
		return 1;
	}
	inVideo = parser.get<string>("inVideo");
	if(inVideo == "-") {
		inVideo = "pipe:0";
	}
	inVideoFormat = parser.get<string>("inVideoFormat");
	inVideoSize = parser.get<string>("inVideoSize");
	inVideoRate = parser.get<string>("inVideoRate");
	inVideoCodec = parser.get<string>("inVideoCodec");
	inAudio = parser.get<string>("inAudio");
	if(inAudio == "-") {
		inAudio = "pipe:0";
	}
	if(inAudio.length() > 0) {
		if(inAudio == "ignore") {
			openInputAudio = false;
			tryAudioInVideo = false;
		} else {
			openInputAudio = true;
			tryAudioInVideo = false;
		}
	} else {
		openInputAudio = false;
		tryAudioInVideo = true;
	}
	inAudioFormat = parser.get<string>("inAudioFormat");
	inAudioChannels = parser.get<string>("inAudioChannels");
	inAudioRate = parser.get<string>("inAudioRate");
	inAudioCodec = parser.get<string>("inAudioCodec");
	outAudioChannelMap = parser.get<string>("outAudioChannelMap");
	from = parser.get<double>("from");
	until = parser.get<double>("until");
	inEvents = parser.get<string>("inEvents");
	outData = parser.get<string>("outData");
	previewImgSeq = parser.get<string>("previewImgSeq");
	lowLatency = parser.get<bool>("lowLatency");
	audioPreview = parser.get<bool>("audioPreview");

	if(from != -1.0 || until != -1.0) {
		if(lowLatency) {
			throw invalid_argument("--lowLatency is incompatible with --from and --until (can't seek in a real time stream)");
		}
		if(from != -1.0 && from < 0.0) {
			throw invalid_argument("If --from is enabled, it can't be negative.");
		}
		if(until != -1.0 && until < 0.0) {
			throw invalid_argument("If --until is enabled, it can't be negative.");
		}
		if(from != -1.0 && until != -1.0 && until <= from) {
			throw invalid_argument("When both are enabled, --from must be less than --until.");
		}
	}

	//Instantiate our classes.
	frameDerivatives = new FrameDerivatives(config, lowLatency);
	ffmpegDriver = new FFmpegDriver(frameDerivatives, lowLatency, lowLatency, from, until, false);
	ffmpegDriver->openInputMedia(inVideo, AVMEDIA_TYPE_VIDEO, inVideoFormat, inVideoSize, "", inVideoRate, inVideoCodec, outAudioChannelMap, tryAudioInVideo);
	if(openInputAudio) {
		ffmpegDriver->openInputMedia(inAudio, AVMEDIA_TYPE_AUDIO, inAudioFormat, "", inAudioChannels, inAudioRate, inAudioCodec, outAudioChannelMap, true);
	}
	sdlDriver = new SDLDriver(config, frameDerivatives, ffmpegDriver, audioPreview && ffmpegDriver->getIsAudioInputPresent());
	faceTracker = new FaceTracker(config, sdlDriver, frameDerivatives, lowLatency);
	faceMapper = new FaceMapper(config, sdlDriver, frameDerivatives, faceTracker);
	metrics = new Metrics(config, "YerFace", frameDerivatives, true);
	outputDriver = new OutputDriver(config, outData, frameDerivatives, faceTracker, sdlDriver);
	if(ffmpegDriver->getIsAudioInputPresent()) {
		sphinxDriver = new SphinxDriver(config, frameDerivatives, ffmpegDriver, sdlDriver, outputDriver, lowLatency);
	}
	eventLogger = new EventLogger(config, inEvents, outputDriver, frameDerivatives, from);

	outputDriver->setEventLogger(eventLogger);

	sdlWindowRenderer.window = NULL;
	sdlWindowRenderer.renderer = NULL;
	windowInitializationFailed = false;

	//Create locks.
	if((frameSizeMutex = SDL_CreateMutex()) == NULL) {
		throw runtime_error("Failed creating mutex!");
	}
	if((flipWorkingCompletedMutex = SDL_CreateMutex()) == NULL) {
		throw runtime_error("Failed creating mutex!");
	}

	//Create worker thread.
	if((workerThread = SDL_CreateThread(runCaptureLoop, "CaptureLoop", (void *)NULL)) == NULL) {
		throw runtime_error("Failed spawning worker thread!");
	}

	//Launch event / rendering loop.
	while(sdlDriver->getIsRunning()) {
		SDL_Delay(10);

		if(sdlWindowRenderer.window == NULL && !windowInitializationFailed) {
			YerFace_MutexLock(frameSizeMutex);
			if(!frameSizeValid) {
				YerFace_MutexUnlock(frameSizeMutex);
				continue;
			}
			try {
				sdlWindowRenderer = sdlDriver->createPreviewWindow(frameSize.width, frameSize.height);
			} catch(exception &e) {
				windowInitializationFailed = true;
				logger->error("Uh oh, failed to create a preview window! Got exception: %s", e.what());
				logger->warn("Continuing despite the lack of a preview window.");
			}
			YerFace_MutexUnlock(frameSizeMutex);
		}

		if(frameDerivatives->getCompletedFrameSet()) {
			doRenderPreviewFrame();
			if(sdlWindowRenderer.window != NULL) {
				sdlDriver->doRenderPreviewFrame();
			}
		}

		sdlDriver->doHandleEvents();
	}

	//Join worker thread.
	SDL_WaitThread(workerThread, NULL);

	//Cleanup.
	SDL_DestroyMutex(frameSizeMutex);
	SDL_DestroyMutex(flipWorkingCompletedMutex);

	delete eventLogger;
	if(sphinxDriver != NULL) {
		delete sphinxDriver;
	}
	delete outputDriver;
	delete metrics;
	delete faceMapper;
	delete faceTracker;
	delete frameDerivatives;
	delete ffmpegDriver;
	delete sdlDriver;
	delete logger;
	return 0;
}

int runCaptureLoop(void *ptr) {
	VideoFrame videoFrame;

	ffmpegDriver->rollDemuxerThreads();

	bool didSetFrameSizeValid = false;
	while(sdlDriver->getIsRunning()) {
		if(!sdlDriver->getIsPaused()) {
			if(!ffmpegDriver->waitForNextVideoFrame(&videoFrame)) {
				logger->info("FFmpeg Demuxer thread finished.");
				sdlDriver->setIsRunning(false);
				continue;
			}
			workingFrameNumber++;

			if(!didSetFrameSizeValid) {
				YerFace_MutexLock(frameSizeMutex);
				if(!frameSizeValid) {
					frameSize = videoFrame.frameCV.size();
					frameSizeValid = true;
					didSetFrameSizeValid = true;
				}
				YerFace_MutexUnlock(frameSizeMutex);
			}

			// Start timer
			metrics->startClock();

			frameDerivatives->setWorkingFrame(&videoFrame);
			ffmpegDriver->releaseVideoFrame(videoFrame);

			eventLogger->startNewFrame();

			faceTracker->processCurrentFrame();
			faceMapper->processCurrentFrame();

			metrics->endClock();

			while(sdlDriver->getIsPaused() && sdlDriver->getIsRunning()) {
				SDL_Delay(100);
			}

			YerFace_MutexLock(flipWorkingCompletedMutex);

			frameDerivatives->advanceWorkingFrameToCompleted();
			faceTracker->advanceWorkingToCompleted();
			faceMapper->advanceWorkingToCompleted();
			if(sphinxDriver != NULL) {
				sphinxDriver->advanceWorkingToCompleted();
			}

			eventLogger->handleCompletedFrame();
			outputDriver->handleCompletedFrame();

			//If requested, write image sequence.
			if(previewImgSeq.length() > 0) {
				doRenderPreviewFrame();

				int filenameLength = previewImgSeq.length() + 32;
				char filename[filenameLength];
				snprintf(filename, filenameLength, "%s-%06lu.png", previewImgSeq.c_str(), workingFrameNumber);
				logger->debug("YerFace writing preview frame to %s ...", filename);
				imwrite(filename, frameDerivatives->getCompletedPreviewFrame());
			}

			YerFace_MutexUnlock(flipWorkingCompletedMutex);
		}
	}

	sdlDriver->stopAudioDriverNow();
	ffmpegDriver->stopAudioCallbacksNow();

	if(sphinxDriver != NULL) {
		sphinxDriver->drainPipelineDataNow();
	}
	outputDriver->drainPipelineDataNow();

	return 0;
}

void doRenderPreviewFrame(void) {
	YerFace_MutexLock(flipWorkingCompletedMutex);

	frameDerivatives->resetCompletedPreviewFrame();

	faceTracker->renderPreviewHUD();
	faceMapper->renderPreviewHUD();
	if(sphinxDriver != NULL) {
		sphinxDriver->renderPreviewHUD();
	}

	Mat previewFrame = frameDerivatives->getCompletedPreviewFrame();

	putText(previewFrame, metrics->getTimesString().c_str(), Point(25,50), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0,0,255), 2);
	putText(previewFrame, metrics->getFPSString().c_str(), Point(25,75), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0,0,255), 2);

	YerFace_MutexUnlock(flipWorkingCompletedMutex);
}

void parseConfigFile(void) {
	logger->verbose("Opening and parsing config file: \"%s\"", configFile.c_str());
	std::ifstream fileStream = std::ifstream(configFile);
	if(fileStream.fail()) {
		throw invalid_argument("Specified config file failed to open.");
	}
	std::stringstream ssBuffer;
	ssBuffer << fileStream.rdbuf();
	config = json::parse(ssBuffer.str());
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2021 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <seastar/core/timed_out_error.hh>
#include <seastar/core/coroutine.hh>
#include <seastar/core/future-util.hh>

#include "raft/logical_clock.hh"

using namespace seastar;

constexpr raft::logical_clock::duration operator "" _t(unsigned long long ticks) {
    return raft::logical_clock::duration{ticks};
}

// A wrapper around `raft::logical_clock` that allows scheduling events to happen after a certain number of ticks.
class logical_timer {
    struct scheduled_impl {
    private:
        bool _resolved = false;

        virtual void do_resolve() = 0;

    public:
        virtual ~scheduled_impl() { }

        void resolve() {
            if (!_resolved) {
                do_resolve();
                _resolved = true;
            }
        }

        void mark_resolved() { _resolved = true; }
        bool resolved() { return _resolved; }
    };

    struct scheduled {
        raft::logical_clock::time_point _at;
        seastar::shared_ptr<scheduled_impl> _impl;

        void resolve() { _impl->resolve(); }
        bool resolved() { return _impl->resolved(); }
    };

    raft::logical_clock _clock;

    // A min-heap of `scheduled` events sorted by `_at`.
    std::vector<scheduled> _scheduled;

    // Comparator for the `_scheduled` min-heap.
    static bool cmp(const scheduled& a, const scheduled& b) {
        return a._at > b._at;
    }

public:
    template <typename... T>
    class timed_out : public raft::error {
        // lw_shared_ptr to make the exception copyable
        lw_shared_ptr<seastar::future<T...>> _fut;

    public:
        timed_out(seastar::future<T...> f) : error("timed out"), _fut(make_lw_shared(std::move(f))) {}
        timed_out(const timed_out&) = default;
        timed_out(timed_out&&) = default;

        seastar::future<T...>& get_future() {
            return *_fut;
        }
    };

    logical_timer() = default;

    logical_timer(const logical_timer&) = delete;
    logical_timer(logical_timer&&) = default;

    // Returns the current logical time (number of `tick()`s since initialization).
    raft::logical_clock::time_point now() {
        return _clock.now();
    }

    // Tick the internal clock.
    // Resolve all events whose scheduled times arrive after that tick.
    void tick() {
        _clock.advance();
        while (!_scheduled.empty() && _scheduled.front()._at <= _clock.now()) {
            _scheduled.front().resolve();
            std::pop_heap(_scheduled.begin(), _scheduled.end(), cmp);
            _scheduled.pop_back();
        }
    }

    // Given a future `f` and a logical time point `tp`, returns a future that resolves
    // when either `f` resolves or the time point `tp` arrives (according to the number of `tick()` calls),
    // whichever comes first.
    //
    // If `tp` comes first, the returned future is resolved with the `timed_out` exception.
    // The exception contains a future equivalent to the original future (i.e. when the original future
    // resolves, the future inside the exception will contain the original future's result).
    //
    // Note: it is highly recommended to pass in futures that always resolve eventually
    // since we attach continuations to these futures, allocating memory.
    //
    // Note: there is a possibility that the internal heap will grow unbounded if we call `with_timeout`
    // more often than we `tick`, so don't do that. It is recommended to call at least one
    // `tick` per one `with_timeout` call (on average in the long run).
    template <typename... T>
    future<T...> with_timeout(raft::logical_clock::time_point tp, future<T...> f) {
        if (f.available()) {
            return f;
        }

        if (tp <= now()) {
            return make_exception_future<T...>(timed_out<T...>{std::move(f)});
        }

        struct sched : public scheduled_impl {
            // The original future (the `f` argument), when it resolves, will set value on `_p`.
            // Before timeout, `_p` is connected to the future returned to the user (`res` below).
            // After timeout, `_p` is a new promise connected to the future returned inside the timed_out exception.
            // Therefore:
            // 1. if `f` resolves before timeout, it will resolve `res`,
            // 2. otherwise it will resolve the future we return inside `timed_out` which is returned through `res`.
            promise<T...> _p;

            virtual ~sched() override { }
            virtual void do_resolve() override {
                promise<T...> new_p;
                _p.set_exception(timed_out<T...>{new_p.get_future()});
                std::swap(new_p, _p);
            }
        };

        auto s = make_shared<sched>();
        auto res = s->_p.get_future();

        _scheduled.push_back(scheduled{
            ._at = tp,
            ._impl = s
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);

        (void)f.then_wrapped([s = std::move(s)] (future<T...> f) mutable {
            // If we're before timeout, `s->_p` is connected to `res` so we're returning the result of `f` to our caller.
            // Otherwise `s->_p` is connected to a future which was previously returned to our caller inside `timed_out`.
            f.forward_to(std::move(s->_p));
            s->mark_resolved();
            // tick() will (eventually) clear the `_scheduled` entry
            // (unless we're already past timeout, in which case the entry has already been cleared).
        });

        return res;
    }

    // Returns a future that resolves after a number of `tick()`s represented by `d`.
    // Example usage: `sleep(20_t)` resolves after 20 `tick()`s.
    // Note: analogous remark applies as for `with_timeout`, i.e. make sure to call at least one `tick`
    // per one `sleep` call on average.
    future<> sleep(raft::logical_clock::duration d) {
        if (d == raft::logical_clock::duration{0}) {
            return make_ready_future<>();
        }

        struct sched : public scheduled_impl {
            promise<> _p;
            virtual ~sched() override {}
            virtual void do_resolve() override { _p.set_value(); }
        };

        auto s = make_shared<sched>();
        auto f = s->_p.get_future();
        _scheduled.push_back(scheduled{
            ._at = now() + d,
            ._impl = std::move(s)
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);

        return f;
    }

    // Schedule `f` to be called at logical time point `tp` (according to this timer's clock).
    template <typename F>
    void schedule(raft::logical_clock::time_point tp, F f) {
        if (tp <= now()) {
            f();
            return;
        }

        struct sched : public scheduled_impl {
            sched(F f) : _f(std::move(f)) {}
            virtual ~sched() override {}
            virtual void do_resolve() override { _f(); }

            F _f;
        };

        _scheduled.push_back(scheduled {
            ._at = tp,
            ._impl = make_shared<sched>(std::move(f))
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);
    }
};

<commit_msg>test: raft: logical_timer: introduce `sleep_until`<commit_after>/*
 * Copyright (C) 2021 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <seastar/core/timed_out_error.hh>
#include <seastar/core/coroutine.hh>
#include <seastar/core/future-util.hh>

#include "raft/logical_clock.hh"

using namespace seastar;

constexpr raft::logical_clock::duration operator "" _t(unsigned long long ticks) {
    return raft::logical_clock::duration{ticks};
}

// A wrapper around `raft::logical_clock` that allows scheduling events to happen after a certain number of ticks.
class logical_timer {
    struct scheduled_impl {
    private:
        bool _resolved = false;

        virtual void do_resolve() = 0;

    public:
        virtual ~scheduled_impl() { }

        void resolve() {
            if (!_resolved) {
                do_resolve();
                _resolved = true;
            }
        }

        void mark_resolved() { _resolved = true; }
        bool resolved() { return _resolved; }
    };

    struct scheduled {
        raft::logical_clock::time_point _at;
        seastar::shared_ptr<scheduled_impl> _impl;

        void resolve() { _impl->resolve(); }
        bool resolved() { return _impl->resolved(); }
    };

    raft::logical_clock _clock;

    // A min-heap of `scheduled` events sorted by `_at`.
    std::vector<scheduled> _scheduled;

    // Comparator for the `_scheduled` min-heap.
    static bool cmp(const scheduled& a, const scheduled& b) {
        return a._at > b._at;
    }

public:
    template <typename... T>
    class timed_out : public raft::error {
        // lw_shared_ptr to make the exception copyable
        lw_shared_ptr<seastar::future<T...>> _fut;

    public:
        timed_out(seastar::future<T...> f) : error("timed out"), _fut(make_lw_shared(std::move(f))) {}
        timed_out(const timed_out&) = default;
        timed_out(timed_out&&) = default;

        seastar::future<T...>& get_future() {
            return *_fut;
        }
    };

    logical_timer() = default;

    logical_timer(const logical_timer&) = delete;
    logical_timer(logical_timer&&) = default;

    // Returns the current logical time (number of `tick()`s since initialization).
    raft::logical_clock::time_point now() {
        return _clock.now();
    }

    // Tick the internal clock.
    // Resolve all events whose scheduled times arrive after that tick.
    void tick() {
        _clock.advance();
        while (!_scheduled.empty() && _scheduled.front()._at <= _clock.now()) {
            _scheduled.front().resolve();
            std::pop_heap(_scheduled.begin(), _scheduled.end(), cmp);
            _scheduled.pop_back();
        }
    }

    // Given a future `f` and a logical time point `tp`, returns a future that resolves
    // when either `f` resolves or the time point `tp` arrives (according to the number of `tick()` calls),
    // whichever comes first.
    //
    // If `tp` comes first, the returned future is resolved with the `timed_out` exception.
    // The exception contains a future equivalent to the original future (i.e. when the original future
    // resolves, the future inside the exception will contain the original future's result).
    //
    // Note: it is highly recommended to pass in futures that always resolve eventually
    // since we attach continuations to these futures, allocating memory.
    //
    // Note: there is a possibility that the internal heap will grow unbounded if we call `with_timeout`
    // more often than we `tick`, so don't do that. It is recommended to call at least one
    // `tick` per one `with_timeout` call (on average in the long run).
    template <typename... T>
    future<T...> with_timeout(raft::logical_clock::time_point tp, future<T...> f) {
        if (f.available()) {
            return f;
        }

        if (tp <= now()) {
            return make_exception_future<T...>(timed_out<T...>{std::move(f)});
        }

        struct sched : public scheduled_impl {
            // The original future (the `f` argument), when it resolves, will set value on `_p`.
            // Before timeout, `_p` is connected to the future returned to the user (`res` below).
            // After timeout, `_p` is a new promise connected to the future returned inside the timed_out exception.
            // Therefore:
            // 1. if `f` resolves before timeout, it will resolve `res`,
            // 2. otherwise it will resolve the future we return inside `timed_out` which is returned through `res`.
            promise<T...> _p;

            virtual ~sched() override { }
            virtual void do_resolve() override {
                promise<T...> new_p;
                _p.set_exception(timed_out<T...>{new_p.get_future()});
                std::swap(new_p, _p);
            }
        };

        auto s = make_shared<sched>();
        auto res = s->_p.get_future();

        _scheduled.push_back(scheduled{
            ._at = tp,
            ._impl = s
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);

        (void)f.then_wrapped([s = std::move(s)] (future<T...> f) mutable {
            // If we're before timeout, `s->_p` is connected to `res` so we're returning the result of `f` to our caller.
            // Otherwise `s->_p` is connected to a future which was previously returned to our caller inside `timed_out`.
            f.forward_to(std::move(s->_p));
            s->mark_resolved();
            // tick() will (eventually) clear the `_scheduled` entry
            // (unless we're already past timeout, in which case the entry has already been cleared).
        });

        return res;
    }

    // Returns a future that resolves at logical time point `tp` (according to this timer's clock).
    // Note: analogous remark applies as for `with_timeout`, i.e. make sure to call at least one `tick`
    // per one `sleep_until` call on average.
    future<> sleep_until(raft::logical_clock::time_point tp) {
        if (tp <= now()) {
            return make_ready_future<>();
        }

        struct sched : public scheduled_impl {
            promise<> _p;
            virtual ~sched() override {}
            virtual void do_resolve() override { _p.set_value(); }
        };

        auto s = make_shared<sched>();
        auto f = s->_p.get_future();
        _scheduled.push_back(scheduled{
            ._at = tp,
            ._impl = std::move(s)
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);

        return f;
    }

    // Returns a future that resolves after a number of `tick()`s represented by `d`.
    // Example usage: `sleep(20_t)` resolves after 20 `tick()`s.
    // Note: analogous remark applies as for `with_timeout`, i.e. make sure to call at least one `tick`
    // per one `sleep` call on average.
    future<> sleep(raft::logical_clock::duration d) {
        return sleep_until(now() + d);
    }

    // Schedule `f` to be called at logical time point `tp` (according to this timer's clock).
    template <typename F>
    void schedule(raft::logical_clock::time_point tp, F f) {
        if (tp <= now()) {
            f();
            return;
        }

        struct sched : public scheduled_impl {
            sched(F f) : _f(std::move(f)) {}
            virtual ~sched() override {}
            virtual void do_resolve() override { _f(); }

            F _f;
        };

        _scheduled.push_back(scheduled {
            ._at = tp,
            ._impl = make_shared<sched>(std::move(f))
        });
        std::push_heap(_scheduled.begin(), _scheduled.end(), cmp);
    }
};

<|endoftext|>


<commit_before>#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/alert_types.hpp"
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/filesystem/operations.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

using boost::filesystem::remove_all;
using boost::filesystem::exists;

void test_swarm()
{
	using namespace libtorrent;

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48010, 49000), "0.0.0.0", 0);
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49010, 50000), "0.0.0.0", 0);
	session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50010, 51000), "0.0.0.0", 0);

	ses1.set_severity_level(alert::debug);
	ses2.set_severity_level(alert::debug);
	ses3.set_severity_level(alert::debug);

	// this is to avoid everything finish from a single peer
	// immediately. To make the swarm actually connect all
	// three peers before finishing.
	float rate_limit = 100000;
	ses1.set_upload_rate_limit(int(rate_limit));
	ses1.set_max_uploads(1);
	ses2.set_download_rate_limit(int(rate_limit / 5));
	ses3.set_download_rate_limit(int(rate_limit / 5));
	ses2.set_upload_rate_limit(int(rate_limit / 10));
	ses3.set_upload_rate_limit(int(rate_limit / 10));

	session_settings settings;
	settings.allow_multiple_connections_per_ip = true;
	settings.ignore_limits_on_local_network = false;
	settings.auto_upload_slots = true;
	settings.auto_upload_slots_rate_based = false;
	ses1.set_settings(settings);
	ses2.set_settings(settings);
	ses3.set_settings(settings);

#ifndef TORRENT_DISABLE_ENCRYPTION
	pe_settings pes;
	pes.out_enc_policy = pe_settings::forced;
	pes.in_enc_policy = pe_settings::forced;
	ses1.set_pe_settings(pes);
	ses2.set_pe_settings(pes);
	ses3.set_pe_settings(pes);
#endif

	torrent_handle tor1;
	torrent_handle tor2;
	torrent_handle tor3;

	boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, true, "_unchoke");

	session_status st = ses1.status();
	TEST_CHECK(st.allowed_upload_slots == 1);
	for (int i = 0; i < 50; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");
		print_alerts(ses3, "ses3");

		st = ses1.status();
		if (st.allowed_upload_slots >= 2) break;

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();
		torrent_status st3 = tor3.status();

		std::cerr
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< st1.num_peers << " " << st.allowed_upload_slots << ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers << " - "
			<< "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st3.progress * 100) << "% "
			<< st3.num_peers
			<< std::endl;

		test_sleep(1000);
	}

	TEST_CHECK(st.allowed_upload_slots == 2);

	// make sure the files are deleted
	ses1.remove_torrent(tor1, session::delete_files);
	ses2.remove_torrent(tor2, session::delete_files);
	ses3.remove_torrent(tor3, session::delete_files);
}

int test_main()
{
	using namespace libtorrent;
	using namespace boost::filesystem;

	// in case the previous run was terminated
	try { remove_all("./tmp1_unchoke"); } catch (std::exception&) {}
	try { remove_all("./tmp2_unchoke"); } catch (std::exception&) {}
	try { remove_all("./tmp3_unchoke"); } catch (std::exception&) {}

	test_swarm();

	test_sleep(2000);
	TEST_CHECK(!exists("./tmp1_unchoke/temporary"));
	TEST_CHECK(!exists("./tmp2_unchoke/temporary"));
	TEST_CHECK(!exists("./tmp3_unchoke/temporary"));

	remove_all("./tmp1_unchoke");
	remove_all("./tmp2_unchoke");
	remove_all("./tmp3_unchoke");

	return 0;
}

<commit_msg>fixed auto unchoke test<commit_after>#include "libtorrent/session.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/alert_types.hpp"
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/filesystem/operations.hpp>

#include "test.hpp"
#include "setup_transfer.hpp"

using boost::filesystem::remove_all;
using boost::filesystem::exists;

void test_swarm()
{
	using namespace libtorrent;

	session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48010, 49000), "0.0.0.0", 0);
	session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49010, 50000), "0.0.0.0", 0);
	session ses3(fingerprint("LT", 0, 1, 0, 0), std::make_pair(50010, 51000), "0.0.0.0", 0);

	ses1.set_severity_level(alert::debug);
	ses2.set_severity_level(alert::debug);
	ses3.set_severity_level(alert::debug);

	// this is to avoid everything finish from a single peer
	// immediately. To make the swarm actually connect all
	// three peers before finishing.
	float rate_limit = 100000;
	ses1.set_upload_rate_limit(int(rate_limit));
	ses1.set_max_uploads(1);
	ses2.set_download_rate_limit(int(rate_limit / 5));
	ses3.set_download_rate_limit(int(rate_limit / 5));
	ses2.set_upload_rate_limit(int(rate_limit / 10));
	ses3.set_upload_rate_limit(int(rate_limit / 10));

	session_settings settings;
	settings.allow_multiple_connections_per_ip = true;
	settings.ignore_limits_on_local_network = false;
	settings.auto_upload_slots = true;
	settings.auto_upload_slots_rate_based = false;
	ses1.set_settings(settings);
	ses2.set_settings(settings);
	ses3.set_settings(settings);

#ifndef TORRENT_DISABLE_ENCRYPTION
	pe_settings pes;
	pes.out_enc_policy = pe_settings::forced;
	pes.in_enc_policy = pe_settings::forced;
	ses1.set_pe_settings(pes);
	ses2.set_pe_settings(pes);
	ses3.set_pe_settings(pes);
#endif

	torrent_handle tor1;
	torrent_handle tor2;
	torrent_handle tor3;

	boost::tie(tor1, tor2, tor3) = setup_transfer(&ses1, &ses2, &ses3, true, false, true, "_unchoke");

	session_status st = ses1.status();
	TEST_CHECK(st.allowed_upload_slots == 1);
	for (int i = 0; i < 50; ++i)
	{
		print_alerts(ses1, "ses1");
		print_alerts(ses2, "ses2");
		print_alerts(ses3, "ses3");

		st = ses1.status();
		std::cerr << st.allowed_upload_slots << " ";
		if (st.allowed_upload_slots >= 2) break;

		torrent_status st1 = tor1.status();
		torrent_status st2 = tor2.status();
		torrent_status st3 = tor3.status();

		std::cerr
			<< "\033[33m" << int(st1.upload_payload_rate / 1000.f) << "kB/s "
			<< st1.num_peers << " " << st.allowed_upload_slots << ": "
			<< "\033[32m" << int(st2.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st2.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st2.progress * 100) << "% "
			<< st2.num_peers << " - "
			<< "\033[32m" << int(st3.download_payload_rate / 1000.f) << "kB/s "
			<< "\033[31m" << int(st3.upload_payload_rate / 1000.f) << "kB/s "
			<< "\033[0m" << int(st3.progress * 100) << "% "
			<< st3.num_peers
			<< std::endl;

		test_sleep(1000);
	}

	TEST_CHECK(st.allowed_upload_slots >= 2);

	// make sure the files are deleted
	ses1.remove_torrent(tor1, session::delete_files);
	ses2.remove_torrent(tor2, session::delete_files);
	ses3.remove_torrent(tor3, session::delete_files);
}

int test_main()
{
	using namespace libtorrent;
	using namespace boost::filesystem;

	// in case the previous run was terminated
	try { remove_all("./tmp1_unchoke"); } catch (std::exception&) {}
	try { remove_all("./tmp2_unchoke"); } catch (std::exception&) {}
	try { remove_all("./tmp3_unchoke"); } catch (std::exception&) {}

	test_swarm();

	test_sleep(2000);
	TEST_CHECK(!exists("./tmp1_unchoke/temporary"));
	TEST_CHECK(!exists("./tmp2_unchoke/temporary"));
	TEST_CHECK(!exists("./tmp3_unchoke/temporary"));

	remove_all("./tmp1_unchoke");
	remove_all("./tmp2_unchoke");
	remove_all("./tmp3_unchoke");

	return 0;
}

<|endoftext|>


<commit_before>/******************************************************************************
** Copyright (c) 2015, Intel Corporation                                     **
** 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 of the copyright holder nor the names of its          **
**    contributors may be used to endorse or promote products derived        **
**    from this software without specific prior written permission.          **
**                                                                           **
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       **
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         **
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     **
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      **
** HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,    **
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  **
** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR    **
** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    **
** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      **
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        **
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ******************************************************************************/
/* Narayanan Sundaram (Intel Corp.)
 * ******************************************************************************/

#include "catch.hpp"
#include "generator.h"
#include <algorithm>
#include <climits>
#include "GraphMatRuntime.h"

class custom_vertex_type {
  public:
    int  iprop;
    float fprop;
  public:
    custom_vertex_type() {
      iprop = 0;
      fprop = 0.0f;
    }
  friend std::ostream &operator<<(std::ostream &outstream, const custom_vertex_type & val)
    {
      outstream << val.iprop << val.fprop;
      return outstream;
    }
};

void test_graph_getset(int n) {
  auto E = generate_random_edgelist<int>(n, 16);
  GraphMat::Graph<custom_vertex_type> G;
  G.ReadEdgelist(E);
  E.clear();

  REQUIRE(G.getNumberOfVertices() == n);
  REQUIRE(G.nnz == n*16);

  for (int i = 1; i <= n; i++) {
    if (G.vertexNodeOwner(i)) {
      custom_vertex_type v;
      v.iprop = i;
      v.fprop = i*2.5;
      G.setVertexproperty(i, v);
    }
  }

  for (int i = 1; i <= n; i++) {
    if (G.vertexNodeOwner(i)) {
      REQUIRE(G.getVertexproperty(i).iprop == i);
      REQUIRE(G.getVertexproperty(i).fprop == Approx(2.5*i));
    }
  }

}

void test_graph_size(int n) {
  {
    auto E = generate_random_edgelist<int>(n, 16);
    GraphMat::Graph<custom_vertex_type> G;
    G.ReadEdgelist(E);
    E.clear();

    REQUIRE(G.getNumberOfVertices() == n);
    REQUIRE(G.nnz == n*16);
  }

  {
    auto E = generate_dense_edgelist<int>(n);
    GraphMat::Graph<custom_vertex_type> G;
    G.ReadEdgelist(E);
    E.clear();

    REQUIRE(G.getNumberOfVertices() == n);
    REQUIRE(G.nnz == n*n);
  }

  {
    auto E = generate_upper_triangular_edgelist<int>(n);
    GraphMat::Graph<custom_vertex_type> G;
    G.ReadEdgelist(E);
    E.clear();

    REQUIRE(G.getNumberOfVertices() == n);
    REQUIRE(G.nnz == n*(n-1)/2);
  }
}

void test_graph_io(int n) {
    auto E = generate_random_edgelist<int>(n, 16);
    GraphMat::random_edge_weights(&E, 128);
    GraphMat::Graph<custom_vertex_type> G;
    G.ReadEdgelist(E);

    GraphMat::edgelist_t<int> E2, E3, E4;
    G.getEdgelist(E2);

    collect_edges(E, E3);
    std::sort(E3.edges, E3.edges + E3.nnz, edge_compare<int>);
    collect_edges(E2, E4);
    std::sort(E4.edges, E4.edges + E4.nnz, edge_compare<int>);

    REQUIRE(E3.nnz == E4.nnz);
    for (int i = 0; i < E3.nnz; i++) {
      REQUIRE(E3.edges[i].src == E4.edges[i].src);
      REQUIRE(E3.edges[i].dst == E4.edges[i].dst);
      REQUIRE(E3.edges[i].val == E4.edges[i].val);
    }

    E.clear();
    E2.clear();
    E3.clear();
    E4.clear();
}

TEST_CASE("Graph tests", "[random]")
{
  SECTION("test get set", "[get][set]") {
    test_graph_getset(500);
    test_graph_getset(1000);
  }
  SECTION("test size", "[size]") {
    test_graph_size(50);
    test_graph_size(500);
    test_graph_size(1000);
  }
  SECTION("test edgelist in/out", "[io]") {
    test_graph_io(500);
  }
}
<commit_msg>update to test code<commit_after>/******************************************************************************
** Copyright (c) 2015, Intel Corporation                                     **
** 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 of the copyright holder nor the names of its          **
**    contributors may be used to endorse or promote products derived        **
**    from this software without specific prior written permission.          **
**                                                                           **
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       **
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         **
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     **
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      **
** HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,    **
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  **
** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR    **
** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    **
** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      **
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        **
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ******************************************************************************/
/* Narayanan Sundaram (Intel Corp.)
 * ******************************************************************************/

#include "catch.hpp"
#include "generator.h"
#include <algorithm>
#include <climits>
#include "GraphMatRuntime.h"

class custom_vertex_type {
  public:
    int  iprop;
    float fprop;
  public:
    custom_vertex_type() {
      iprop = 0;
      fprop = 0.0f;
    }
  friend std::ostream &operator<<(std::ostream &outstream, const custom_vertex_type & val)
    {
      outstream << val.iprop << val.fprop;
      return outstream;
    }
};

void test_graph_getset(int n) {
  auto E = generate_random_edgelist<int>(n, 16);
  GraphMat::Graph<custom_vertex_type> G;
  G.ReadEdgelist(E);
  E.clear();

  REQUIRE(G.getNumberOfVertices() == n);
  REQUIRE(G.nnz == n*16);

  for (int i = 1; i <= n; i++) {
    if (G.vertexNodeOwner(i)) {
      custom_vertex_type v;
      v.iprop = i;
      v.fprop = i*2.5;
      G.setVertexproperty(i, v);
    }
  }

  for (int i = 1; i <= n; i++) {
    if (G.vertexNodeOwner(i)) {
      REQUIRE(G.getVertexproperty(i).iprop == i);
      REQUIRE(G.getVertexproperty(i).fprop == Approx(2.5*i));
    }
  }

}

void test_graph_size(int n) {
  {
    auto E = generate_random_edgelist<int>(n, 16);
    GraphMat::Graph<custom_vertex_type> G;
    G.ReadEdgelist(E);
    E.clear();

    REQUIRE(G.getNumberOfVertices() == n);
    REQUIRE(G.nnz == n*16);
  }

  {
    auto E = generate_dense_edgelist<int>(n);
    GraphMat::Graph<custom_vertex_type> G;
    G.ReadEdgelist(E);
    E.clear();

    REQUIRE(G.getNumberOfVertices() == n);
    REQUIRE(G.nnz == n*n);
  }

  {
    auto E = generate_upper_triangular_edgelist<int>(n);
    GraphMat::Graph<custom_vertex_type> G;
    G.ReadEdgelist(E);
    E.clear();

    REQUIRE(G.getNumberOfVertices() == n);
    REQUIRE(G.nnz == n*(n-1)/2);
  }
}

void test_graph_io(int n) {
    auto E = generate_random_edgelist<int>(n, 16);
    GraphMat::random_edge_weights(&E, 128);

    GraphMat::edgelist_t<int> E1(E.m, E.n, E.nnz);
    memcpy(E1.edges, E.edges, E.nnz*sizeof(GraphMat::edge_t<int>));

    GraphMat::Graph<custom_vertex_type> G;
    G.ReadEdgelist(E);
    E.clear();

    GraphMat::edgelist_t<int> E2, E3, E4;
    G.getEdgelist(E2);

    collect_edges(E1, E3);
    std::sort(E3.edges, E3.edges + E3.nnz, edge_compare<int>);
    collect_edges(E2, E4);
    std::sort(E4.edges, E4.edges + E4.nnz, edge_compare<int>);

    REQUIRE(E3.nnz == E4.nnz);
    for (int i = 0; i < E3.nnz; i++) {
      REQUIRE(E3.edges[i].src == E4.edges[i].src);
      REQUIRE(E3.edges[i].dst == E4.edges[i].dst);
      REQUIRE(E3.edges[i].val == E4.edges[i].val);
    }

    E1.clear();
    E2.clear();
    E3.clear();
    E4.clear();
}

TEST_CASE("Graph tests", "[random]")
{
  SECTION("test get set", "[get][set]") {
    test_graph_getset(500);
    test_graph_getset(1000);
  }
  SECTION("test size", "[size]") {
    test_graph_size(50);
    test_graph_size(500);
    test_graph_size(1000);
  }
  SECTION("test edgelist in/out", "[io]") {
    test_graph_io(500);
  }
}
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------//
//arquivo referencia para alguns tópicos de C/C++, tratanto temas referentes a:
//structs, ponteiros, operadores de ponteiros (*, &, ->), entrada e saída.
//a linguagem C/C++ é uma linguagem "top-down", então tudo que você usa nela
//deve ter sido implementado (ou ao menos declarado) antes da utilização.
//----------------------------------------------------------------------------//

#include <iostream>       //biblioteca para entrada e saida (cin e cout)
#include <string>         //biblioteca para utilizar strings
#include <stack>          //biblioteca para utilizar a estrutura de dados pilha
#include <cstdlib>        //biblioteca da linguagem C, que contém o random()

using namespace std;      //"torna os simbolos das bibliotecas padrões visíveis"

//----------------------------------------------------------------------------//
//IMPLEMENTAÇÃO DOS CONJUNTOS E FUNÇÕES QUE OPERAM SOBRE ELES-----------------//
//----------------------------------------------------------------------------//
string operators[4] = {"*", "-", "+", "/"};  //array que contém os operadores
string terminators[4] = {"x", "y", "1.0", "2.0"};  //array dos terminais

//1o mandamento do fabrício: nomes de variáveis e métodos: ou tudo em inglês
//                           ou tudo em português.
//2o mandamento do fabrício: nomes de métodos -> usar ações.
string get_random_operator(){
    //função que retorna um operador aleatório.

    return operators[random()%4];
}

string get_random_terminator(){
    //função que retorna um terminal aleatório.

    return terminators[random()%4];
}

string get_random_everything(){
    //função que retorna um elemento de qualquer um dos conjuntos. para evitar
    //arvores monstruosas, a probabilidade de retornar um terminal é maior.

    if (random()%10>7)
        return get_random_operator();
    else
        return get_random_terminator();
}

//----------------------------------------------------------------------------//
//IMPLEMENTAÇÃO DO NOVO TIPO DE DADOS "NODE"----------------------------------//
//----------------------------------------------------------------------------//
typedef struct node {

    //defino aqui uma estrutura chamada node, que tem ponteiros para seus filhos.
    //o typedef elimina a necessidade de declarar variaveis do tipo node usando
    //o prefixo struct. Uma struct é uma declaração de um novo tipo primitivo de
    //dados. Structs são "precursores" de classes: classes possuem métodos e
    //atributos, enquanto structs possuem apenas atributos. A vantagem de traba_
    //lhar com structs agora é que nos livramos de complicações com coisas espe_
    //cíficas de classes, como: construtor, destrutor, encapsulamento, etc.

    string value;

    //um ponteiro para uma variável é declarado:
    //  <tipo de variável> *<id>
    //para acessar os atributos de uma struct, usamos:
    //  <nome-struct>.<nome-atributo>
    //Quando nossa struct é na verdade um ponteiro para uma struct, é necessário
    //resolver o endereço antes:
    //  *<nome-struct>.<nome-atributo>
    //ou usar o operador "->":
    //  <nome-struct>-><nome-atributo>
    //resumindo:
    //  O operador '&' retorna o endereço da variável;
    //  O operador '*' quando usado na declaração, cria um ponteiro para o tipo
    //      de variável declarada.
    //  O operador '*' quando usado como prefixo numa variável resolve o
    //      endereço daquela variável (a variável deve ser um ponteiro)
    //  O operador '->' é usado para omitir o operador '*' no acesso de atribu_
    //      tos de structs (e classes)
    //OBS: um array[] é sempre um ponteiro implícito.

    //OBS 2: (me disseram que) é uma boa prática, sempre que criar ponteiros,
    //inicializá-los com "NULL".
    node *leftChild = NULL;
    node *rightChild = NULL;

} node;

//3o mandamento do fabrício: métodos de debug tem um sufixo "_d".
void print_node_d(node *root){

    //função que imprime um nó recursivamente.

    //Aqui um exemplo de como eu pensei para percorrer as árvores binárias:
    //como cada nó guarda uma referencia para seus dois filhos, acessar a árvore
    //de forma recursiva elimina a necessidade de uma referencia para o pai, e
    //pode ser feito de forma simples, já que a chamada fica aberta até terminar
    //as chamadas recursivas.

    //OBS: uma passagem de parâmetros por referencia em C/C++ é feita passando
    //o ponteiro que aponta para a região de memória da variável. Então, neste
    //caso, está sendo passado o endereço da raiz da árvore.

    cout << "(";
    if (root->leftChild)
        print_node_d(root->leftChild);

    cout << root->value;

    if (root->rightChild)
        print_node_d(root->rightChild);

    cout << ")";
}

node *generate_tree(){

    //essa função é um "construtor": ela cria uma árvore de expressão e retorna
    //um ponteiro para seu endereço na memória.

    //declarando um node na heap. o operador new retorna um endereço de memória,
    //então seu retorno deve ser armazenado num ponteiro para o tipo de variável.
    //o operador new pode acabar retornando NULL caso o SO não forneça nenhuma
    //memória para o programa, então é bom fazer uma verificação no ponteiro
    //antes de começar a manipulá-lo.
    node *root = new node;

    //um ponteiro quando usado como operação lógica retorna verdadeiro caso
    //seja diferente de NULL;
    if (!root)
        return NULL;

    root->value = get_random_everything();

    if (root->value=="+"||root->value=="-"||root->value=="*"||root->value=="/"){

        //se for criado um terminal, a construção para por aí, caso contrário,
        //se for um operador, é necessário ter 2 números para operar em cima,
        //então, chamo recursivamente o construtor para os ponteiros dos filhos

        root->leftChild = generate_tree();
        root->rightChild = generate_tree();
    }
    return root;
}

void destruct_tree(node *root){

    //essa função funciona como um "destrutor". quando alocamos memória para uma
    //variável devemos sempre liberá-la após o uso. em Java, o garbage collector
    //faz esse papel, mas em C/C++ o programador precisa ter esse controle.
    //todo new deve casar com um delete.
    //Essa função também opera sobre a árvore recursivamente, então quando
    //um nó é passado para ela, este nó e todos os seus filhos são deletados.
    //é importante ressaltar também que é necessário passar o mesmo ponteiro
    //que foi retornado pelo new para o delete. caso vc faça:
    //  int *i = new int;
    //  i++;
    //  delete i;
    //seu programa explode, pois vc incrementou o endereço e tentou deletar
    //um endereço diferente do original.

    if (root->leftChild)
        destruct_tree(root->leftChild);
    if (root->rightChild)
        destruct_tree(root->rightChild);
    delete root;
}

//----------------------------------------------------------------------------//
//MÉTODO PRINCIPAL -----------------------------------------------------------//
//----------------------------------------------------------------------------//
int main(){

    int i;

    //declarando um array de nós na stack. a stack é um pedaço de memória muito
    //limitado que é alocado para o programa quando ele começa a executar.
    //como as árvores podem assumir grandes tamanhos e pode ser interessante
    //ter grandes populações, as vezes é necessário mais memória do que a stack
    //pode oferecer, sendo necessário usar o operador new.
    node *pop[15];

    //colocando o horário do computador como seed do random, para que o compor_
    //tamento do código não seja sempre igual em todas as execuções (os números
    //gerados pelo random() são pseudo-aleatórios).
    srand(time(NULL));

    //percorro o array inteiro da população, inicializando cada indivíduo.
    for (i=0; i<15; i++)
        pop[i] = generate_tree();

    //imprimo todos os indivíduos que foram criados na população inicial
    for (i=0; i<15; i++){
        print_node_d(pop[i]);
        cout << endl;
    }

    //libero a memoria alocada. todo new deve casar com um delete!
    for (i=0; i<15; i++)
        destruct_tree(pop[i]);

    return 0;
}
<commit_msg>Update main.cpp<commit_after>//----------------------------------------------------------------------------//
//arquivo referencia para alguns tópicos de C/C++, tratanto temas referentes a:
//structs, ponteiros, operadores de ponteiros (*, &, ->), entrada e saída.
//a linguagem C/C++ é uma linguagem "top-down", então tudo que você usa nela
//deve ter sido implementado (ou ao menos declarado) antes da utilização.
//----------------------------------------------------------------------------//

#include <iostream>       //biblioteca para entrada e saida (cin e cout)
#include <string>         //biblioteca para utilizar strings
#include <stack>          //biblioteca para utilizar a estrutura de dados pilha
#include <cstdlib>        //biblioteca da linguagem C, que contém o random()

using namespace std;      //"torna os simbolos das bibliotecas padrões visíveis"

//----------------------------------------------------------------------------//
//IMPLEMENTAÇÃO DOS CONJUNTOS E FUNÇÕES QUE OPERAM SOBRE ELES-----------------//
//----------------------------------------------------------------------------//
string operators[4] = {"*", "-", "+", "/"};  //array que contém os operadores
string terminators[4] = {"x", "y", "1.0", "2.0"};  //array dos terminais

//1o mandamento do fabrício: nomes de variáveis e métodos: ou tudo em inglês
//                           ou tudo em português.
//2o mandamento do fabrício: nomes de métodos -> usar ações.
string get_random_operator(){
    //função que retorna um operador aleatório.

    return operators[random()%4];
}

string get_random_terminator(){
    //função que retorna um terminal aleatório.

    return terminators[random()%4];
}

string get_random_everything(){
    //função que retorna um elemento de qualquer um dos conjuntos. para evitar
    //arvores monstruosas, a probabilidade de retornar um terminal é maior.

    if (random()%10>7)
        return get_random_operator();
    else
        return get_random_terminator();
}

//----------------------------------------------------------------------------//
//IMPLEMENTAÇÃO DO NOVO TIPO DE DADOS "NODE"----------------------------------//
//----------------------------------------------------------------------------//
typedef struct node {

    //defino aqui uma estrutura chamada node, que tem ponteiros para seus filhos.
    //o typedef elimina a necessidade de declarar variaveis do tipo node usando
    //o prefixo struct. Uma struct é uma declaração de um novo tipo primitivo de
    //dados. Structs são "precursores" de classes: classes possuem métodos e
    //atributos, enquanto structs possuem apenas atributos. A vantagem de traba_
    //lhar com structs agora é que nos livramos de complicações com coisas espe_
    //cíficas de classes, como: construtor, destrutor, encapsulamento, etc.

    string value;

    //um ponteiro para uma variável é declarado:
    //  <tipo de variável> *<id>
    //para acessar os atributos de uma struct, usamos:
    //  <nome-struct>.<nome-atributo>
    //Quando nossa struct é na verdade um ponteiro para uma struct, é necessário
    //resolver o endereço antes:
    //  *<nome-struct>.<nome-atributo>
    //ou usar o operador "->":
    //  <nome-struct>-><nome-atributo>
    //resumindo:
    //  O operador '&' retorna o endereço da variável;
    //  O operador '*' quando usado na declaração, cria um ponteiro para o tipo
    //      de variável declarada.
    //  O operador '*' quando usado como prefixo numa variável resolve o
    //      endereço daquela variável (a variável deve ser um ponteiro)
    //  O operador '->' é usado para omitir o operador '*' no acesso de atribu_
    //      tos de structs (e classes)
    //OBS: um array[] é sempre um ponteiro implícito.

    //OBS 2: (me disseram que) é uma boa prática, sempre que criar ponteiros,
    //inicializá-los com "NULL".

    node *leftChild = NULL;
    node *rightChild = NULL;

} node;

//3o mandamento do fabrício: métodos de debug tem um sufixo "_d".
void print_node_d(node *root){

    //função que imprime um nó recursivamente.

    //Aqui um exemplo de como eu pensei para percorrer as árvores binárias:
    //como cada nó guarda uma referencia para seus dois filhos, acessar a árvore
    //de forma recursiva elimina a necessidade de uma referencia para o pai, e
    //pode ser feito de forma simples, já que a chamada fica aberta até terminar
    //as chamadas recursivas.

    //OBS: uma passagem de parâmetros por referencia em C/C++ é feita passando
    //o ponteiro que aponta para a região de memória da variável. Então, neste
    //caso, está sendo passado o endereço da raiz da árvore.

    if (root->leftChild){
        cout << "(";
        print_node_d(root->leftChild);
    }

    cout << root->value;

    if (root->rightChild){
        print_node_d(root->rightChild);
        cout << ")";
    }
}

node *generate_tree(){

    //essa função é um "construtor": ela cria uma árvore de expressão e retorna
    //um ponteiro para seu endereço na memória.

    //declarando um node na heap. o operador new retorna um endereço de memória,
    //então seu retorno deve ser armazenado num ponteiro para o tipo de variável.
    //o operador new pode acabar retornando NULL caso o SO não forneça nenhuma
    //memória para o programa, então é bom fazer uma verificação no ponteiro
    //antes de começar a manipulá-lo.
    node *root = new node;

    //um ponteiro quando usado como operação lógica retorna verdadeiro caso
    //seja diferente de NULL;
    if (!root)
        return NULL;

    root->value = get_random_everything();

    if (root->value=="+"||root->value=="-"||root->value=="*"||root->value=="/"){

        //se for criado um terminal, a construção para por aí, caso contrário,
        //se for um operador, é necessário ter 2 números para operar em cima,
        //então, chamo recursivamente o construtor para os ponteiros dos filhos

        root->leftChild = generate_tree();
        root->rightChild = generate_tree();
    }
    return root;
}

void destruct_tree(node *root){

    //essa função funciona como um "destrutor". quando alocamos memória para uma
    //variável devemos sempre liberá-la após o uso. em Java, o garbage collector
    //faz esse papel, mas em C/C++ o programador precisa ter esse controle.
    //todo new deve casar com um delete.
    //Essa função também opera sobre a árvore recursivamente, então quando
    //um nó é passado para ela, este nó e todos os seus filhos são deletados.
    //é importante ressaltar também que é necessário passar o mesmo ponteiro
    //que foi retornado pelo new para o delete. caso vc faça:
    //  int *i = new int;
    //  i++;
    //  delete i;
    //seu programa explode, pois vc incrementou o endereço e tentou deletar
    //um endereço diferente do original.

    if (root->leftChild)
        destruct_tree(root->leftChild);
    if (root->rightChild)
        destruct_tree(root->rightChild);
    delete root;
}

//----------------------------------------------------------------------------//
//MÉTODO PRINCIPAL -----------------------------------------------------------//
//----------------------------------------------------------------------------//
int main(){

    int i;

    //declarando um array de nós na stack. a stack é um pedaço de memória muito
    //limitado que é alocado para o programa quando ele começa a executar.
    //como as árvores podem assumir grandes tamanhos e pode ser interessante
    //ter grandes populações, as vezes é necessário mais memória do que a stack
    //pode oferecer, sendo necessário usar o operador new.
    node *pop[15];

    //colocando o horário do computador como seed do random, para que o compor_
    //tamento do código não seja sempre igual em todas as execuções (os números
    //gerados pelo random() são pseudo-aleatórios).
    srand(time(NULL));

    //percorro o array inteiro da população, inicializando cada indivíduo.
    for (i=0; i<15; i++)
        pop[i] = generate_tree();

    //imprimo todos os indivíduos que foram criados na população inicial
    for (i=0; i<15; i++){
        print_node_d(pop[i]);
        cout << endl;
    }

    //libero a memoria alocada. todo new deve casar com um delete!
    for (i=0; i<15; i++)
        destruct_tree(pop[i]);

    return 0;
}
<|endoftext|>