<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of duihome.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/
#include <cmath>
#include <QTimer>
#include <QGraphicsSceneMouseEvent>
#include "mainwindow.h"
#include <QApplication>
#include <QX11Info>
#include <QPainter>
#include <DuiScalableImage>
#include <DuiCancelEvent>
#include <DuiSceneManager>
#include "switcherbuttonview.h"
#include "switcherbutton.h"

#ifdef Q_WS_X11
bool SwitcherButtonView::badMatchOccurred = false;
#endif

SwitcherButtonView::SwitcherButtonView(SwitcherButton *button) :
    DuiWidgetView(button),
    controller(button),
    xWindowPixmap(0),
    xWindowPixmapDamage(0)
{
    // Configure timers
    windowCloseTimer.setSingleShot(true);
    connect(&windowCloseTimer, SIGNAL(timeout()), this, SLOT(resetState()));

    // Connect to the windowVisibilityChanged signal of the HomeApplication to get information about window visiblity changes
    connect(qApp, SIGNAL(windowVisibilityChanged(Window)), this, SLOT(windowVisibilityChanged(Window)));

    // Show interest in X pixmap change signals
    connect(qApp, SIGNAL(damageEvent(Qt::HANDLE &, short &, short &, unsigned short &, unsigned short &)), this, SLOT(damageEvent(Qt::HANDLE &, short &, short &, unsigned short &, unsigned short &)));
}

SwitcherButtonView::~SwitcherButtonView()
{
#ifdef Q_WS_X11
    if (xWindowPixmapDamage != 0) {
        // Unregister the pixmap from XDamage events
        X11Wrapper::XDamageDestroy(QX11Info::display(), xWindowPixmapDamage);
    }

    if (xWindowPixmap != 0) {
        // Dereference the old pixmap ID
        X11Wrapper::XFreePixmap(QX11Info::display(), xWindowPixmap);
    }
#endif
}

void SwitcherButtonView::drawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    // Store the painter state
    painter->save();

    QPoint pos = style()->iconPosition().toPoint();
    QSize size = style()->iconSize();

    QRect target(pos, size);

    // Do the actual drawing
    backendSpecificDrawBackground(painter, option, target);

    // Restore the painter state
    painter->restore();
}

void SwitcherButtonView::backendSpecificDrawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect& target) const
{
    Q_UNUSED(painter);
    Q_UNUSED(option);
    Q_UNUSED(target);
}

void SwitcherButtonView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *) const
{
    // Store the painter state
    painter->save();

    // Draw the container
    const DuiScalableImage *container = style()->containerImage();
    if (container != NULL) {
        container->draw(QRect(QPoint(0, 0), size().toSize()), painter);
    }

    // Draw text
    const QString text(controller->text());
    if (!text.isEmpty() && controller->isTextVisible()) {
        QFont font = style()->font();
        painter->setFont(font);
        painter->setPen(style()->textColor());
        painter->setOpacity(style()->textOpacity());
        painter->drawText(titleRect(), Qt::AlignLeft | Qt::ElideRight, text);
    }

    // Draw a close image at top right corner, at the moment it is assumed that
    // the close button is square!!
    if (style()->closeImage()) {
	painter->setOpacity(1.0f);
	QRectF title = titleRect();

	QRectF rect = buttonRect();
	rect.setTopLeft(QPointF(rect.right() - title.height(), title.y()));
	rect.setWidth(title.height());
	rect.setHeight(title.height());
	style()->closeImage()->draw(rect.x(), rect.y(), rect.width(), rect.height(), painter);
    }

    // Restore the painter state
    painter->restore();
}

QRectF SwitcherButtonView::buttonRect() const
{
    QRectF rect(QPointF(), size());

    return rect;
}

QRectF SwitcherButtonView::titleRect() const
{
    QRectF rect = buttonRect();
    QRectF close = closeRect();
    QFontMetrics fm(style()->font());

    rect.setTopLeft(rect.topLeft() - QPointF(0, fm.height()));
    rect.setBottomRight(rect.topRight() + QPointF(-(close.width() + 2 * style()->textMarginLeft()), fm.height()));
    rect.translate(style()->textMarginLeft(), -style()->textMarginBottom());
    return rect;
}

QRectF SwitcherButtonView::closeRect() const
{
    QRectF rect = buttonRect();
    if (style()->closeImage()) {
        const QPixmap* closeImage = style()->closeImage()->pixmap();
	// calculate the rect for the close button alone
	rect.setBottomLeft(rect.topRight() - QPointF(closeImage->width() * 0.5f, 0));
	rect.setTopRight(rect.topRight() - QPointF(0, closeImage->height() * 0.5f));
	rect.setWidth(closeImage->width());
	rect.setHeight(closeImage->height());
    } else {
        // HACK: specify a fake 1x1 rectagle at the top-right corner of the button
        rect.setBottomLeft(rect.topRight() - QPointF(1, -1));
    }

    return rect;
}

QRectF SwitcherButtonView::boundingRect() const
{
    return buttonRect().united(closeRect());
}

void SwitcherButtonView::applyStyle()
{
    DuiWidgetView::applyStyle();
    updateThumbnail();
}

void SwitcherButtonView::setupModel()
{
    DuiWidgetView::setupModel();

    if (model()->xWindow() != 0) {
        updateXWindowPixmap();
        updateThumbnail();
    }
    updateViewMode();
}

void SwitcherButtonView::updateViewMode()
{
    switch (model()->viewMode()) {
    case SwitcherButtonModel::Small:
	style().setModeSmall();
	break;
    case SwitcherButtonModel::Medium:
	style().setModeMedium();
	break;
    case SwitcherButtonModel::Large:
	style().setModeLarge();
	break;
    case SwitcherButtonModel::UnSpecified:
	break;
    }

    // When the style mode changes, the style is not automatically applied -> call it explicitly
    applyStyle();
}

void SwitcherButtonView::updateData(const QList<const char *>& modifications)
{
    DuiWidgetView::updateData(modifications);
    const char *member;
    foreach(member, modifications) {
        if (member == SwitcherButtonModel::XWindow && model()->xWindow() != 0) {
            updateXWindowPixmap();
            updateThumbnail();
        }

	if (member == SwitcherButtonModel::ViewMode) {
	    updateViewMode();
	}
    }
}

void SwitcherButtonView::resetState()
{
    controller->setVisible(true);
    controller->prepareGeometryChange();
}

void SwitcherButtonView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (!model()->down()) {

        model()->setDown(true);

        if (closeRect().contains(event->pos())) {
            model()->setPressed(SwitcherButtonModel::ClosePressed);
        } else if (buttonRect().contains(event->pos())) {
            model()->setPressed(SwitcherButtonModel::ButtonPressed);
        } else {
            model()->setPressed(SwitcherButtonModel::NonePressed);
        }
        event->accept();
    }
}

void SwitcherButtonView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (model()->down()) {
        model()->setDown(false);

        if (closeRect().contains(event->pos())) {
            if (model()->pressed() == SwitcherButtonModel::ClosePressed) {
                // Close icon clicked, send the close signal and hide
                // the button
                controller->close();
                controller->setVisible(false);
                windowCloseTimer.start(5000);
            }
        } else if (buttonRect().contains(event->pos())) {
            if (model()->pressed() == SwitcherButtonModel::ButtonPressed) {
                // Switcher button clicked, let the controller know that
                // the window should be brought to front
                model()->click();
                controller->switchToWindow();
                update();
            }
        }

        model()->setPressed(SwitcherButtonModel::NonePressed);
        event->accept();
    }
}

void SwitcherButtonView::cancelEvent(DuiCancelEvent *event)
{
    if (model()->down()) {
        model()->setDown(false);
        model()->setPressed(SwitcherButtonModel::NonePressed);

        event->accept();
    }
}

void SwitcherButtonView::updateXWindowPixmap()
{
#ifdef Q_WS_X11
    if (xWindowPixmapDamage != 0) {
        // Unregister the old pixmap from XDamage events
        X11Wrapper::XDamageDestroy(QX11Info::display(), xWindowPixmapDamage);
        xWindowPixmapDamage = 0;
    }

    if (xWindowPixmap != 0) {
        // Dereference the old pixmap ID
        X11Wrapper::XFreePixmap(QX11Info::display(), xWindowPixmap);
        xWindowPixmap = 0;
    }

    // It is possible that the window is not redirected so check for errors
    XErrorHandler errh = X11Wrapper::XSetErrorHandler(handleXError);

    // Get the pixmap ID of the X window
    xWindowPixmap = X11Wrapper::XCompositeNameWindowPixmap(QX11Info::display(), model()->xWindow());
    X11Wrapper::XSync(QX11Info::display(), FALSE);

    // If a BadMatch error occurred set the window pixmap ID to 0
    if (badMatchOccurred) {
        xWindowPixmap = 0;
        badMatchOccurred = false;
    }

    // Reset the error handler
    X11Wrapper::XSetErrorHandler(errh);

    if (xWindowPixmap != 0) {
        // Register the pixmap for XDamage events
        xWindowPixmapDamage = X11Wrapper::XDamageCreate(QX11Info::display(), xWindowPixmap, XDamageReportNonEmpty);

        backendSpecificUpdateXWindowPixmap();
    }
#endif
}

void SwitcherButtonView::backendSpecificUpdateXWindowPixmap()
{
}

void SwitcherButtonView::updateThumbnail()
{
    // Redraw
    update();
}

#ifdef Q_WS_X11
int SwitcherButtonView::handleXError(Display *, XErrorEvent *event)
{
    if (event->error_code == BadMatch) {
        badMatchOccurred = true;
    }
    return 0;
}
#endif

void SwitcherButtonView::windowVisibilityChanged(Window window)
{
    if (window == model()->xWindow()) {
        updateXWindowPixmap();
        updateThumbnail();
    }
}

void SwitcherButtonView::damageEvent(Qt::HANDLE &damage, short &x, short &y, unsigned short &width, unsigned short &height)
{
    Q_UNUSED(x);
    Q_UNUSED(y);
    Q_UNUSED(width);
    Q_UNUSED(height);
    if (xWindowPixmapDamage == damage) {
        update();
    }
}

DUI_REGISTER_VIEW_NEW(SwitcherButtonView, SwitcherButton)
<commit_msg>Revert "Changes: The switcher buttons do not need to be rotated any more"<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of duihome.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/
#include <cmath>
#include <QTimer>
#include <QGraphicsSceneMouseEvent>
#include "mainwindow.h"
#include <QApplication>
#include <QX11Info>
#include <QPainter>
#include <DuiScalableImage>
#include <DuiCancelEvent>
#include <DuiSceneManager>
#include "switcherbuttonview.h"
#include "switcherbutton.h"

#ifdef Q_WS_X11
bool SwitcherButtonView::badMatchOccurred = false;
#endif

SwitcherButtonView::SwitcherButtonView(SwitcherButton *button) :
    DuiWidgetView(button),
    controller(button),
    xWindowPixmap(0),
    xWindowPixmapDamage(0)
{
    // Configure timers
    windowCloseTimer.setSingleShot(true);
    connect(&windowCloseTimer, SIGNAL(timeout()), this, SLOT(resetState()));

    // Connect to the windowVisibilityChanged signal of the HomeApplication to get information about window visiblity changes
    connect(qApp, SIGNAL(windowVisibilityChanged(Window)), this, SLOT(windowVisibilityChanged(Window)));

    // Show interest in X pixmap change signals
    connect(qApp, SIGNAL(damageEvent(Qt::HANDLE &, short &, short &, unsigned short &, unsigned short &)), this, SLOT(damageEvent(Qt::HANDLE &, short &, short &, unsigned short &, unsigned short &)));
}

SwitcherButtonView::~SwitcherButtonView()
{
#ifdef Q_WS_X11
    if (xWindowPixmapDamage != 0) {
        // Unregister the pixmap from XDamage events
        X11Wrapper::XDamageDestroy(QX11Info::display(), xWindowPixmapDamage);
    }

    if (xWindowPixmap != 0) {
        // Dereference the old pixmap ID
        X11Wrapper::XFreePixmap(QX11Info::display(), xWindowPixmap);
    }
#endif
}

void SwitcherButtonView::drawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    // Store the painter state
    painter->save();

    // Rotate the thumbnails and adjust their size if the screen
    // has been rotated

    DuiSceneManager *manager = MainWindow::instance()->sceneManager();
    QPoint pos = style()->iconPosition().toPoint();
    QSize size = style()->iconSize();

    if (manager->orientation() == Dui::Portrait) {
        size.transpose();
    }

    switch (manager->orientationAngle()) {
        case Dui::Angle90:
            pos -= QPoint(size.width(), 0);
            break;
        case Dui::Angle180:
            pos -= QPoint(size.width(), size.height());
            break;
        case Dui::Angle270:
            pos -= QPoint(0, size.height());
            break;
        default:
            break;
    }

    painter->rotate(-manager->orientationAngle());

    QRect target(pos, size);

    // Do the actual drawing
    backendSpecificDrawBackground(painter, option, target);

    // Restore the painter state
    painter->restore();
}

void SwitcherButtonView::backendSpecificDrawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect& target) const
{
    Q_UNUSED(painter);
    Q_UNUSED(option);
    Q_UNUSED(target);
}

void SwitcherButtonView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *) const
{
    // Store the painter state
    painter->save();

    // Draw the container
    const DuiScalableImage *container = style()->containerImage();
    if (container != NULL) {
        container->draw(QRect(QPoint(0, 0), size().toSize()), painter);
    }

    // Draw text
    const QString text(controller->text());
    if (!text.isEmpty() && controller->isTextVisible()) {
        QFont font = style()->font();
        painter->setFont(font);
        painter->setPen(style()->textColor());
        painter->setOpacity(style()->textOpacity());
        painter->drawText(titleRect(), Qt::AlignLeft | Qt::ElideRight, text);
    }

    // Draw a close image at top right corner, at the moment it is assumed that
    // the close button is square!!
    if (style()->closeImage()) {
	painter->setOpacity(1.0f);
	QRectF title = titleRect();

	QRectF rect = buttonRect();
	rect.setTopLeft(QPointF(rect.right() - title.height(), title.y()));
	rect.setWidth(title.height());
	rect.setHeight(title.height());
	style()->closeImage()->draw(rect.x(), rect.y(), rect.width(), rect.height(), painter);
    }

    // Restore the painter state
    painter->restore();
}

QRectF SwitcherButtonView::buttonRect() const
{
    QRectF rect(QPointF(), size());

    return rect;
}

QRectF SwitcherButtonView::titleRect() const
{
    QRectF rect = buttonRect();
    QRectF close = closeRect();
    QFontMetrics fm(style()->font());

    rect.setTopLeft(rect.topLeft() - QPointF(0, fm.height()));
    rect.setBottomRight(rect.topRight() + QPointF(-(close.width() + 2 * style()->textMarginLeft()), fm.height()));
    rect.translate(style()->textMarginLeft(), -style()->textMarginBottom());
    return rect;
}

QRectF SwitcherButtonView::closeRect() const
{
    QRectF rect = buttonRect();
    if (style()->closeImage()) {
        const QPixmap* closeImage = style()->closeImage()->pixmap();
	// calculate the rect for the close button alone
	rect.setBottomLeft(rect.topRight() - QPointF(closeImage->width() * 0.5f, 0));
	rect.setTopRight(rect.topRight() - QPointF(0, closeImage->height() * 0.5f));
	rect.setWidth(closeImage->width());
	rect.setHeight(closeImage->height());
    } else {
        // HACK: specify a fake 1x1 rectagle at the top-right corner of the button
        rect.setBottomLeft(rect.topRight() - QPointF(1, -1));
    }

    return rect;
}

QRectF SwitcherButtonView::boundingRect() const
{
    return buttonRect().united(closeRect());
}

void SwitcherButtonView::applyStyle()
{
    DuiWidgetView::applyStyle();
    updateThumbnail();
}

void SwitcherButtonView::setupModel()
{
    DuiWidgetView::setupModel();

    if (model()->xWindow() != 0) {
        updateXWindowPixmap();
        updateThumbnail();
    }
    updateViewMode();
}

void SwitcherButtonView::updateViewMode()
{
    switch (model()->viewMode()) {
    case SwitcherButtonModel::Small:
	style().setModeSmall();
	break;
    case SwitcherButtonModel::Medium:
	style().setModeMedium();
	break;
    case SwitcherButtonModel::Large:
	style().setModeLarge();
	break;
    case SwitcherButtonModel::UnSpecified:
	break;
    }

    // When the style mode changes, the style is not automatically applied -> call it explicitly
    applyStyle();
}

void SwitcherButtonView::updateData(const QList<const char *>& modifications)
{
    DuiWidgetView::updateData(modifications);
    const char *member;
    foreach(member, modifications) {
        if (member == SwitcherButtonModel::XWindow && model()->xWindow() != 0) {
            updateXWindowPixmap();
            updateThumbnail();
        }

	if (member == SwitcherButtonModel::ViewMode) {
	    updateViewMode();
	}
    }
}

void SwitcherButtonView::resetState()
{
    controller->setVisible(true);
    controller->prepareGeometryChange();
}

void SwitcherButtonView::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (!model()->down()) {

        model()->setDown(true);

        if (closeRect().contains(event->pos())) {
            model()->setPressed(SwitcherButtonModel::ClosePressed);
        } else if (buttonRect().contains(event->pos())) {
            model()->setPressed(SwitcherButtonModel::ButtonPressed);
        } else {
            model()->setPressed(SwitcherButtonModel::NonePressed);
        }
        event->accept();
    }
}

void SwitcherButtonView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (model()->down()) {
        model()->setDown(false);

        if (closeRect().contains(event->pos())) {
            if (model()->pressed() == SwitcherButtonModel::ClosePressed) {
                // Close icon clicked, send the close signal and hide
                // the button
                controller->close();
                controller->setVisible(false);
                windowCloseTimer.start(5000);
            }
        } else if (buttonRect().contains(event->pos())) {
            if (model()->pressed() == SwitcherButtonModel::ButtonPressed) {
                // Switcher button clicked, let the controller know that
                // the window should be brought to front
                model()->click();
                controller->switchToWindow();
                update();
            }
        }

        model()->setPressed(SwitcherButtonModel::NonePressed);
        event->accept();
    }
}

void SwitcherButtonView::cancelEvent(DuiCancelEvent *event)
{
    if (model()->down()) {
        model()->setDown(false);
        model()->setPressed(SwitcherButtonModel::NonePressed);

        event->accept();
    }
}

void SwitcherButtonView::updateXWindowPixmap()
{
#ifdef Q_WS_X11
    if (xWindowPixmapDamage != 0) {
        // Unregister the old pixmap from XDamage events
        X11Wrapper::XDamageDestroy(QX11Info::display(), xWindowPixmapDamage);
        xWindowPixmapDamage = 0;
    }

    if (xWindowPixmap != 0) {
        // Dereference the old pixmap ID
        X11Wrapper::XFreePixmap(QX11Info::display(), xWindowPixmap);
        xWindowPixmap = 0;
    }

    // It is possible that the window is not redirected so check for errors
    XErrorHandler errh = X11Wrapper::XSetErrorHandler(handleXError);

    // Get the pixmap ID of the X window
    xWindowPixmap = X11Wrapper::XCompositeNameWindowPixmap(QX11Info::display(), model()->xWindow());
    X11Wrapper::XSync(QX11Info::display(), FALSE);

    // If a BadMatch error occurred set the window pixmap ID to 0
    if (badMatchOccurred) {
        xWindowPixmap = 0;
        badMatchOccurred = false;
    }

    // Reset the error handler
    X11Wrapper::XSetErrorHandler(errh);

    if (xWindowPixmap != 0) {
        // Register the pixmap for XDamage events
        xWindowPixmapDamage = X11Wrapper::XDamageCreate(QX11Info::display(), xWindowPixmap, XDamageReportNonEmpty);

        backendSpecificUpdateXWindowPixmap();
    }
#endif
}

void SwitcherButtonView::backendSpecificUpdateXWindowPixmap()
{
}

void SwitcherButtonView::updateThumbnail()
{
    // Redraw
    update();
}

#ifdef Q_WS_X11
int SwitcherButtonView::handleXError(Display *, XErrorEvent *event)
{
    if (event->error_code == BadMatch) {
        badMatchOccurred = true;
    }
    return 0;
}
#endif

void SwitcherButtonView::windowVisibilityChanged(Window window)
{
    if (window == model()->xWindow()) {
        updateXWindowPixmap();
        updateThumbnail();
    }
}

void SwitcherButtonView::damageEvent(Qt::HANDLE &damage, short &x, short &y, unsigned short &width, unsigned short &height)
{
    Q_UNUSED(x);
    Q_UNUSED(y);
    Q_UNUSED(width);
    Q_UNUSED(height);
    if (xWindowPixmapDamage == damage) {
        update();
    }
}

DUI_REGISTER_VIEW_NEW(SwitcherButtonView, SwitcherButton)
<|endoftext|>


<commit_before>// Copyright (c) 2011 - 2017, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/timer/Timer.h>

#include <stingraykit/diagnostics/ExecutorsProfiler.h>
#include <stingraykit/function/CancellableFunction.h>
#include <stingraykit/function/bind.h>
#include <stingraykit/function/function_name_getter.h>
#include <stingraykit/log/Logger.h>
#include <stingraykit/time/ElapsedTime.h>
#include <stingraykit/FunctionToken.h>
#include <stingraykit/TaskLifeToken.h>

#include <list>
#include <map>

namespace stingray
{

	class Timer::CallbackQueue
	{
		typedef std::list<CallbackInfoPtr>					ContainerInternal;
		typedef std::map<TimeDuration, ContainerInternal>	Container;

	private:
		Mutex			_mutex;
		Container		_container;

	public:
		typedef ContainerInternal::iterator iterator;

		inline Mutex& Sync()
		{ return _mutex; }

		inline bool IsEmpty() const
		{
			MutexLock l(_mutex);
			return _container.empty();
		}

		CallbackInfoPtr Top() const;
		void Push(const CallbackInfoPtr& ci);
		void Erase(const CallbackInfoPtr& ci);
		CallbackInfoPtr Pop();
	};

	class Timer::CallbackInfo
	{
		STINGRAYKIT_NONCOPYABLE(CallbackInfo);

		typedef function<void()>			FuncT;
		typedef CallbackQueue::iterator		QueueIterator;

	private:
		FuncT						_func;
		TimeDuration				_timeToTrigger;
		optional<TimeDuration>		_period;
		TaskLifeToken				_token;

		bool						_erased;
		optional<QueueIterator>		_iterator;

	private:
		friend class CallbackQueue;

		void SetIterator(const optional<QueueIterator>& it)		{ _iterator = it; }
		const optional<QueueIterator>& GetIterator() const		{ return _iterator; }
		void SetErased()										{ _erased = true; }
		bool IsErased() const									{ return _erased; }

	public:
		CallbackInfo(const FuncT& func, const TimeDuration& timeToTrigger, const optional<TimeDuration>& period, const TaskLifeToken& token)
			:	_func(func),
				_timeToTrigger(timeToTrigger),
				_period(period),
				_token(token),
				_erased(false)
		{ }

		const FuncT& GetFunc() const							{ return _func; }
		FutureExecutionTester GetExecutionTester() const 		{ return _token.GetExecutionTester(); }
		void Release()											{ _token.Release(); }

		bool IsPeriodic() const									{ return _period.is_initialized(); }
		void Restart(const TimeDuration& currentTime)
		{
			STINGRAYKIT_CHECK(_period, "CallbackInfo::Restart internal error: _period is set!");
			_timeToTrigger = currentTime + *_period;
		}
		TimeDuration GetTimeToTrigger() const					{ return _timeToTrigger; }
	};

	Timer::CallbackInfoPtr Timer::CallbackQueue::Top() const
	{
		MutexLock l(_mutex);
		if (!_container.empty())
		{
			const ContainerInternal& listForTop = _container.begin()->second;
			STINGRAYKIT_CHECK(!listForTop.empty(), "try to get callback from empty list");
			return listForTop.front();
		}
		else
			return null;
	}

	void Timer::CallbackQueue::Push(const CallbackInfoPtr& ci)
	{
		MutexLock l(_mutex);
		if (ci->IsErased())
			return;

		ContainerInternal& listToInsert = _container[ci->GetTimeToTrigger()];
		ci->SetIterator(listToInsert.insert(listToInsert.end(), ci));
	}

	void Timer::CallbackQueue::Erase(const CallbackInfoPtr& ci)
	{
		MutexLock l(_mutex);
		ci->SetErased();

		const optional<iterator>& it = ci->GetIterator();
		if (!it)
			return;

		TimeDuration keyToErase = ci->GetTimeToTrigger();
		ContainerInternal& listToErase = _container[keyToErase];
		listToErase.erase(*it);
		if (listToErase.empty())
			_container.erase(keyToErase);
		ci->SetIterator(null);
	}

	Timer::CallbackInfoPtr Timer::CallbackQueue::Pop()
	{
		MutexLock l(_mutex);
		STINGRAYKIT_CHECK(!_container.empty(), "popping callback from empty map");
		ContainerInternal& listToPop = _container.begin()->second;
		STINGRAYKIT_CHECK(!listToPop.empty(), "popping callback from empty list");

		CallbackInfoPtr ci = listToPop.front();
		listToPop.pop_front();
		if (listToPop.empty())
			_container.erase(ci->GetTimeToTrigger());
		ci->SetIterator(null);
		return ci;
	}

	STINGRAYKIT_DEFINE_NAMED_LOGGER(Timer);

	Timer::Timer(const std::string& timerName, const ExceptionHandler& exceptionHandler, bool profileCalls)
		:	_timerName(timerName),
			_exceptionHandler(exceptionHandler),
			_profileCalls(profileCalls),
			_alive(true),
			_queue(make_shared<CallbackQueue>()),
			_worker(make_shared<Thread>(timerName, bind(&Timer::ThreadFunc, this, not_using(_1))))
	{ }

	Timer::~Timer()
	{
		{
			MutexLock l(_queue->Sync());
			_alive = false;
			_cond.Broadcast();
		}
		_worker.reset();

		MutexLock l(_queue->Sync());
		while(!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			MutexUnlock ul(l);
			LocalExecutionGuard guard(top->GetExecutionTester());

			top.reset();
			if (guard)
			{
				s_logger.Warning() << "killing timer " << _timerName << " which still has some functions to execute";
				break;
			}
		}
	}

	Token Timer::SetTimeout(const TimeDuration& timeout, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, null, TaskLifeToken());
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<FunctionToken>(bind(&Timer::RemoveTask, _queue, ci));
	}

	Token Timer::SetTimer(const TimeDuration& interval, const function<void()>& func)
	{ return SetTimer(interval, interval, func); }

	Token Timer::SetTimer(const TimeDuration& timeout, const TimeDuration& interval, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, interval, TaskLifeToken());
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<FunctionToken>(bind(&Timer::RemoveTask, _queue, ci));
	}

	void Timer::AddTask(const function<void()>& task, const FutureExecutionTester& tester)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(MakeCancellableFunction(task, tester), _monotonic.Elapsed(), null, TaskLifeToken::CreateDummyTaskToken());
		_queue->Push(ci);
		_cond.Broadcast();
	}

	void Timer::RemoveTask(const CallbackQueuePtr& queue, const CallbackInfoPtr& ci)
	{
		{
			MutexLock l(queue->Sync());
			queue->Erase(ci);
		}
		ci->Release();
	}

	std::string Timer::GetProfilerMessage(const function<void()>& func)
	{ return StringBuilder() % get_function_name(func) % " in Timer '" % _timerName % "'"; }

	void Timer::ThreadFunc()
	{
		MutexLock l(_queue->Sync());

		while (_alive)
		{
			if (_queue->IsEmpty())
			{
				_cond.Wait(_queue->Sync());
				continue;
			}

			CallbackInfoPtr top = _queue->Top();
			if (top->GetTimeToTrigger() <= _monotonic.Elapsed())
			{
				_queue->Pop();

				{
					MutexUnlock ul(l);
					LocalExecutionGuard guard(top->GetExecutionTester());
					if (!guard)
					{
						top.reset();
						continue;
					}

					if (top->IsPeriodic())
						top->Restart(_monotonic.Elapsed());

					try
					{
						if (_profileCalls)
						{
							AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
							(top->GetFunc())();
						}
						else
							(top->GetFunc())();
					}
					catch(const std::exception &ex)
					{ _exceptionHandler(ex); }

					if (!top->IsPeriodic())
						top.reset();
				}

				if (top)
					_queue->Push(top);
			}
			else //top timer not triggered
			{
				const TimeDuration waitTime = top->GetTimeToTrigger() - _monotonic.Elapsed();
				top.reset();
				if (waitTime > TimeDuration())
					_cond.TimedWait(_queue->Sync(), waitTime);
			}
		}

		const TimeDuration currentTime = _monotonic.Elapsed();
		while (!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			if (top->GetTimeToTrigger() <= currentTime)
				break;

			MutexUnlock ul(l);
			LocalExecutionGuard guard(top->GetExecutionTester());
			if (!guard)
			{
				top.reset();
				continue;
			}

			try
			{
				if (_profileCalls)
				{
					AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
					(top->GetFunc())();
				}
				else
					(top->GetFunc())();
			}
			catch(const std::exception &ex)
			{ _exceptionHandler(ex); }

			top.reset();
		}
	}

	void Timer::DefaultExceptionHandler(const std::exception& ex)
	{ s_logger.Error() << "Timer func exception: " << ex; }

}
<commit_msg>Timer: minor reduce of mutex locking time<commit_after>// Copyright (c) 2011 - 2017, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/timer/Timer.h>

#include <stingraykit/diagnostics/ExecutorsProfiler.h>
#include <stingraykit/function/CancellableFunction.h>
#include <stingraykit/function/bind.h>
#include <stingraykit/function/function_name_getter.h>
#include <stingraykit/log/Logger.h>
#include <stingraykit/time/ElapsedTime.h>
#include <stingraykit/FunctionToken.h>
#include <stingraykit/TaskLifeToken.h>

#include <list>
#include <map>

namespace stingray
{

	class Timer::CallbackQueue
	{
		typedef std::list<CallbackInfoPtr>					ContainerInternal;
		typedef std::map<TimeDuration, ContainerInternal>	Container;

	private:
		Mutex			_mutex;
		Container		_container;

	public:
		typedef ContainerInternal::iterator iterator;

		inline Mutex& Sync()
		{ return _mutex; }

		inline bool IsEmpty() const
		{
			MutexLock l(_mutex);
			return _container.empty();
		}

		CallbackInfoPtr Top() const;
		void Push(const CallbackInfoPtr& ci);
		void Erase(const CallbackInfoPtr& ci);
		CallbackInfoPtr Pop();
	};

	class Timer::CallbackInfo
	{
		STINGRAYKIT_NONCOPYABLE(CallbackInfo);

		typedef function<void()>			FuncT;
		typedef CallbackQueue::iterator		QueueIterator;

	private:
		FuncT						_func;
		TimeDuration				_timeToTrigger;
		optional<TimeDuration>		_period;
		TaskLifeToken				_token;

		bool						_erased;
		optional<QueueIterator>		_iterator;

	private:
		friend class CallbackQueue;

		void SetIterator(const optional<QueueIterator>& it)		{ _iterator = it; }
		const optional<QueueIterator>& GetIterator() const		{ return _iterator; }
		void SetErased()										{ _erased = true; }
		bool IsErased() const									{ return _erased; }

	public:
		CallbackInfo(const FuncT& func, const TimeDuration& timeToTrigger, const optional<TimeDuration>& period, const TaskLifeToken& token)
			:	_func(func),
				_timeToTrigger(timeToTrigger),
				_period(period),
				_token(token),
				_erased(false)
		{ }

		const FuncT& GetFunc() const							{ return _func; }
		FutureExecutionTester GetExecutionTester() const 		{ return _token.GetExecutionTester(); }
		void Release()											{ _token.Release(); }

		bool IsPeriodic() const									{ return _period.is_initialized(); }
		void Restart(const TimeDuration& currentTime)
		{
			STINGRAYKIT_CHECK(_period, "CallbackInfo::Restart internal error: _period is set!");
			_timeToTrigger = currentTime + *_period;
		}
		TimeDuration GetTimeToTrigger() const					{ return _timeToTrigger; }
	};

	Timer::CallbackInfoPtr Timer::CallbackQueue::Top() const
	{
		MutexLock l(_mutex);
		if (!_container.empty())
		{
			const ContainerInternal& listForTop = _container.begin()->second;
			STINGRAYKIT_CHECK(!listForTop.empty(), "try to get callback from empty list");
			return listForTop.front();
		}
		else
			return null;
	}

	void Timer::CallbackQueue::Push(const CallbackInfoPtr& ci)
	{
		MutexLock l(_mutex);
		if (ci->IsErased())
			return;

		ContainerInternal& listToInsert = _container[ci->GetTimeToTrigger()];
		ci->SetIterator(listToInsert.insert(listToInsert.end(), ci));
	}

	void Timer::CallbackQueue::Erase(const CallbackInfoPtr& ci)
	{
		MutexLock l(_mutex);
		ci->SetErased();

		const optional<iterator>& it = ci->GetIterator();
		if (!it)
			return;

		TimeDuration keyToErase = ci->GetTimeToTrigger();
		ContainerInternal& listToErase = _container[keyToErase];
		listToErase.erase(*it);
		if (listToErase.empty())
			_container.erase(keyToErase);
		ci->SetIterator(null);
	}

	Timer::CallbackInfoPtr Timer::CallbackQueue::Pop()
	{
		MutexLock l(_mutex);
		STINGRAYKIT_CHECK(!_container.empty(), "popping callback from empty map");
		ContainerInternal& listToPop = _container.begin()->second;
		STINGRAYKIT_CHECK(!listToPop.empty(), "popping callback from empty list");

		CallbackInfoPtr ci = listToPop.front();
		listToPop.pop_front();
		if (listToPop.empty())
			_container.erase(ci->GetTimeToTrigger());
		ci->SetIterator(null);
		return ci;
	}

	STINGRAYKIT_DEFINE_NAMED_LOGGER(Timer);

	Timer::Timer(const std::string& timerName, const ExceptionHandler& exceptionHandler, bool profileCalls)
		:	_timerName(timerName),
			_exceptionHandler(exceptionHandler),
			_profileCalls(profileCalls),
			_alive(true),
			_queue(make_shared<CallbackQueue>()),
			_worker(make_shared<Thread>(timerName, bind(&Timer::ThreadFunc, this, not_using(_1))))
	{ }

	Timer::~Timer()
	{
		{
			MutexLock l(_queue->Sync());
			_alive = false;
			_cond.Broadcast();
		}
		_worker.reset();

		MutexLock l(_queue->Sync());
		while(!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			MutexUnlock ul(l);
			LocalExecutionGuard guard(top->GetExecutionTester());

			top.reset();
			if (guard)
			{
				s_logger.Warning() << "killing timer " << _timerName << " which still has some functions to execute";
				break;
			}
		}
	}

	Token Timer::SetTimeout(const TimeDuration& timeout, const function<void()>& func)
	{
		const CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, null, TaskLifeToken());
		{
			MutexLock l(_queue->Sync());
			_queue->Push(ci);
			_cond.Broadcast();
		}

		return MakeToken<FunctionToken>(bind(&Timer::RemoveTask, _queue, ci));
	}

	Token Timer::SetTimer(const TimeDuration& interval, const function<void()>& func)
	{ return SetTimer(interval, interval, func); }

	Token Timer::SetTimer(const TimeDuration& timeout, const TimeDuration& interval, const function<void()>& func)
	{
		const CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, interval, TaskLifeToken());

		{
			MutexLock l(_queue->Sync());
			_queue->Push(ci);
			_cond.Broadcast();
		}

		return MakeToken<FunctionToken>(bind(&Timer::RemoveTask, _queue, ci));
	}

	void Timer::AddTask(const function<void()>& task, const FutureExecutionTester& tester)
	{
		const CallbackInfoPtr ci = make_shared<CallbackInfo>(MakeCancellableFunction(task, tester), _monotonic.Elapsed(), null, TaskLifeToken::CreateDummyTaskToken());

		MutexLock l(_queue->Sync());
		_queue->Push(ci);
		_cond.Broadcast();
	}

	void Timer::RemoveTask(const CallbackQueuePtr& queue, const CallbackInfoPtr& ci)
	{
		{
			MutexLock l(queue->Sync());
			queue->Erase(ci);
		}
		ci->Release();
	}

	std::string Timer::GetProfilerMessage(const function<void()>& func)
	{ return StringBuilder() % get_function_name(func) % " in Timer '" % _timerName % "'"; }

	void Timer::ThreadFunc()
	{
		MutexLock l(_queue->Sync());

		while (_alive)
		{
			if (_queue->IsEmpty())
			{
				_cond.Wait(_queue->Sync());
				continue;
			}

			CallbackInfoPtr top = _queue->Top();
			if (top->GetTimeToTrigger() <= _monotonic.Elapsed())
			{
				_queue->Pop();

				{
					MutexUnlock ul(l);
					LocalExecutionGuard guard(top->GetExecutionTester());
					if (!guard)
					{
						top.reset();
						continue;
					}

					if (top->IsPeriodic())
						top->Restart(_monotonic.Elapsed());

					try
					{
						if (_profileCalls)
						{
							AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
							(top->GetFunc())();
						}
						else
							(top->GetFunc())();
					}
					catch(const std::exception &ex)
					{ _exceptionHandler(ex); }

					if (!top->IsPeriodic())
						top.reset();
				}

				if (top)
					_queue->Push(top);
			}
			else //top timer not triggered
			{
				const TimeDuration waitTime = top->GetTimeToTrigger() - _monotonic.Elapsed();
				top.reset();
				if (waitTime > TimeDuration())
					_cond.TimedWait(_queue->Sync(), waitTime);
			}
		}

		const TimeDuration currentTime = _monotonic.Elapsed();
		while (!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			if (top->GetTimeToTrigger() <= currentTime)
				break;

			MutexUnlock ul(l);
			LocalExecutionGuard guard(top->GetExecutionTester());
			if (!guard)
			{
				top.reset();
				continue;
			}

			try
			{
				if (_profileCalls)
				{
					AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
					(top->GetFunc())();
				}
				else
					(top->GetFunc())();
			}
			catch(const std::exception &ex)
			{ _exceptionHandler(ex); }

			top.reset();
		}
	}

	void Timer::DefaultExceptionHandler(const std::exception& ex)
	{ s_logger.Error() << "Timer func exception: " << ex; }

}
<|endoftext|>


<commit_before>/**
 * Copyright 2013 Da Zheng
 *
 * This file is part of SA-GraphLib.
 *
 * SA-GraphLib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SA-GraphLib 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 SA-GraphLib.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>

#include "graph.h"
#include "common.h"
#include "native_file.h"

size_t read_edge_list(const std::string &file, std::vector<edge> &edge_vector)
{
	native_file local_f(file);
	ssize_t file_size = local_f.get_size();
	assert(file_size > (ssize_t) sizeof(edge));
	assert(file_size % sizeof(edge) == 0);
	int num_edges = file_size / sizeof(edge);
	edge *edges = new edge[num_edges];
	FILE *f = fopen(file.c_str(), "r");
	if (f == NULL) {
		perror("fopen");
		assert(0);
	}
	size_t ret = fread(edges, file_size, 1, f);
	assert(ret == 1);
	fclose(f);
	edge_vector.assign(edges, edges + num_edges);
	delete [] edges;
	return edge_vector.size();
}

size_t read_edge_list_text(const std::string &file, std::vector<edge> &edges)
{
	FILE *f = fopen(file.c_str(), "r");
	if (f == NULL) {
		perror("fopen");
		assert(0);
	}

	ssize_t read;
	size_t len = 0;
	char *line = NULL;
	while ((read = getline(&line, &len, f)) != -1) {
		if (line[read - 1] == '\n')
			line[read - 1] = 0;
		if (line[read - 2] == '\r')
			line[read - 2] = 0;
		if (line[0] == '#')
			continue;
		char *second = strchr(line, '\t');
		assert(second);
		*second = 0;
		second++;
		if (!isnumeric(line) || !isnumeric(second)) {
			printf("%s\t%s\n", line, second);
			continue;
		}
		vertex_id_t from = atol(line);
		vertex_id_t to = atol(second);
		edges.push_back(edge(from, to));
	}
	fclose(f);
	return edges.size();
}

static struct comp_edge {
	bool operator() (const edge &e1, const edge &e2) {
		if (e1.get_from() == e2.get_from())
			return e1.get_to() < e2.get_to();
		else
			return e1.get_from() < e2.get_from();
	}
} edge_comparator;

undirected_graph *undirected_graph::create(edge edges[], size_t num_edges)
{
	undirected_graph *g = new undirected_graph();
	// Each edge appears twice and in different directions.
	// When we sort the edges with the first vertex id, we only need
	// a single scan to construct the graph in the form of
	// the adjacency list.
	edge *tmp = new edge[num_edges * 2];
	for (size_t i = 0; i < num_edges; i++) {
		tmp[2 * i] = edges[i];
		tmp[2 * i + 1] = edge(edges[i].get_to(), edges[i].get_from());
	}
	edges = tmp;
	num_edges *= 2;
	std::sort(edges, edges + num_edges, edge_comparator);

	vertex_id_t curr = edges[0].get_from();
	in_mem_undirected_vertex v(curr);
	for (size_t i = 0; i < num_edges; i++) {
		vertex_id_t id = edges[i].get_from();
		if (curr == id) {
			// We have to make sure the edge doesn't exist.
			assert(!v.has_edge(edges[i].get_to()));
			v.add_edge(edges[i].get_to());
		}
		else {
			g->add_vertex(v);
			vertex_id_t prev = curr + 1;
			curr = id;
			// The vertices without edges won't show up in the edge list,
			// but we need to fill the gap in the vertex Id space with empty
			// vertices.
			while (prev < curr) {
				v = in_mem_undirected_vertex(prev);
				prev++;
				g->add_vertex(v);
			}
			v = in_mem_undirected_vertex(curr);
			v.add_edge(edges[i].get_to());
		}
	}
	g->add_vertex(v);
	delete [] edges;
	return g;
}

undirected_graph *undirected_graph::load_edge_list(const std::string &file)
{
	std::vector<edge> edges;
	read_edge_list(file, edges);
	return create(edges.data(), edges.size());
}

undirected_graph *undirected_graph::load_edge_list_text(const std::string &file)
{
	std::vector<edge> edges;
	read_edge_list_text(file, edges);
	return create(edges.data(), edges.size());
}

undirected_graph *undirected_graph::load_adjacency_list(const std::string &file)
{
	assert(0);
}

vertex_index *undirected_graph::create_vertex_index() const
{
	return vertex_index::create<in_mem_undirected_vertex>(vertices);
}

void undirected_graph::dump(const std::string &file) const
{
	FILE *f = fopen(file.c_str(), "w");
	if (f == NULL) {
		perror("fopen");
		assert(0);
	}

	for (size_t i = 0; i < vertices.size(); i++) {
		int mem_size = vertices[i].get_serialize_size();
		char *buf = new char[mem_size];
		ext_mem_undirected_vertex::serialize(vertices[i], buf, mem_size);
		ssize_t ret = fwrite(buf, mem_size, 1, f);
		delete [] buf;
		assert(ret == 1);
	}

	fclose(f);
}

size_t undirected_graph::get_num_edges() const
{
	size_t num_edges = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		num_edges += vertices[i].get_num_edges();
	return num_edges;
}

size_t undirected_graph::get_num_non_empty_vertices() const
{
	size_t num_vertices = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		if (vertices[i].get_num_edges() > 0)
			num_vertices++;
	return num_vertices;
}

static struct comp_in_edge {
	bool operator() (const edge &e1, const edge &e2) {
		if (e1.get_to() == e2.get_to())
			return e1.get_from() < e2.get_from();
		else
			return e1.get_to() < e2.get_to();
	}
} in_edge_comparator;

directed_graph *directed_graph::create(edge edges[], size_t num_edges)
{
	directed_graph *g = new directed_graph();

	std::sort(edges, edges + num_edges, edge_comparator);
	edge *sorted_out_edges = edges;

	edge *copied_edges = new edge[num_edges];
	memcpy(copied_edges, edges, num_edges * sizeof(edges[0]));
	std::sort(copied_edges, copied_edges + num_edges, in_edge_comparator);
	edge *sorted_in_edges = copied_edges;

	vertex_id_t curr = min(sorted_out_edges[0].get_from(),
			sorted_in_edges[0].get_to());
	in_mem_directed_vertex v(curr);
	size_t out_idx = 0;
	size_t in_idx = 0;
	while (out_idx < num_edges && in_idx < num_edges) {
		while (sorted_out_edges[out_idx].get_from() == curr
				&& out_idx < num_edges) {
			v.add_out_edge(sorted_out_edges[out_idx++].get_to());
		}
		while (sorted_in_edges[in_idx].get_to() == curr
				&& in_idx < num_edges) {
			v.add_in_edge(sorted_in_edges[in_idx++].get_from());
		}
		g->add_vertex(v);
		vertex_id_t prev = curr + 1;
		if (out_idx < num_edges && in_idx < num_edges)
			curr = min(sorted_out_edges[out_idx].get_from(),
					sorted_in_edges[in_idx].get_to());
		else if (out_idx < num_edges)
			curr = sorted_out_edges[out_idx].get_from();
		else if (in_idx < num_edges)
			curr = sorted_in_edges[in_idx].get_to();
		else
			break;
		// The vertices without edges won't show up in the edge list,
		// but we need to fill the gap in the vertex Id space with empty
		// vertices.
		while (prev < curr) {
			v = in_mem_directed_vertex(prev);
			prev++;
			g->add_vertex(v);
		}
		v = in_mem_directed_vertex(curr);
	}

	assert(g->get_num_in_edges() == num_edges);
	assert(g->get_num_out_edges() == num_edges);
	delete [] copied_edges;
	return g;
}

directed_graph *directed_graph::load_edge_list(const std::string &file)
{
	std::vector<edge> edges;
	read_edge_list(file, edges);
	return create(edges.data(), edges.size());
}

directed_graph *directed_graph::load_edge_list_text(const std::string &file)
{
	std::vector<edge> edges;
	read_edge_list_text(file, edges);
	return create(edges.data(), edges.size());
}

directed_graph *directed_graph::load_adjacency_list(const std::string &file)
{
	assert(0);
	return NULL;
}

vertex_index *directed_graph::create_vertex_index() const
{
	return vertex_index::create<in_mem_directed_vertex>(vertices);
}

void directed_graph::dump(const std::string &file) const
{
	FILE *f = fopen(file.c_str(), "w");
	if (f == NULL) {
		perror("fopen");
		assert(0);
	}

	for (size_t i = 0; i < vertices.size(); i++) {
		int mem_size = vertices[i].get_serialize_size();
		char *buf = new char[mem_size];
		ext_mem_directed_vertex::serialize(vertices[i], buf, mem_size);
		ssize_t ret = fwrite(buf, mem_size, 1, f);
		delete [] buf;
		assert(ret == 1);
	}

	fclose(f);
}

size_t directed_graph::get_num_in_edges() const
{
	size_t num_in_edges = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		num_in_edges += vertices[i].get_num_in_edges();
	return num_in_edges;
}

size_t directed_graph::get_num_out_edges() const
{
	size_t num_out_edges = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		num_out_edges += vertices[i].get_num_out_edges();
	return num_out_edges;
}

size_t directed_graph::get_num_non_empty_vertices() const
{
	size_t num_vertices = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		if (vertices[i].get_num_in_edges() > 0
				|| vertices[i].get_num_out_edges() > 0)
			num_vertices++;
	return num_vertices;
}
<commit_msg>[Graph]: remove the code of reading binary edge lists.<commit_after>/**
 * Copyright 2013 Da Zheng
 *
 * This file is part of SA-GraphLib.
 *
 * SA-GraphLib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SA-GraphLib 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 SA-GraphLib.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>

#include "graph.h"
#include "common.h"
#include "native_file.h"

size_t read_edge_list_text(const std::string &file, std::vector<edge> &edges)
{
	FILE *f = fopen(file.c_str(), "r");
	if (f == NULL) {
		perror("fopen");
		assert(0);
	}

	ssize_t read;
	size_t len = 0;
	char *line = NULL;
	while ((read = getline(&line, &len, f)) != -1) {
		if (line[read - 1] == '\n')
			line[read - 1] = 0;
		if (line[read - 2] == '\r')
			line[read - 2] = 0;
		if (line[0] == '#')
			continue;
		char *second = strchr(line, '\t');
		assert(second);
		*second = 0;
		second++;
		if (!isnumeric(line) || !isnumeric(second)) {
			printf("%s\t%s\n", line, second);
			continue;
		}
		vertex_id_t from = atol(line);
		vertex_id_t to = atol(second);
		edges.push_back(edge(from, to));
	}
	fclose(f);
	return edges.size();
}

static struct comp_edge {
	bool operator() (const edge &e1, const edge &e2) {
		if (e1.get_from() == e2.get_from())
			return e1.get_to() < e2.get_to();
		else
			return e1.get_from() < e2.get_from();
	}
} edge_comparator;

undirected_graph *undirected_graph::create(edge edges[], size_t num_edges)
{
	undirected_graph *g = new undirected_graph();
	// Each edge appears twice and in different directions.
	// When we sort the edges with the first vertex id, we only need
	// a single scan to construct the graph in the form of
	// the adjacency list.
	edge *tmp = new edge[num_edges * 2];
	for (size_t i = 0; i < num_edges; i++) {
		tmp[2 * i] = edges[i];
		tmp[2 * i + 1] = edge(edges[i].get_to(), edges[i].get_from());
	}
	edges = tmp;
	num_edges *= 2;
	std::sort(edges, edges + num_edges, edge_comparator);

	vertex_id_t curr = edges[0].get_from();
	in_mem_undirected_vertex v(curr);
	for (size_t i = 0; i < num_edges; i++) {
		vertex_id_t id = edges[i].get_from();
		if (curr == id) {
			// We have to make sure the edge doesn't exist.
			assert(!v.has_edge(edges[i].get_to()));
			v.add_edge(edges[i].get_to());
		}
		else {
			g->add_vertex(v);
			vertex_id_t prev = curr + 1;
			curr = id;
			// The vertices without edges won't show up in the edge list,
			// but we need to fill the gap in the vertex Id space with empty
			// vertices.
			while (prev < curr) {
				v = in_mem_undirected_vertex(prev);
				prev++;
				g->add_vertex(v);
			}
			v = in_mem_undirected_vertex(curr);
			v.add_edge(edges[i].get_to());
		}
	}
	g->add_vertex(v);
	delete [] edges;
	return g;
}

undirected_graph *undirected_graph::load_edge_list_text(const std::string &file)
{
	std::vector<edge> edges;
	read_edge_list_text(file, edges);
	return create(edges.data(), edges.size());
}

undirected_graph *undirected_graph::load_adjacency_list(const std::string &file)
{
	assert(0);
}

vertex_index *undirected_graph::create_vertex_index() const
{
	return vertex_index::create<in_mem_undirected_vertex>(vertices);
}

void undirected_graph::dump(const std::string &file) const
{
	FILE *f = fopen(file.c_str(), "w");
	if (f == NULL) {
		perror("fopen");
		assert(0);
	}

	for (size_t i = 0; i < vertices.size(); i++) {
		int mem_size = vertices[i].get_serialize_size();
		char *buf = new char[mem_size];
		ext_mem_undirected_vertex::serialize(vertices[i], buf, mem_size);
		ssize_t ret = fwrite(buf, mem_size, 1, f);
		delete [] buf;
		assert(ret == 1);
	}

	fclose(f);
}

size_t undirected_graph::get_num_edges() const
{
	size_t num_edges = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		num_edges += vertices[i].get_num_edges();
	return num_edges;
}

size_t undirected_graph::get_num_non_empty_vertices() const
{
	size_t num_vertices = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		if (vertices[i].get_num_edges() > 0)
			num_vertices++;
	return num_vertices;
}

static struct comp_in_edge {
	bool operator() (const edge &e1, const edge &e2) {
		if (e1.get_to() == e2.get_to())
			return e1.get_from() < e2.get_from();
		else
			return e1.get_to() < e2.get_to();
	}
} in_edge_comparator;

directed_graph *directed_graph::create(edge edges[], size_t num_edges)
{
	directed_graph *g = new directed_graph();

	std::sort(edges, edges + num_edges, edge_comparator);
	edge *sorted_out_edges = edges;

	edge *copied_edges = new edge[num_edges];
	memcpy(copied_edges, edges, num_edges * sizeof(edges[0]));
	std::sort(copied_edges, copied_edges + num_edges, in_edge_comparator);
	edge *sorted_in_edges = copied_edges;

	vertex_id_t curr = min(sorted_out_edges[0].get_from(),
			sorted_in_edges[0].get_to());
	in_mem_directed_vertex v(curr);
	size_t out_idx = 0;
	size_t in_idx = 0;
	while (out_idx < num_edges && in_idx < num_edges) {
		while (sorted_out_edges[out_idx].get_from() == curr
				&& out_idx < num_edges) {
			v.add_out_edge(sorted_out_edges[out_idx++].get_to());
		}
		while (sorted_in_edges[in_idx].get_to() == curr
				&& in_idx < num_edges) {
			v.add_in_edge(sorted_in_edges[in_idx++].get_from());
		}
		g->add_vertex(v);
		vertex_id_t prev = curr + 1;
		if (out_idx < num_edges && in_idx < num_edges)
			curr = min(sorted_out_edges[out_idx].get_from(),
					sorted_in_edges[in_idx].get_to());
		else if (out_idx < num_edges)
			curr = sorted_out_edges[out_idx].get_from();
		else if (in_idx < num_edges)
			curr = sorted_in_edges[in_idx].get_to();
		else
			break;
		// The vertices without edges won't show up in the edge list,
		// but we need to fill the gap in the vertex Id space with empty
		// vertices.
		while (prev < curr) {
			v = in_mem_directed_vertex(prev);
			prev++;
			g->add_vertex(v);
		}
		v = in_mem_directed_vertex(curr);
	}

	assert(g->get_num_in_edges() == num_edges);
	assert(g->get_num_out_edges() == num_edges);
	delete [] copied_edges;
	return g;
}

directed_graph *directed_graph::load_edge_list_text(const std::string &file)
{
	std::vector<edge> edges;
	read_edge_list_text(file, edges);
	return create(edges.data(), edges.size());
}

directed_graph *directed_graph::load_adjacency_list(const std::string &file)
{
	assert(0);
	return NULL;
}

vertex_index *directed_graph::create_vertex_index() const
{
	return vertex_index::create<in_mem_directed_vertex>(vertices);
}

void directed_graph::dump(const std::string &file) const
{
	FILE *f = fopen(file.c_str(), "w");
	if (f == NULL) {
		perror("fopen");
		assert(0);
	}

	for (size_t i = 0; i < vertices.size(); i++) {
		int mem_size = vertices[i].get_serialize_size();
		char *buf = new char[mem_size];
		ext_mem_directed_vertex::serialize(vertices[i], buf, mem_size);
		ssize_t ret = fwrite(buf, mem_size, 1, f);
		delete [] buf;
		assert(ret == 1);
	}

	fclose(f);
}

size_t directed_graph::get_num_in_edges() const
{
	size_t num_in_edges = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		num_in_edges += vertices[i].get_num_in_edges();
	return num_in_edges;
}

size_t directed_graph::get_num_out_edges() const
{
	size_t num_out_edges = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		num_out_edges += vertices[i].get_num_out_edges();
	return num_out_edges;
}

size_t directed_graph::get_num_non_empty_vertices() const
{
	size_t num_vertices = 0;
	for (size_t i = 0; i < vertices.size(); i++)
		if (vertices[i].get_num_in_edges() > 0
				|| vertices[i].get_num_out_edges() > 0)
			num_vertices++;
	return num_vertices;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

int N;
ll x[2][100010];
typedef tuple<int, int> point;
typedef tuple<ll, int, int> edge;

int main () {
  cin >> N;
  for (auto i = 0; i < N; ++i) {
    cin >> x[0][i] >> x[1][i];
  }
  vector<edge> V;
  for (auto k = 0; k < 2; ++k) {
    vector<point> W;
    for (auto i = 0; i < N; ++i) {
      W.push_back(point(x[k][i], i));
    }
    sort(W.begin(), W.end());
    for (auto i = 0; i < N-1; ++i) {
      int d0 = get<0>(W[i]);
      int p0 = get<1>(W[i+1]);
      int d1 = get<0>(W[i]);
      int p1 = get<1>(W[i+1]);
      V.push_back(edge(abs(d0 - d1), p0, p1));
    }
  }
  sort(V.begin(), V.end());
  int used = 0;
  ll ans = 0;
  bool visited[100010];
  fill(visited, visited+100010, false);
  auto it = V.begin();
  while (used < N) {
    ll cost = get<0>(*it);
    int p0 = get<1>(*it);
    int p1 = get<2>(*it);
    ++it;
    if (visited[p0] && visited[p1]) continue;
    if (!visited[p0]) {
      visited[p0] = true;
      ++used;
    }
    if (!visited[p1]) {
      visited[p1] = true;
      ++used;
    }
    ans += cost;
  }
  cout << ans << endl;
}
<commit_msg>tried D.cpp to 'D'<commit_after>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

int N;
ll x[2][100010];
typedef tuple<int, int> point;
typedef tuple<ll, int, int> edge;

int main () {
  cin >> N;
  for (auto i = 0; i < N; ++i) {
    cin >> x[0][i] >> x[1][i];
  }
  vector<edge> V;
  for (auto k = 0; k < 2; ++k) {
    vector<point> W;
    for (auto i = 0; i < N; ++i) {
      W.push_back(point(x[k][i], i));
    }
    sort(W.begin(), W.end());
    for (auto i = 0; i < N-1; ++i) {
      int d0 = get<0>(W[i]);
      int p0 = get<1>(W[i+1]);
      int d1 = get<0>(W[i]);
      int p1 = get<1>(W[i+1]);
      V.push_back(edge(abs(d0 - d1), p0, p1));
    }
  }
  sort(V.begin(), V.end());
  int used = 0;
  ll ans = 0;
  bool visited[100010];
  fill(visited, visited+100010, false);
  auto it = V.begin();
  while (used < N) {
    ll cost = get<0>(*it);
    int p0 = get<1>(*it);
    int p1 = get<2>(*it);
    cerr << "cost = " << cost << ", p0 = " << p0 << ", p1 = " << p1 << endl;
    ++it;
    if (visited[p0] && visited[p1]) continue;
    if (!visited[p0]) {
      visited[p0] = true;
      ++used;
    }
    if (!visited[p1]) {
      visited[p1] = true;
      ++used;
    }
    ans += cost;
  }
  cout << ans << endl;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/extension_apitest.h"

#include "chrome/browser/browser.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"

// Tabs is flaky on chromeos and linux views debug build.
// http://crbug.com/48920
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) && !defined(NDEBUG)
#define MAYBE_Tabs FLAKY_Tabs
#else
#define MAYBE_Tabs Tabs
#endif

// TabOnRemoved is flaky on chromeos and linux views debug build.
// http://crbug.com/49258
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) && !defined(NDEBUG)
#define MAYBE_TabOnRemoved FLAKY_TabOnRemoved
#else
#define MAYBE_TabOnRemoved TabOnRemoved
#endif

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tabs) {
  ASSERT_TRUE(StartHTTPServer());

  // The test creates a tab and checks that the URL of the new tab
  // is that of the new tab page.  Make sure the pref that controls
  // this is set.
  browser()->profile()->GetPrefs()->SetBoolean(
      prefs::kHomePageIsNewTabPage, true);

  ASSERT_TRUE(RunExtensionTest("tabs/basics")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabGetCurrent) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/get_current")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabConnect) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/connect")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_TabOnRemoved) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/on_removed")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CaptureVisibleTabJpeg) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionSubtest("tabs/capture_visible_tab",
                                  "test_jpeg.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FAILS_CaptureVisibleTabPng) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionSubtest("tabs/capture_visible_tab",
                                  "test_png.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabsOnUpdated) {
  ASSERT_TRUE(StartHTTPServer());

  ASSERT_TRUE(RunExtensionTest("tabs/on_updated")) << message_;
}
<commit_msg>Broaden flakiness filter for ExtensionApiTest.Tabs as it now appears to fail on NDEBUG builds too.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/extension_apitest.h"

#include "chrome/browser/browser.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"

// Tabs is flaky on chromeos and linux views build.
// http://crbug.com/48920
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS)
#define MAYBE_Tabs FLAKY_Tabs
#else
#define MAYBE_Tabs Tabs
#endif

// TabOnRemoved is flaky on chromeos and linux views debug build.
// http://crbug.com/49258
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) && !defined(NDEBUG)
#define MAYBE_TabOnRemoved FLAKY_TabOnRemoved
#else
#define MAYBE_TabOnRemoved TabOnRemoved
#endif

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tabs) {
  ASSERT_TRUE(StartHTTPServer());

  // The test creates a tab and checks that the URL of the new tab
  // is that of the new tab page.  Make sure the pref that controls
  // this is set.
  browser()->profile()->GetPrefs()->SetBoolean(
      prefs::kHomePageIsNewTabPage, true);

  ASSERT_TRUE(RunExtensionTest("tabs/basics")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabGetCurrent) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/get_current")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabConnect) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/connect")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_TabOnRemoved) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionTest("tabs/on_removed")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CaptureVisibleTabJpeg) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionSubtest("tabs/capture_visible_tab",
                                  "test_jpeg.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FAILS_CaptureVisibleTabPng) {
  ASSERT_TRUE(StartHTTPServer());
  ASSERT_TRUE(RunExtensionSubtest("tabs/capture_visible_tab",
                                  "test_png.html")) << message_;
}

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabsOnUpdated) {
  ASSERT_TRUE(StartHTTPServer());

  ASSERT_TRUE(RunExtensionTest("tabs/on_updated")) << message_;
}
<|endoftext|>


<commit_before>#include "a_star.h"

using namespace std;

/* Constructor */
AStar::AStar() {
	// TODO: throw error
}

AStar::AStar(State &init) {
	root_ 			= init;
	//solutionLeaf_ = NULL;
	debugLevel_ = 0;

	/* Initialize costs of root node */
	root_.setGCost(0);
	root_.setHCost(root_.computeHCost());
	root_.setFCost(root_.getGCost() + root_.getHCost());

	/* Add initial state to queue */
	open_.push(root_);
	openVector_.push_back(root_);
	//closed_.push_back(root_);
}

/* Destructor */
AStar::~AStar() { }

/* Planning functions */
bool AStar::solve() {
	int pos, posi;

	/* Initialize closed list (open list initialized in constructor) */
	closed_.clear();

	while(!open_.empty()) {
	//for (int i = 0; i < 1500; i++) {
		/* Retrieve first element of queue and delete from queue */
		State tmp = open_.top();
		open_.pop();

		/* Keep a copy of the priority queue as a vector, so we can
		 * check if something is already in the open list
		 */

		//cout << " ############################" << endl;
		//cout << " Printing open before delete " << endl;
		//printOpen();
		//cout << " ############################" << endl;
		isOpen(&tmp, &pos);
		openVector_.erase(openVector_.begin() + pos);
		//cout << " ############################" << endl;
		//cout << " Printing open after delete " << endl;
		//cout << " ############################" << endl;
		//printOpen();
		//cout << " ############################" << endl;

		/* Add tmp to closed list */
		if(!isClosed(&tmp, &pos)) {
			closed_.push_back(tmp);
		}

		/* Check if we have found the solution */
		if(tmp.isGoal()) {
			cout << "AStar::solve(): Solution found" << endl;
			extractSolution(&tmp);
			//printSolution();
			return true;
		}

		/* Compute neighboring states */
		vector<State> neighbors = tmp.expandState();

		//cout << " ###########################" << endl;
		//cout << " Printing closed list " << endl;
		//printClosed();
		//printOpen();
		//cout << " ###########################" << endl;

		cout << "!! Computed " << neighbors.size() << " neighbors !!" << endl;
		/* Iterate over neighboring states */
		for (int i = 0; i < neighbors.size(); i++) {
			/* Compute tentative g-cost of neighbor
			 * NOTE: the distance between a neigbhor and tmp is always
			 * 		 one move
			 */
			int tentative_g_score = tmp.getGCost() + 1;

			/* Check if neighbor is already in closed list */
			if(isClosed(&neighbors.at(i), &pos)) {
				/* 1. compute g-cost of neighbor
				 * 2. if g-cost is better than that of the state in the
				 * closed_list, reopen the state (remove from closed, add to
				 * open)
				 */
				//if(tentative_g_score < neighbors.at(i).getGCost()) {
					///* Add to open list - i.e. reopen the node */
					//open_.push(neighbors.at(i));

					///* Remove from closed list */
					//closed_.erase(closed_.begin() + pos);
				//} else {
					//continue;
				//}
				continue;
			} else {
				/* 1. create new state based on parent tmp
				 * 2. compute f, g, h costs (done in constructor)
				 * 3. add to open_ list
				 */
				/* Only add to open list, if it was not already in there */
				if(!isOpen(&neighbors.at(i), &posi)) {
					cout << "--- Adding the following neighbor to the open list ---" << endl;
					neighbors.at(i).printState();
					open_.push(neighbors.at(i));
					openVector_.push_back(neighbors.at(i));
				}

				/* NOTE: technically we should check if the node is already
				 * 		 in the open list, but that's hard to do with a
				 *		 priority queue. Instead we allow to add duplicates
				 * 		 to the open list and discard them on following iterations
				 * 		 (because they will be in closed list and discarded.
				 */
			}
		}
		cout << "--- End neighbor iteration ---" << endl;
	}

	/* If the while loop terminates without calling extractSolution, then no
	 * solution has been found */
	cout << "Error AStar: No solution has been found." << endl;
	return false;
}

void AStar::extractSolution(State* solutionLeaf) {
	State* tmp = solutionLeaf;

	/*while(tmp->getParent() != NULL) {
		tmp->printState();
		solution_.push_back(*tmp);
		tmp = tmp->getParent();
	}*/
	vector<Direction> solution = solutionLeaf->getCommands();
	cout << "Solution: ";
	for(unsigned int i = solution.size()-1; i >0; i--){
	  switch(solution[i]){
      case LEFT: cout << "LEFT ";
      case RIGHT: cout << "RIGHT ";
      case UP: cout << "UP ";
      case DOWN: cout << "DOWN ";
      defualt: cout << "STAY ";
    }
  }
  cout << "End solution" << endl;;

	/* Since the solution goes from goal to initial state, reverse the vector
	 * such that it goes from initial to final state */
	reverse(solution_.begin(), solution_.end());
}

bool AStar::isOpen(State* state, int *pos) {
	bool debug = false;

	if(debug) {
		cout << "AStar::isOpen: Size of open list: " << openVector_.size() << endl;
		cout << " Comparint state vs open list element" << endl;
		state->printState();
		cout << "##### vs. ####" << endl;
	}

	for (int i = 0; i < openVector_.size(); i++) {
		if(debug) {
			openVector_.at(i).printState();
		}

		if(*state == openVector_.at(i)) {
			if(debug) {
				cout << "state == openVector_.at(" << i  << ")" << endl;
			}

			*pos = i;
			return true;
		} else {
			if(debug) {
				cout << "state != openVector_.at(" << i  << ")" << endl;
			}
		}
	}

	return false;
}

bool AStar::isClosed(State* state, int *pos) {
	bool debug = false;

	if(debug) {
		cout << "AStar::isClosed: Size of closed list: " << closed_.size() << endl;
		cout << " Comparint state vs open list element" << endl;
		state->printState();
		cout << "##### vs. ####" << endl;
	}

	for (int i = 0; i < closed_.size(); i++) {
		if(debug) {
			closed_.at(i).printState();
		}

		if(*state == closed_.at(i)) {
			if(debug) {
				cout << "state == closed_.at(" << i  << ")" << endl;
			}

			*pos = i;
			return true;
		} else {
			if(debug) {
				cout << "state != closed_.at(" << i  << ")" << endl;
			}
		}
	}

	return false;
}

/* Display functions */
void AStar::printSolution() {
	for (int i = 0; i < solution_.size(); i++) {
		cout << solution_.at(i);

		for (int j = 0; j < solution_.at(i).getWorld()->getSizeX() * 2; j++) {
			cout << "#";
		}

		cout << endl;
	}
}

void AStar::printOpen() {
	/* Can't iterate over queue, therefore printing the open list is not
	 * trivial and would involve creating a copy of the queue */
	for (int i = 0; i < openVector_.size(); i++) {
		openVector_.at(i).printState();

		for (int j = 0; j < openVector_.at(i).getWorld()->getSizeX() * 2; j++) {
			cout << "#";
		}

		cout << endl;
	}
}

void AStar::printClosed() {
	for (int i = 0; i < closed_.size(); i++) {
		closed_.at(i).printState();

		for (int j = 0; j < closed_.at(i).getWorld()->getSizeX() * 2; j++) {
			cout << "#";
		}

		cout << endl;
	}
}

/* Get functions */
statePQ AStar::getOpen() { return open_; }
std::vector<State> AStar::getClosed() { return closed_; }
vector<State> AStar::getSolution() { return solution_; }
<commit_msg>Properly prints solution<commit_after>#include "a_star.h"

using namespace std;

/* Constructor */
AStar::AStar() {
	// TODO: throw error
}

AStar::AStar(State &init) {
	root_ 			= init;
	//solutionLeaf_ = NULL;
	debugLevel_ = 0;

	/* Initialize costs of root node */
	root_.setGCost(0);
	root_.setHCost(root_.computeHCost());
	root_.setFCost(root_.getGCost() + root_.getHCost());

	/* Add initial state to queue */
	open_.push(root_);
	openVector_.push_back(root_);
	//closed_.push_back(root_);
}

/* Destructor */
AStar::~AStar() { }

/* Planning functions */
bool AStar::solve() {
	int pos, posi;

	/* Initialize closed list (open list initialized in constructor) */
	closed_.clear();

	while(!open_.empty()) {
	//for (int i = 0; i < 1500; i++) {
		/* Retrieve first element of queue and delete from queue */
		State tmp = open_.top();
		open_.pop();

		/* Keep a copy of the priority queue as a vector, so we can
		 * check if something is already in the open list
		 */

		//cout << " ############################" << endl;
		//cout << " Printing open before delete " << endl;
		//printOpen();
		//cout << " ############################" << endl;
		isOpen(&tmp, &pos);
		openVector_.erase(openVector_.begin() + pos);
		//cout << " ############################" << endl;
		//cout << " Printing open after delete " << endl;
		//cout << " ############################" << endl;
		//printOpen();
		//cout << " ############################" << endl;

		/* Add tmp to closed list */
		if(!isClosed(&tmp, &pos)) {
			closed_.push_back(tmp);
		}

		/* Check if we have found the solution */
		if(tmp.isGoal()) {
			cout << "AStar::solve(): Solution found" << endl;
			extractSolution(&tmp);
			//printSolution();
			return true;
		}

		/* Compute neighboring states */
		vector<State> neighbors = tmp.expandState();

		//cout << " ###########################" << endl;
		//cout << " Printing closed list " << endl;
		//printClosed();
		//printOpen();
		//cout << " ###########################" << endl;

		cout << "!! Computed " << neighbors.size() << " neighbors !!" << endl;
		/* Iterate over neighboring states */
		for (int i = 0; i < neighbors.size(); i++) {
			/* Compute tentative g-cost of neighbor
			 * NOTE: the distance between a neigbhor and tmp is always
			 * 		 one move
			 */
			int tentative_g_score = tmp.getGCost() + 1;

			/* Check if neighbor is already in closed list */
			if(isClosed(&neighbors.at(i), &pos)) {
				/* 1. compute g-cost of neighbor
				 * 2. if g-cost is better than that of the state in the
				 * closed_list, reopen the state (remove from closed, add to
				 * open)
				 */
				//if(tentative_g_score < neighbors.at(i).getGCost()) {
					///* Add to open list - i.e. reopen the node */
					//open_.push(neighbors.at(i));

					///* Remove from closed list */
					//closed_.erase(closed_.begin() + pos);
				//} else {
					//continue;
				//}
				continue;
			} else {
				/* 1. create new state based on parent tmp
				 * 2. compute f, g, h costs (done in constructor)
				 * 3. add to open_ list
				 */
				/* Only add to open list, if it was not already in there */
				if(!isOpen(&neighbors.at(i), &posi)) {
					cout << "--- Adding the following neighbor to the open list ---" << endl;
					neighbors.at(i).printState();
					open_.push(neighbors.at(i));
					openVector_.push_back(neighbors.at(i));
				}

				/* NOTE: technically we should check if the node is already
				 * 		 in the open list, but that's hard to do with a
				 *		 priority queue. Instead we allow to add duplicates
				 * 		 to the open list and discard them on following iterations
				 * 		 (because they will be in closed list and discarded.
				 */
			}
		}
		cout << "--- End neighbor iteration ---" << endl;
	}

	/* If the while loop terminates without calling extractSolution, then no
	 * solution has been found */
	cout << "Error AStar: No solution has been found." << endl;
	return false;
}

void AStar::extractSolution(State* solutionLeaf) {
	State* tmp = solutionLeaf;

	vector<Direction> solution = solutionLeaf->getCommands();
	cout << "Solution: ";
	for(unsigned int i = solution.size()-1; i > 0; i--){
		cout << i << " " << endl;
		cout << solution[i] << endl;
	  switch(solution[i]){
      case LEFT: cout << "LEFT "; break;
      case RIGHT: cout << "RIGHT "; break;
      case UP: cout << "UP "; break;
      case DOWN: cout << "DOWN "; break;
      default: cout << "STAY "; break;
    }
  }
  cout << "End solution" << endl;;

	/* Since the solution goes from goal to initial state, reverse the vector
	 * such that it goes from initial to final state */
	reverse(solution_.begin(), solution_.end());
}

bool AStar::isOpen(State* state, int *pos) {
	bool debug = false;

	if(debug) {
		cout << "AStar::isOpen: Size of open list: " << openVector_.size() << endl;
		cout << " Comparint state vs open list element" << endl;
		state->printState();
		cout << "##### vs. ####" << endl;
	}

	for (int i = 0; i < openVector_.size(); i++) {
		if(debug) {
			openVector_.at(i).printState();
		}

		if(*state == openVector_.at(i)) {
			if(debug) {
				cout << "state == openVector_.at(" << i  << ")" << endl;
			}

			*pos = i;
			return true;
		} else {
			if(debug) {
				cout << "state != openVector_.at(" << i  << ")" << endl;
			}
		}
	}

	return false;
}

bool AStar::isClosed(State* state, int *pos) {
	bool debug = false;

	if(debug) {
		cout << "AStar::isClosed: Size of closed list: " << closed_.size() << endl;
		cout << " Comparint state vs open list element" << endl;
		state->printState();
		cout << "##### vs. ####" << endl;
	}

	for (int i = 0; i < closed_.size(); i++) {
		if(debug) {
			closed_.at(i).printState();
		}

		if(*state == closed_.at(i)) {
			if(debug) {
				cout << "state == closed_.at(" << i  << ")" << endl;
			}

			*pos = i;
			return true;
		} else {
			if(debug) {
				cout << "state != closed_.at(" << i  << ")" << endl;
			}
		}
	}

	return false;
}

/* Display functions */
void AStar::printSolution() {
	for (int i = 0; i < solution_.size(); i++) {
		cout << solution_.at(i);

		for (int j = 0; j < solution_.at(i).getWorld()->getSizeX() * 2; j++) {
			cout << "#";
		}

		cout << endl;
	}
}

void AStar::printOpen() {
	/* Can't iterate over queue, therefore printing the open list is not
	 * trivial and would involve creating a copy of the queue */
	for (int i = 0; i < openVector_.size(); i++) {
		openVector_.at(i).printState();

		for (int j = 0; j < openVector_.at(i).getWorld()->getSizeX() * 2; j++) {
			cout << "#";
		}

		cout << endl;
	}
}

void AStar::printClosed() {
	for (int i = 0; i < closed_.size(); i++) {
		closed_.at(i).printState();

		for (int j = 0; j < closed_.at(i).getWorld()->getSizeX() * 2; j++) {
			cout << "#";
		}

		cout << endl;
	}
}

/* Get functions */
statePQ AStar::getOpen() { return open_; }
std::vector<State> AStar::getClosed() { return closed_; }
vector<State> AStar::getSolution() { return solution_; }
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: xmlenums.hxx,v $
 *
 *  $Revision: 1.1 $
 *
 *  last change: $Author: mtg $ $Date: 2001-05-16 11:46:28 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _XMLENUMS_HXX_
#define _XMLENUMS_HXX_

enum XMLForbiddenCharactersEnum
{
    XML_FORBIDDEN_CHARACTER_LANGUAGE,
    XML_FORBIDDEN_CHARACTER_COUNTRY,
    XML_FORBIDDEN_CHARACTER_VARIANT,
    XML_FORBIDDEN_CHARACTER_BEGIN_LINE,
    XML_FORBIDDEN_CHARACTER_END_LINE,
    XML_FORBIDDEN_CHARACTER_MAX
};

enum XMLSymbolDescriptorsEnum
{
    XML_SYMBOL_DESCRIPTOR_NAME,
    XML_SYMBOL_DESCRIPTOR_EXPORT_NAME,
    XML_SYMBOL_DESCRIPTOR_SYMBOL_SET,
    XML_SYMBOL_DESCRIPTOR_CHARACTER,
    XML_SYMBOL_DESCRIPTOR_FONT_NAME,
    XML_SYMBOL_DESCRIPTOR_CHAR_SET,
    XML_SYMBOL_DESCRIPTOR_FAMILY,
    XML_SYMBOL_DESCRIPTOR_PITCH,
    XML_SYMBOL_DESCRIPTOR_WEIGHT,
    XML_SYMBOL_DESCRIPTOR_ITALIC,
    XML_SYMBOL_DESCRIPTOR_MAX
};
#endif
<commit_msg>INTEGRATION: CWS ooo19126 (1.1.640); FILE MERGED 2005/09/05 14:38:36 rt 1.1.640.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: xmlenums.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 13:36:34 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _XMLENUMS_HXX_
#define _XMLENUMS_HXX_

enum XMLForbiddenCharactersEnum
{
    XML_FORBIDDEN_CHARACTER_LANGUAGE,
    XML_FORBIDDEN_CHARACTER_COUNTRY,
    XML_FORBIDDEN_CHARACTER_VARIANT,
    XML_FORBIDDEN_CHARACTER_BEGIN_LINE,
    XML_FORBIDDEN_CHARACTER_END_LINE,
    XML_FORBIDDEN_CHARACTER_MAX
};

enum XMLSymbolDescriptorsEnum
{
    XML_SYMBOL_DESCRIPTOR_NAME,
    XML_SYMBOL_DESCRIPTOR_EXPORT_NAME,
    XML_SYMBOL_DESCRIPTOR_SYMBOL_SET,
    XML_SYMBOL_DESCRIPTOR_CHARACTER,
    XML_SYMBOL_DESCRIPTOR_FONT_NAME,
    XML_SYMBOL_DESCRIPTOR_CHAR_SET,
    XML_SYMBOL_DESCRIPTOR_FAMILY,
    XML_SYMBOL_DESCRIPTOR_PITCH,
    XML_SYMBOL_DESCRIPTOR_WEIGHT,
    XML_SYMBOL_DESCRIPTOR_ITALIC,
    XML_SYMBOL_DESCRIPTOR_MAX
};
#endif
<|endoftext|>


<commit_before>#include "Test.h"
#include "SkPath.h"
#include "SkParse.h"
#include "SkSize.h"

static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
                                const SkRect& bounds) {
    REPORTER_ASSERT(reporter, p.isConvex());
    REPORTER_ASSERT(reporter, p.getBounds() == bounds);

    SkPath p2(p);
    REPORTER_ASSERT(reporter, p2.isConvex());
    REPORTER_ASSERT(reporter, p2.getBounds() == bounds);

    SkPath other;
    other.swap(p2);
    REPORTER_ASSERT(reporter, other.isConvex());
    REPORTER_ASSERT(reporter, other.getBounds() == bounds);
}

static void setFromString(SkPath* path, const char str[]) {
    bool first = true;
    while (str) {
        SkScalar x, y;
        str = SkParse::FindScalar(str, &x);
        if (NULL == str) {
            break;
        }
        str = SkParse::FindScalar(str, &y);
        SkASSERT(str);
        if (first) {
            path->moveTo(x, y);
            first = false;
        } else {
            path->lineTo(x, y);
        }
    }
}

static void test_convexity(skiatest::Reporter* reporter) {
    static const SkPath::Convexity U = SkPath::kUnknown_Convexity;
    static const SkPath::Convexity C = SkPath::kConcave_Convexity;
    static const SkPath::Convexity V = SkPath::kConvex_Convexity;

    SkPath path;

    REPORTER_ASSERT(reporter, U == SkPath::ComputeConvexity(path));
    path.addCircle(0, 0, 10);
    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
    path.addCircle(0, 0, 10);   // 2nd circle
    REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
    path.reset();
    path.addRect(0, 0, 10, 10, SkPath::kCCW_Direction);
    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
    path.reset();
    path.addRect(0, 0, 10, 10, SkPath::kCW_Direction);
    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));

    static const struct {
        const char*         fPathStr;
        SkPath::Convexity   fExpectedConvexity;
    } gRec[] = {
        { "0 0", SkPath::kUnknown_Convexity },
        { "0 0 10 10", SkPath::kUnknown_Convexity },
        { "0 0 10 10 20 20 0 0 10 10", SkPath::kUnknown_Convexity },
        { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
        { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
        { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
    };

    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
        SkPath path;
        setFromString(&path, gRec[i].fPathStr);
        SkPath::Convexity c = SkPath::ComputeConvexity(path);
        REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
    }
}

void TestPath(skiatest::Reporter* reporter);
void TestPath(skiatest::Reporter* reporter) {
    {
        SkSize size;
        size.fWidth = 3.4f;
        size.width();
        size = SkSize::Make(3,4);
        SkISize isize = SkISize::Make(3,4);
    }

    SkTSize<SkScalar>::Make(3,4);

    SkPath  p, p2;
    SkRect  bounds, bounds2;

    REPORTER_ASSERT(reporter, p.isEmpty());
    REPORTER_ASSERT(reporter, !p.isConvex());
    REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
    REPORTER_ASSERT(reporter, !p.isInverseFillType());
    REPORTER_ASSERT(reporter, p == p2);
    REPORTER_ASSERT(reporter, !(p != p2));

    REPORTER_ASSERT(reporter, p.getBounds().isEmpty());

    bounds.set(0, 0, SK_Scalar1, SK_Scalar1);

    p.setIsConvex(false);
    p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
    check_convex_bounds(reporter, p, bounds);

    p.reset();
    p.setIsConvex(false);
    p.addOval(bounds);
    check_convex_bounds(reporter, p, bounds);

    p.reset();
    p.setIsConvex(false);
    p.addRect(bounds);
    check_convex_bounds(reporter, p, bounds);

    REPORTER_ASSERT(reporter, p != p2);
    REPORTER_ASSERT(reporter, !(p == p2));

    // does getPoints return the right result
    REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
    SkPoint pts[4];
    int count = p.getPoints(pts, 4);
    REPORTER_ASSERT(reporter, count == 4);
    bounds2.set(pts, 4);
    REPORTER_ASSERT(reporter, bounds == bounds2);

    bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
    p.offset(SK_Scalar1*3, SK_Scalar1*4);
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

#if 0 // isRect needs to be implemented
    REPORTER_ASSERT(reporter, p.isRect(NULL));
    bounds.setEmpty();
    REPORTER_ASSERT(reporter, p.isRect(&bounds2));
    REPORTER_ASSERT(reporter, bounds == bounds2);

    // now force p to not be a rect
    bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
    p.addRect(bounds);
    REPORTER_ASSERT(reporter, !p.isRect(NULL));
#endif

    SkPoint pt;

    p.moveTo(SK_Scalar1, 0);
    p.getLastPt(&pt);
    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);

    // check that reset and rewind clear the convex hint back to false
    p.setIsConvex(false);
    REPORTER_ASSERT(reporter, !p.isConvex());
    p.setIsConvex(true);
    REPORTER_ASSERT(reporter, p.isConvex());
    p.reset();
    REPORTER_ASSERT(reporter, !p.isConvex());
    p.setIsConvex(true);
    REPORTER_ASSERT(reporter, p.isConvex());
    p.rewind();
    REPORTER_ASSERT(reporter, !p.isConvex());

    test_convexity(reporter);
}

#include "TestClassDef.h"
DEFINE_TESTCLASS("Path", PathTestClass, TestPath)
<commit_msg>migrate more tests from GrPath.cpp<commit_after>#include "Test.h"
#include "SkPath.h"
#include "SkParse.h"
#include "SkSize.h"

static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
                            SkPath::Convexity expected) {
    SkPath::Convexity c = SkPath::ComputeConvexity(path);
    REPORTER_ASSERT(reporter, c == expected);
}

static void test_convexity2(skiatest::Reporter* reporter) {
    SkPath pt;
    pt.moveTo(0, 0);
    pt.close();
//    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
    check_convexity(reporter, pt, SkPath::kUnknown_Convexity);

    SkPath line;
    line.moveTo(12, 20);
    line.lineTo(-12, -20);
    line.close();
    //    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
    check_convexity(reporter, pt, SkPath::kUnknown_Convexity);

    SkPath triLeft;
    triLeft.moveTo(0, 0);
    triLeft.lineTo(1, 0);
    triLeft.lineTo(1, 1);
    triLeft.close();
    check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);

    SkPath triRight;
    triRight.moveTo(0, 0);
    triRight.lineTo(-1, 0);
    triRight.lineTo(1, 1);
    triRight.close();
    check_convexity(reporter, triRight, SkPath::kConvex_Convexity);

    SkPath square;
    square.moveTo(0, 0);
    square.lineTo(1, 0);
    square.lineTo(1, 1);
    square.lineTo(0, 1);
    square.close();
    check_convexity(reporter, square, SkPath::kConvex_Convexity);

    SkPath redundantSquare;
    redundantSquare.moveTo(0, 0);
    redundantSquare.lineTo(0, 0);
    redundantSquare.lineTo(0, 0);
    redundantSquare.lineTo(1, 0);
    redundantSquare.lineTo(1, 0);
    redundantSquare.lineTo(1, 0);
    redundantSquare.lineTo(1, 1);
    redundantSquare.lineTo(1, 1);
    redundantSquare.lineTo(1, 1);
    redundantSquare.lineTo(0, 1);
    redundantSquare.lineTo(0, 1);
    redundantSquare.lineTo(0, 1);
    redundantSquare.close();
    check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);

    SkPath bowTie;
    bowTie.moveTo(0, 0);
    bowTie.lineTo(0, 0);
    bowTie.lineTo(0, 0);
    bowTie.lineTo(1, 1);
    bowTie.lineTo(1, 1);
    bowTie.lineTo(1, 1);
    bowTie.lineTo(1, 0);
    bowTie.lineTo(1, 0);
    bowTie.lineTo(1, 0);
    bowTie.lineTo(0, 1);
    bowTie.lineTo(0, 1);
    bowTie.lineTo(0, 1);
    bowTie.close();
    check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);

    SkPath spiral;
    spiral.moveTo(0, 0);
    spiral.lineTo(1, 0);
    spiral.lineTo(1, 1);
    spiral.lineTo(0, 1);
    spiral.lineTo(0,.5);
    spiral.lineTo(.5,.5);
    spiral.lineTo(.5,.75);
    spiral.close();
//    check_convexity(reporter, spiral, SkPath::kConcave_Convexity);

    SkPath dent;
    dent.moveTo(0, 0);
    dent.lineTo(1, 1);
    dent.lineTo(0, 1);
    dent.lineTo(-.5,2);
    dent.lineTo(-2, 1);
    dent.close();
    check_convexity(reporter, dent, SkPath::kConcave_Convexity);
}

static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
                                const SkRect& bounds) {
    REPORTER_ASSERT(reporter, p.isConvex());
    REPORTER_ASSERT(reporter, p.getBounds() == bounds);

    SkPath p2(p);
    REPORTER_ASSERT(reporter, p2.isConvex());
    REPORTER_ASSERT(reporter, p2.getBounds() == bounds);

    SkPath other;
    other.swap(p2);
    REPORTER_ASSERT(reporter, other.isConvex());
    REPORTER_ASSERT(reporter, other.getBounds() == bounds);
}

static void setFromString(SkPath* path, const char str[]) {
    bool first = true;
    while (str) {
        SkScalar x, y;
        str = SkParse::FindScalar(str, &x);
        if (NULL == str) {
            break;
        }
        str = SkParse::FindScalar(str, &y);
        SkASSERT(str);
        if (first) {
            path->moveTo(x, y);
            first = false;
        } else {
            path->lineTo(x, y);
        }
    }
}

static void test_convexity(skiatest::Reporter* reporter) {
    static const SkPath::Convexity U = SkPath::kUnknown_Convexity;
    static const SkPath::Convexity C = SkPath::kConcave_Convexity;
    static const SkPath::Convexity V = SkPath::kConvex_Convexity;

    SkPath path;

    REPORTER_ASSERT(reporter, U == SkPath::ComputeConvexity(path));
    path.addCircle(0, 0, 10);
    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
    path.addCircle(0, 0, 10);   // 2nd circle
    REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
    path.reset();
    path.addRect(0, 0, 10, 10, SkPath::kCCW_Direction);
    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
    path.reset();
    path.addRect(0, 0, 10, 10, SkPath::kCW_Direction);
    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));

    static const struct {
        const char*         fPathStr;
        SkPath::Convexity   fExpectedConvexity;
    } gRec[] = {
        { "0 0", SkPath::kUnknown_Convexity },
        { "0 0 10 10", SkPath::kUnknown_Convexity },
        { "0 0 10 10 20 20 0 0 10 10", SkPath::kUnknown_Convexity },
        { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
        { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
        { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
    };

    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
        SkPath path;
        setFromString(&path, gRec[i].fPathStr);
        SkPath::Convexity c = SkPath::ComputeConvexity(path);
        REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
    }
}

void TestPath(skiatest::Reporter* reporter);
void TestPath(skiatest::Reporter* reporter) {
    {
        SkSize size;
        size.fWidth = 3.4f;
        size.width();
        size = SkSize::Make(3,4);
        SkISize isize = SkISize::Make(3,4);
    }

    SkTSize<SkScalar>::Make(3,4);

    SkPath  p, p2;
    SkRect  bounds, bounds2;

    REPORTER_ASSERT(reporter, p.isEmpty());
    REPORTER_ASSERT(reporter, !p.isConvex());
    REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
    REPORTER_ASSERT(reporter, !p.isInverseFillType());
    REPORTER_ASSERT(reporter, p == p2);
    REPORTER_ASSERT(reporter, !(p != p2));

    REPORTER_ASSERT(reporter, p.getBounds().isEmpty());

    bounds.set(0, 0, SK_Scalar1, SK_Scalar1);

    p.setIsConvex(false);
    p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
    check_convex_bounds(reporter, p, bounds);

    p.reset();
    p.setIsConvex(false);
    p.addOval(bounds);
    check_convex_bounds(reporter, p, bounds);

    p.reset();
    p.setIsConvex(false);
    p.addRect(bounds);
    check_convex_bounds(reporter, p, bounds);

    REPORTER_ASSERT(reporter, p != p2);
    REPORTER_ASSERT(reporter, !(p == p2));

    // does getPoints return the right result
    REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
    SkPoint pts[4];
    int count = p.getPoints(pts, 4);
    REPORTER_ASSERT(reporter, count == 4);
    bounds2.set(pts, 4);
    REPORTER_ASSERT(reporter, bounds == bounds2);

    bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
    p.offset(SK_Scalar1*3, SK_Scalar1*4);
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

#if 0 // isRect needs to be implemented
    REPORTER_ASSERT(reporter, p.isRect(NULL));
    bounds.setEmpty();
    REPORTER_ASSERT(reporter, p.isRect(&bounds2));
    REPORTER_ASSERT(reporter, bounds == bounds2);

    // now force p to not be a rect
    bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
    p.addRect(bounds);
    REPORTER_ASSERT(reporter, !p.isRect(NULL));
#endif

    SkPoint pt;

    p.moveTo(SK_Scalar1, 0);
    p.getLastPt(&pt);
    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);

    // check that reset and rewind clear the convex hint back to false
    p.setIsConvex(false);
    REPORTER_ASSERT(reporter, !p.isConvex());
    p.setIsConvex(true);
    REPORTER_ASSERT(reporter, p.isConvex());
    p.reset();
    REPORTER_ASSERT(reporter, !p.isConvex());
    p.setIsConvex(true);
    REPORTER_ASSERT(reporter, p.isConvex());
    p.rewind();
    REPORTER_ASSERT(reporter, !p.isConvex());

    test_convexity(reporter);
    test_convexity2(reporter);
}

#include "TestClassDef.h"
DEFINE_TESTCLASS("Path", PathTestClass, TestPath)
<|endoftext|>