<commit_before>/***************************************************************************
*   Copyright © 2003 Unai Garro <ugarro@gmail.com>                        *
*                                                                         *
*   This program 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 2 of the License, or     *
*   (at your option) any later version.                                   *
***************************************************************************/

#include "ingredientmatcherdialog.h"

#include "datablocks/recipelist.h"
#include "widgets/ingredientlistview.h"
#include "datablocks/elementlist.h"
#include "backends/recipedb.h"
#include "widgets/krelistview.h"
#include "widgets/unitcombobox.h"
#include "widgets/fractioninput.h"
#include "widgets/amountunitinput.h"
#include "datablocks/mixednumber.h"
#include "actionshandlers/recipeactionshandler.h"

#include <q3header.h>
#include <QPainter>
#include <q3groupbox.h>
#include <QPointer>
//Added by qt3to4:
#include <QHBoxLayout>
#include <Q3ValueList>
#include <QLabel>
#include <QVBoxLayout>

#include <kapplication.h>
#include <kcursor.h>
#include <klocale.h>
#include <knuminput.h>
#include <kconfig.h>
#include <kglobal.h>
#include <kdebug.h>
#include <kdialog.h>
#include <kvbox.h>
#include <KHBox>

#include "profiling.h"

IngredientMatcherDialog::IngredientMatcherDialog( QWidget *parent, RecipeDB *db ) : QSplitter( parent )
{
	// Initialize internal variables
	database = db;

	//Design the dialog

	setOrientation( Qt::Vertical );
	setChildrenCollapsible( false );

	QWidget * upperBox = new QWidget( this );

	// Ingredient list
	QHBoxLayout *layout2 = new QHBoxLayout;
	layout2->setObjectName( "layout2" );

	allIngListView = new KreListView( this, QString(), true, 0 );
	StdIngredientListView *list_view = new StdIngredientListView(allIngListView,database);
	list_view->setSelectionMode( Q3ListView::Multi );
	allIngListView->setListView(list_view);
	layout2->addWidget( allIngListView );
	allIngListView->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );

	QVBoxLayout *layout1 = new QVBoxLayout;
	layout1->addStretch();
	layout1->setObjectName( "layout1" );

	addButton = new KPushButton;
	addButton->setObjectName( "addButton" );
	addButton->setIcon( KIcon( "arrow-right" ) );
	addButton->setFixedSize( QSize( 32, 32 ) );
	layout1->addWidget( addButton );

	removeButton = new KPushButton;
	removeButton->setObjectName( "removeButton" );
	removeButton->setIcon( KIcon( "arrow-left" ) );
	removeButton->setFixedSize( QSize( 32, 32 ) );
	layout1->addWidget( removeButton );
	layout1->addStretch();
	layout2->addLayout( layout1 );

	ingListView = new KreListView( this, QString(), true );
	ingListView->listView() ->addColumn( i18nc( "@title:column", "Ingredient (required?)" ) );
	ingListView->listView() ->addColumn( i18nc( "@title:column", "Amount Available" ) );
	layout2->addWidget( ingListView );
	upperBox->setLayout( layout2 );
	addWidget( upperBox );
	ingListView->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );

	KVBox * lowerBox = new KVBox( this );

	// Box to select allowed number of missing ingredients
	missingBox = new KHBox( lowerBox );
	missingNumberLabel = new QLabel( missingBox );
	missingNumberLabel->setText( i18nc(
		"@label:spinbox Number of missing ingredients allowed when doing a search by ingredients",
		"Missing ingredients allowed:" ) );
	missingNumberSpinBox = new KIntSpinBox( missingBox );
	missingNumberSpinBox->setMinimum( -1 );
	missingNumberSpinBox->setSpecialValueText( i18nc(
		"@item Any amount of ingredients missing when doing a search by ingredients", "Any" ) );

	// Found recipe list
	recipeListView = new KreListView( lowerBox, i18nc( "@title", "Matching Recipes" ), false, 1, missingBox );
	recipeListView->listView() ->setAllColumnsShowFocus( true );

	recipeListView->listView() ->addColumn( i18nc( "@title:column Recipe Title", "Title" ) );

	KConfigGroup config( KGlobal::config(), "Advanced" );
	bool show_id = config.readEntry( "ShowID", false );
	recipeListView->listView() ->addColumn( "Id" , show_id ? -1 : 0 );

	recipeListView->listView()->addColumn( i18nc( "@title:column Missing ingredients in a search result",
		"Missing Ingredients" ) );

	recipeListView->listView() ->setSorting( -1 );
	recipeListView->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );

	actionHandler = new RecipeActionsHandler( recipeListView->listView(), database );

	KHBox *buttonBox = new KHBox( lowerBox );

	okButton = new KPushButton( buttonBox );
	okButton->setIcon( KIcon( "dialog-ok" ) );
	okButton->setText( i18nc( "@action:button", "Find matching recipes" ) );

	//buttonBox->layout()->addItem( new QSpacerItem( 10,10, QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ) );

	clearButton = new KPushButton( buttonBox );
	clearButton->setIcon( KIcon( "edit-clear" ) );
	clearButton->setText( i18nc( "@action:button Clear search criteria", "Clear" ) );

	addWidget( lowerBox );

	// Connect signals & slots
	connect ( okButton, SIGNAL( clicked() ), this, SLOT( findRecipes() ) );
	connect ( clearButton, SIGNAL( clicked() ), recipeListView->listView(), SLOT( clear() ) );
	connect ( clearButton, SIGNAL( clicked() ), this, SLOT( unselectIngredients() ) );
	connect( recipeListView->listView(), SIGNAL( selectionChanged() ), this, SLOT( haveSelectedItems() ) );
	connect ( actionHandler, SIGNAL( recipeSelected( int, int ) ), SIGNAL( recipeSelected( int, int ) ) );
	connect( addButton, SIGNAL( clicked() ), this, SLOT( addIngredient() ) );
	connect( removeButton, SIGNAL( clicked() ), this, SLOT( removeIngredient() ) );
	connect( ingListView->listView(), SIGNAL( doubleClicked( Q3ListViewItem*, const QPoint &, int ) ), SLOT( itemRenamed( Q3ListViewItem*, const QPoint &, int ) ) );
}

IngredientMatcherDialog::~IngredientMatcherDialog()
{
}

void IngredientMatcherDialog::itemRenamed( Q3ListViewItem* item, const QPoint &, int col )
{
	if ( col == 1 ) {
		QPointer<KDialog> amountEditDialog = new KDialog(this);
		amountEditDialog->setCaption(i18nc("@title:window", "Enter amount"));
		amountEditDialog->setButtons(KDialog::Cancel | KDialog::Ok);
		amountEditDialog->setDefaultButton(KDialog::Ok);
		amountEditDialog->setModal( false );
		Q3GroupBox *box = new Q3GroupBox( 1, Qt::Horizontal, i18nc("@title:group", "Amount"), amountEditDialog );
		AmountUnitInput *amountEdit = new AmountUnitInput( box, database );
		// Set the values from the item
		if ( !item->text(1).isEmpty() ) {
			amountEdit->setAmount( MixedNumber::fromString(item->text(2)) );
			Unit u;
			u.setId(item->text(3).toInt());
			amountEdit->setUnit( u );
		} else {
			amountEdit->setAmount( MixedNumber(-1) );
			Unit u;
			u.setId(-1);
			amountEdit->setUnit( u );
		}

		amountEditDialog->setMainWidget(box);
		amountEditDialog->adjustSize();
		amountEditDialog->resize( 400, amountEditDialog->size().height() );
		amountEditDialog->setFixedHeight( amountEditDialog->size().height() );

		if ( amountEditDialog->exec() == QDialog::Accepted ) {
			MixedNumber amount = amountEdit->amount();
			Unit unit = amountEdit->unit();

			if ( amount.toDouble() <= 1e-5 ) {
				amount = MixedNumber(-1);
				unit.setId(-1);

				item->setText(1,QString());
			} else {
				item->setText(1,amount.toString()+' '+unit.determineName(amount.toDouble(), /*useAbbrev=*/false));
			}

			item->setText(2,amount.toString());
			item->setText(3,QString::number(unit.id()));

			IngredientList::iterator ing_it = m_item_ing_map[item];
			(*ing_it).amount = amount.toDouble();
			(*ing_it).units = unit;
		}

		delete amountEditDialog;
	}
}

void IngredientMatcherDialog::addIngredient()
{
	QList<Q3ListViewItem *> items = allIngListView->listView()->selectedItems();
	if ( !items.isEmpty() ) {
		for (int i = 0; i < items.size(); ++i) {
			bool dup = false;
			for ( Q3ListViewItem *exists_it = ingListView->listView()->firstChild(); exists_it; exists_it = exists_it->nextSibling() ) {
				if ( exists_it->text( 0 ) == items[i]->text( 0 ) ) {
					dup = true;
					break;
				}
			}

			if ( !dup ) {
				Q3ListViewItem * new_item = new Q3CheckListItem( ingListView->listView(), items[i]->text( 0 ), Q3CheckListItem::CheckBox );

				ingListView->listView() ->setSelected( new_item, true );
				ingListView->listView() ->ensureItemVisible( new_item );
				allIngListView->listView() ->setSelected( items[i], false );

				m_ingredientList.append( Ingredient( items[i]->text( 0 ), 0, Unit(), -1, items[i]->text( 1 ).toInt() ) );
				m_item_ing_map.insert( new_item, m_ingredientList.end()-- );
			}
		}
	}
}

void IngredientMatcherDialog::removeIngredient()
{
	Q3ListViewItem * item = ingListView->listView() ->selectedItem();
	if ( item ) {
		for ( IngredientList::iterator it = m_ingredientList.begin(); it != m_ingredientList.end(); ++it ) {
			if ( *m_item_ing_map.find( item ) == it ) {
				m_ingredientList.remove( it );
				m_item_ing_map.remove( item );
				break;
			}
		}
		delete item;
	}
}

void IngredientMatcherDialog::unselectIngredients()
{
	ingListView->listView()->clear();
	for ( Q3ListViewItem *it = allIngListView->listView()->firstChild(); it; it = it->nextSibling() )
		allIngListView->listView()->setSelected(it,false);
	m_ingredientList.clear();
	m_item_ing_map.clear();
}

void IngredientMatcherDialog::findRecipes( void )
{
	KApplication::setOverrideCursor( Qt::WaitCursor );

	START_TIMER("Ingredient Matcher: loading database data");

	RecipeList rlist;
	database->loadRecipes( &rlist, RecipeDB::Title | RecipeDB::NamesOnly | RecipeDB::Ingredients | RecipeDB::IngredientAmounts );

	END_TIMER();
	START_TIMER("Ingredient Matcher: analyzing data for matching recipes");

	// Clear the list
	recipeListView->listView() ->clear();

	// Now show the recipes with ingredients that are contained in the previous set
	// of ingredients
	RecipeList incompleteRecipes;
	QList <int> missingNumbers;
	Q3ValueList <IngredientList> missingIngredients;

	RecipeList::Iterator it;
	for ( it = rlist.begin();it != rlist.end();++it ) {
		IngredientList il = ( *it ).ingList;
		if ( il.isEmpty() )
			continue;

		IngredientList missing;
		if ( m_ingredientList.containsSubSet( il, missing, true, database ) ) {
			new CustomRecipeListItem( recipeListView->listView(), *it );
		}
		else {
			incompleteRecipes.append( *it );
			missingIngredients.append( missing );
			missingNumbers.append( missing.count() );
		}
	}
	END_TIMER();

	//Check if the user wants to show missing ingredients

	if ( missingNumberSpinBox->value() == 0 ) {
		KApplication::restoreOverrideCursor();
		return ;
	} //"None"

	START_TIMER("Ingredient Matcher: searching for and displaying partial matches");

	IngredientList requiredIngredients;
	for ( Q3ListViewItem *it = ingListView->listView()->firstChild(); it; it = it->nextSibling() ) {
		if ( ((Q3CheckListItem*)it)->isOn() )
			requiredIngredients << *m_item_ing_map[it];
	}

	// Classify recipes with missing ingredients in different lists by amount
	QList<int>::Iterator nit;
	Q3ValueList<IngredientList>::Iterator ilit;
	int missingNoAllowed = missingNumberSpinBox->value();

	if ( missingNoAllowed == -1 )  // "Any"
	{
		for ( nit = missingNumbers.begin();nit != missingNumbers.end();++nit )
			if ( ( *nit ) > missingNoAllowed )
				missingNoAllowed = ( *nit );
	}

	for ( int missingNo = 1; missingNo <= missingNoAllowed; missingNo++ ) {
		nit = missingNumbers.begin();
		ilit = missingIngredients.begin();

		bool titleShownYet = false;

		for ( it = incompleteRecipes.begin();it != incompleteRecipes.end();++it, ++nit, ++ilit ) {
			if ( !( *it ).ingList.containsAny( m_ingredientList ) )
				continue;

			if ( !( *it ).ingList.containsSubSet( requiredIngredients ) )
				continue;

			if ( ( *nit ) == missingNo ) {
				if ( !titleShownYet ) {
					new SectionItem( recipeListView->listView(), i18ncp( "@label:textbox", "You are missing 1 ingredient for:", "You are missing %1 ingredients for:", missingNo ) );
					titleShownYet = true;
				}
				new CustomRecipeListItem( recipeListView->listView(), *it, *ilit );
			}
		}
	}
	END_TIMER();

	KApplication::restoreOverrideCursor();
}

void IngredientMatcherDialog::reload( ReloadFlags flag )
{
	( ( StdIngredientListView* ) allIngListView->listView() ) ->reload(flag);
}

RecipeActionsHandler* IngredientMatcherDialog::getActionsHandler() const
{
	return actionHandler;
}

void IngredientMatcherDialog::addAction( KAction * action )
{
	actionHandler->addRecipeAction( action );
}

void IngredientMatcherDialog::haveSelectedItems()
{
	QList<int> list = actionHandler->recipeIDs();
	if ( list.isEmpty() )
		emit recipeSelected( false );
	else
		emit recipeSelected( true );
}

void SectionItem::paintCell ( QPainter * p, const QColorGroup & /*cg*/, int column, int width, int /*align*/ )
{
	// Draw the section's deco
	p->setPen( KGlobalSettings::activeTitleColor() );
	p->setBrush( KGlobalSettings::activeTitleColor() );
	p->drawRect( 0, 0, width, height() );

	// Draw the section's text
	if ( column == 0 ) {
		QFont titleFont = KGlobalSettings::windowTitleFont ();
		p->setFont( titleFont );

		p->setPen( KGlobalSettings::activeTextColor() );
		p->drawText( 0, 0, width, height(), Qt::AlignLeft | Qt::AlignVCenter, mText );
	}
}

#include "ingredientmatcherdialog.moc"
<commit_msg>Fix iterator use<commit_after>/***************************************************************************
*   Copyright © 2003 Unai Garro <ugarro@gmail.com>                        *
*                                                                         *
*   This program 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 2 of the License, or     *
*   (at your option) any later version.                                   *
***************************************************************************/

#include "ingredientmatcherdialog.h"

#include "datablocks/recipelist.h"
#include "widgets/ingredientlistview.h"
#include "datablocks/elementlist.h"
#include "backends/recipedb.h"
#include "widgets/krelistview.h"
#include "widgets/unitcombobox.h"
#include "widgets/fractioninput.h"
#include "widgets/amountunitinput.h"
#include "datablocks/mixednumber.h"
#include "actionshandlers/recipeactionshandler.h"

#include <q3header.h>
#include <QPainter>
#include <q3groupbox.h>
#include <QPointer>
//Added by qt3to4:
#include <QHBoxLayout>
#include <Q3ValueList>
#include <QLabel>
#include <QVBoxLayout>

#include <kapplication.h>
#include <kcursor.h>
#include <klocale.h>
#include <knuminput.h>
#include <kconfig.h>
#include <kglobal.h>
#include <kdebug.h>
#include <kdialog.h>
#include <kvbox.h>
#include <KHBox>

#include "profiling.h"

IngredientMatcherDialog::IngredientMatcherDialog( QWidget *parent, RecipeDB *db ) : QSplitter( parent )
{
	// Initialize internal variables
	database = db;

	//Design the dialog

	setOrientation( Qt::Vertical );
	setChildrenCollapsible( false );

	QWidget * upperBox = new QWidget( this );

	// Ingredient list
	QHBoxLayout *layout2 = new QHBoxLayout;
	layout2->setObjectName( "layout2" );

	allIngListView = new KreListView( this, QString(), true, 0 );
	StdIngredientListView *list_view = new StdIngredientListView(allIngListView,database);
	list_view->setSelectionMode( Q3ListView::Multi );
	allIngListView->setListView(list_view);
	layout2->addWidget( allIngListView );
	allIngListView->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );

	QVBoxLayout *layout1 = new QVBoxLayout;
	layout1->addStretch();
	layout1->setObjectName( "layout1" );

	addButton = new KPushButton;
	addButton->setObjectName( "addButton" );
	addButton->setIcon( KIcon( "arrow-right" ) );
	addButton->setFixedSize( QSize( 32, 32 ) );
	layout1->addWidget( addButton );

	removeButton = new KPushButton;
	removeButton->setObjectName( "removeButton" );
	removeButton->setIcon( KIcon( "arrow-left" ) );
	removeButton->setFixedSize( QSize( 32, 32 ) );
	layout1->addWidget( removeButton );
	layout1->addStretch();
	layout2->addLayout( layout1 );

	ingListView = new KreListView( this, QString(), true );
	ingListView->listView() ->addColumn( i18nc( "@title:column", "Ingredient (required?)" ) );
	ingListView->listView() ->addColumn( i18nc( "@title:column", "Amount Available" ) );
	layout2->addWidget( ingListView );
	upperBox->setLayout( layout2 );
	addWidget( upperBox );
	ingListView->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );

	KVBox * lowerBox = new KVBox( this );

	// Box to select allowed number of missing ingredients
	missingBox = new KHBox( lowerBox );
	missingNumberLabel = new QLabel( missingBox );
	missingNumberLabel->setText( i18nc(
		"@label:spinbox Number of missing ingredients allowed when doing a search by ingredients",
		"Missing ingredients allowed:" ) );
	missingNumberSpinBox = new KIntSpinBox( missingBox );
	missingNumberSpinBox->setMinimum( -1 );
	missingNumberSpinBox->setSpecialValueText( i18nc(
		"@item Any amount of ingredients missing when doing a search by ingredients", "Any" ) );

	// Found recipe list
	recipeListView = new KreListView( lowerBox, i18nc( "@title", "Matching Recipes" ), false, 1, missingBox );
	recipeListView->listView() ->setAllColumnsShowFocus( true );

	recipeListView->listView() ->addColumn( i18nc( "@title:column Recipe Title", "Title" ) );

	KConfigGroup config( KGlobal::config(), "Advanced" );
	bool show_id = config.readEntry( "ShowID", false );
	recipeListView->listView() ->addColumn( "Id" , show_id ? -1 : 0 );

	recipeListView->listView()->addColumn( i18nc( "@title:column Missing ingredients in a search result",
		"Missing Ingredients" ) );

	recipeListView->listView() ->setSorting( -1 );
	recipeListView->setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );

	actionHandler = new RecipeActionsHandler( recipeListView->listView(), database );

	KHBox *buttonBox = new KHBox( lowerBox );

	okButton = new KPushButton( buttonBox );
	okButton->setIcon( KIcon( "dialog-ok" ) );
	okButton->setText( i18nc( "@action:button", "Find matching recipes" ) );

	//buttonBox->layout()->addItem( new QSpacerItem( 10,10, QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ) );

	clearButton = new KPushButton( buttonBox );
	clearButton->setIcon( KIcon( "edit-clear" ) );
	clearButton->setText( i18nc( "@action:button Clear search criteria", "Clear" ) );

	addWidget( lowerBox );

	// Connect signals & slots
	connect ( okButton, SIGNAL( clicked() ), this, SLOT( findRecipes() ) );
	connect ( clearButton, SIGNAL( clicked() ), recipeListView->listView(), SLOT( clear() ) );
	connect ( clearButton, SIGNAL( clicked() ), this, SLOT( unselectIngredients() ) );
	connect( recipeListView->listView(), SIGNAL( selectionChanged() ), this, SLOT( haveSelectedItems() ) );
	connect ( actionHandler, SIGNAL( recipeSelected( int, int ) ), SIGNAL( recipeSelected( int, int ) ) );
	connect( addButton, SIGNAL( clicked() ), this, SLOT( addIngredient() ) );
	connect( removeButton, SIGNAL( clicked() ), this, SLOT( removeIngredient() ) );
	connect( ingListView->listView(), SIGNAL( doubleClicked( Q3ListViewItem*, const QPoint &, int ) ), SLOT( itemRenamed( Q3ListViewItem*, const QPoint &, int ) ) );
}

IngredientMatcherDialog::~IngredientMatcherDialog()
{
}

void IngredientMatcherDialog::itemRenamed( Q3ListViewItem* item, const QPoint &, int col )
{
	if ( col == 1 ) {
		QPointer<KDialog> amountEditDialog = new KDialog(this);
		amountEditDialog->setCaption(i18nc("@title:window", "Enter amount"));
		amountEditDialog->setButtons(KDialog::Cancel | KDialog::Ok);
		amountEditDialog->setDefaultButton(KDialog::Ok);
		amountEditDialog->setModal( false );
		Q3GroupBox *box = new Q3GroupBox( 1, Qt::Horizontal, i18nc("@title:group", "Amount"), amountEditDialog );
		AmountUnitInput *amountEdit = new AmountUnitInput( box, database );
		// Set the values from the item
		if ( !item->text(1).isEmpty() ) {
			amountEdit->setAmount( MixedNumber::fromString(item->text(2)) );
			Unit u;
			u.setId(item->text(3).toInt());
			amountEdit->setUnit( u );
		} else {
			amountEdit->setAmount( MixedNumber(-1) );
			Unit u;
			u.setId(-1);
			amountEdit->setUnit( u );
		}

		amountEditDialog->setMainWidget(box);
		amountEditDialog->adjustSize();
		amountEditDialog->resize( 400, amountEditDialog->size().height() );
		amountEditDialog->setFixedHeight( amountEditDialog->size().height() );

		if ( amountEditDialog->exec() == QDialog::Accepted ) {
			MixedNumber amount = amountEdit->amount();
			Unit unit = amountEdit->unit();

			if ( amount.toDouble() <= 1e-5 ) {
				amount = MixedNumber(-1);
				unit.setId(-1);

				item->setText(1,QString());
			} else {
				item->setText(1,amount.toString()+' '+unit.determineName(amount.toDouble(), /*useAbbrev=*/false));
			}

			item->setText(2,amount.toString());
			item->setText(3,QString::number(unit.id()));

			IngredientList::iterator ing_it = m_item_ing_map[item];
			(*ing_it).amount = amount.toDouble();
			(*ing_it).units = unit;
		}

		delete amountEditDialog;
	}
}

void IngredientMatcherDialog::addIngredient()
{
	QList<Q3ListViewItem *> items = allIngListView->listView()->selectedItems();
	if ( !items.isEmpty() ) {
		for (int i = 0; i < items.size(); ++i) {
			bool dup = false;
			for ( Q3ListViewItem *exists_it = ingListView->listView()->firstChild(); exists_it; exists_it = exists_it->nextSibling() ) {
				if ( exists_it->text( 0 ) == items[i]->text( 0 ) ) {
					dup = true;
					break;
				}
			}

			if ( !dup ) {
				Q3ListViewItem * new_item = new Q3CheckListItem( ingListView->listView(), items[i]->text( 0 ), Q3CheckListItem::CheckBox );

				ingListView->listView() ->setSelected( new_item, true );
				ingListView->listView() ->ensureItemVisible( new_item );
				allIngListView->listView() ->setSelected( items[i], false );

				IngredientList::iterator it = m_ingredientList.append( Ingredient( items[i]->text( 0 ), 0, Unit(), -1, items[i]->text( 1 ).toInt() ) );
				m_item_ing_map.insert( new_item, it );
			}
		}
	}
}

void IngredientMatcherDialog::removeIngredient()
{
	Q3ListViewItem * item = ingListView->listView() ->selectedItem();
	if ( item ) {
		for ( IngredientList::iterator it = m_ingredientList.begin(); it != m_ingredientList.end(); ++it ) {
			if ( *m_item_ing_map.find( item ) == it ) {
				m_ingredientList.remove( it );
				m_item_ing_map.remove( item );
				break;
			}
		}
		delete item;
	}
}

void IngredientMatcherDialog::unselectIngredients()
{
	ingListView->listView()->clear();
	for ( Q3ListViewItem *it = allIngListView->listView()->firstChild(); it; it = it->nextSibling() )
		allIngListView->listView()->setSelected(it,false);
	m_ingredientList.clear();
	m_item_ing_map.clear();
}

void IngredientMatcherDialog::findRecipes( void )
{
	KApplication::setOverrideCursor( Qt::WaitCursor );

	START_TIMER("Ingredient Matcher: loading database data");

	RecipeList rlist;
	database->loadRecipes( &rlist, RecipeDB::Title | RecipeDB::NamesOnly | RecipeDB::Ingredients | RecipeDB::IngredientAmounts );

	END_TIMER();
	START_TIMER("Ingredient Matcher: analyzing data for matching recipes");

	// Clear the list
	recipeListView->listView() ->clear();

	// Now show the recipes with ingredients that are contained in the previous set
	// of ingredients
	RecipeList incompleteRecipes;
	QList <int> missingNumbers;
	Q3ValueList <IngredientList> missingIngredients;

	RecipeList::Iterator it;
	for ( it = rlist.begin();it != rlist.end();++it ) {
		IngredientList il = ( *it ).ingList;
		if ( il.isEmpty() )
			continue;

		IngredientList missing;
		if ( m_ingredientList.containsSubSet( il, missing, true, database ) ) {
			new CustomRecipeListItem( recipeListView->listView(), *it );
		}
		else {
			incompleteRecipes.append( *it );
			missingIngredients.append( missing );
			missingNumbers.append( missing.count() );
		}
	}
	END_TIMER();

	//Check if the user wants to show missing ingredients

	if ( missingNumberSpinBox->value() == 0 ) {
		KApplication::restoreOverrideCursor();
		return ;
	} //"None"

	START_TIMER("Ingredient Matcher: searching for and displaying partial matches");

	IngredientList requiredIngredients;
	for ( Q3ListViewItem *it = ingListView->listView()->firstChild(); it; it = it->nextSibling() ) {
		if ( ((Q3CheckListItem*)it)->isOn() )
			requiredIngredients << *m_item_ing_map[it];
	}

	// Classify recipes with missing ingredients in different lists by amount
	QList<int>::Iterator nit;
	Q3ValueList<IngredientList>::Iterator ilit;
	int missingNoAllowed = missingNumberSpinBox->value();

	if ( missingNoAllowed == -1 )  // "Any"
	{
		for ( nit = missingNumbers.begin();nit != missingNumbers.end();++nit )
			if ( ( *nit ) > missingNoAllowed )
				missingNoAllowed = ( *nit );
	}

	for ( int missingNo = 1; missingNo <= missingNoAllowed; missingNo++ ) {
		nit = missingNumbers.begin();
		ilit = missingIngredients.begin();

		bool titleShownYet = false;

		for ( it = incompleteRecipes.begin();it != incompleteRecipes.end();++it, ++nit, ++ilit ) {
			if ( !( *it ).ingList.containsAny( m_ingredientList ) )
				continue;

			if ( !( *it ).ingList.containsSubSet( requiredIngredients ) )
				continue;

			if ( ( *nit ) == missingNo ) {
				if ( !titleShownYet ) {
					new SectionItem( recipeListView->listView(), i18ncp( "@label:textbox", "You are missing 1 ingredient for:", "You are missing %1 ingredients for:", missingNo ) );
					titleShownYet = true;
				}
				new CustomRecipeListItem( recipeListView->listView(), *it, *ilit );
			}
		}
	}
	END_TIMER();

	KApplication::restoreOverrideCursor();
}

void IngredientMatcherDialog::reload( ReloadFlags flag )
{
	( ( StdIngredientListView* ) allIngListView->listView() ) ->reload(flag);
}

RecipeActionsHandler* IngredientMatcherDialog::getActionsHandler() const
{
	return actionHandler;
}

void IngredientMatcherDialog::addAction( KAction * action )
{
	actionHandler->addRecipeAction( action );
}

void IngredientMatcherDialog::haveSelectedItems()
{
	QList<int> list = actionHandler->recipeIDs();
	if ( list.isEmpty() )
		emit recipeSelected( false );
	else
		emit recipeSelected( true );
}

void SectionItem::paintCell ( QPainter * p, const QColorGroup & /*cg*/, int column, int width, int /*align*/ )
{
	// Draw the section's deco
	p->setPen( KGlobalSettings::activeTitleColor() );
	p->setBrush( KGlobalSettings::activeTitleColor() );
	p->drawRect( 0, 0, width, height() );

	// Draw the section's text
	if ( column == 0 ) {
		QFont titleFont = KGlobalSettings::windowTitleFont ();
		p->setFont( titleFont );

		p->setPen( KGlobalSettings::activeTextColor() );
		p->drawText( 0, 0, width, height(), Qt::AlignLeft | Qt::AlignVCenter, mText );
	}
}

#include "ingredientmatcherdialog.moc"
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (C) 2003 by                                                 *
 *   Unai Garro (ugarro@users.sourceforge.net)                             *
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#include "ingredientmatcherdialog.h"

#include "datablocks/recipelist.h"
#include "elementlist.h"
#include "DBBackend/recipedb.h"
#include "widgets/krelistview.h"

#include <qhbox.h>
#include <kiconloader.h>
#include <klocale.h>

IngredientMatcherDialog::IngredientMatcherDialog(QWidget *parent,RecipeDB *db):QVBox(parent)
{
// Initialize internal variables
database=db;

//Design the dialog
setSpacing(10);

ingredientListView=new KreListView(this,i18n("Ingredients"));
ingredientListView->listView()->setAllColumnsShowFocus(true);
ingredientListView->listView()->addColumn("*");
ingredientListView->listView()->addColumn(i18n("Ingredient"));

recipeListView=new KreListView(this,i18n("Matching Recipes"));
recipeListView->listView()->setAllColumnsShowFocus(true);
recipeListView->listView()->addColumn(i18n("Title"));
recipeListView->listView()->addColumn(i18n("Missing Ingredients"));

KIconLoader il;
QHBox *buttonBox=new QHBox(this);

okButton=new QPushButton(buttonBox);
okButton->setIconSet(il.loadIconSet("button_ok", KIcon::Small));
okButton->setText(i18n("Find matching recipes"));

clearButton=new QPushButton(buttonBox);
clearButton->setIconSet(il.loadIconSet("editclear", KIcon::Small));
clearButton->setText(i18n("Clear recipe list"));

// Load the data
reloadIngredients();

// Connect signals & slots
connect (okButton,SIGNAL(clicked()), this,SLOT(findRecipes()));
connect (clearButton,SIGNAL(clicked()),recipeListView->listView(),SLOT(clear()));

}

IngredientMatcherDialog::~IngredientMatcherDialog()
{

}

void IngredientMatcherDialog::findRecipes(void)
{
RecipeList rlist;
IngredientList ilist;
database->loadRecipeDetails(&rlist,true,false);

QListViewItem *qlv_it;

// First make a list of the ingredients that we have
for (qlv_it=ingredientListView->listView()->firstChild();qlv_it;qlv_it=qlv_it->nextSibling())
	{
	IngredientListItem *il_it=(IngredientListItem*) qlv_it;
	if (il_it->isOn())
		{
		Ingredient ing; ing.name=il_it->name(); ing.ingredientID=il_it->id();
		ilist.append(ing);
		}
	}

// Now show the recipes with ingredients that are contained in the previous set
// of ingredients
RecipeList::Iterator it;
for (it=rlist.begin();it!=rlist.end();++it)
	{
	IngredientList il=(*it).ingList;

	if (ilist.containsSubSet(il))
		{
		new RecipeListItem(recipeListView->listView(),*it);
		}

	}
}

void IngredientMatcherDialog::reloadIngredients(void)
{

ingredientListView->listView()->clear();
ElementList ingredientList;
database->loadIngredients(&ingredientList);

ElementList::Iterator it;

for (it=ingredientList.begin();it!=ingredientList.end();++it)
	{
	Element ingredient=*it;
	new IngredientListItem(ingredientListView->listView(),ingredient);
	}
}
<commit_msg>Take advantage of the new filter function in KreListView<commit_after>/***************************************************************************
 *   Copyright (C) 2003 by                                                 *
 *   Unai Garro (ugarro@users.sourceforge.net)                             *
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#include "ingredientmatcherdialog.h"

#include "datablocks/recipelist.h"
#include "elementlist.h"
#include "DBBackend/recipedb.h"
#include "widgets/krelistview.h"

#include <qhbox.h>
#include <kiconloader.h>
#include <klocale.h>

IngredientMatcherDialog::IngredientMatcherDialog(QWidget *parent,RecipeDB *db):QVBox(parent)
{
	// Initialize internal variables
	database=db;

	//Design the dialog
	setSpacing(10);

	ingredientListView=new KreListView(this,i18n("Ingredients"),true,1);
	ingredientListView->listView()->setAllColumnsShowFocus(true);
	ingredientListView->listView()->addColumn("*");
	ingredientListView->listView()->addColumn(i18n("Ingredient"));

	recipeListView=new KreListView(this,i18n("Matching Recipes"));
	recipeListView->listView()->setAllColumnsShowFocus(true);
	recipeListView->listView()->addColumn(i18n("Title"));
	recipeListView->listView()->addColumn(i18n("Missing Ingredients"));

	KIconLoader il;
	QHBox *buttonBox=new QHBox(this);

	okButton=new QPushButton(buttonBox);
	okButton->setIconSet(il.loadIconSet("button_ok", KIcon::Small));
	okButton->setText(i18n("Find matching recipes"));

	clearButton=new QPushButton(buttonBox);
	clearButton->setIconSet(il.loadIconSet("editclear", KIcon::Small));
	clearButton->setText(i18n("Clear recipe list"));

	// Load the data
	reloadIngredients();

	// Connect signals & slots
	connect (okButton,SIGNAL(clicked()), this,SLOT(findRecipes()));
	connect (clearButton,SIGNAL(clicked()),recipeListView->listView(),SLOT(clear()));

}

IngredientMatcherDialog::~IngredientMatcherDialog()
{

}

void IngredientMatcherDialog::findRecipes(void)
{
	RecipeList rlist;
	IngredientList ilist;
	database->loadRecipeDetails(&rlist,true,false);

	QListViewItem *qlv_it;

	// First make a list of the ingredients that we have
	for (qlv_it=ingredientListView->listView()->firstChild();qlv_it;qlv_it=qlv_it->nextSibling())
		{
		IngredientListItem *il_it=(IngredientListItem*) qlv_it;
		if (il_it->isOn())
			{
			Ingredient ing; ing.name=il_it->name(); ing.ingredientID=il_it->id();
			ilist.append(ing);
			}
		}

	// Now show the recipes with ingredients that are contained in the previous set
	// of ingredients
	RecipeList::Iterator it;
	for (it=rlist.begin();it!=rlist.end();++it)
		{
		IngredientList il=(*it).ingList;

		if (ilist.containsSubSet(il))
			{
			new RecipeListItem(recipeListView->listView(),*it);
			}

		}
}

void IngredientMatcherDialog::reloadIngredients(void)
{

	ingredientListView->listView()->clear();
	ElementList ingredientList;
	database->loadIngredients(&ingredientList);

	ElementList::Iterator it;

	for (it=ingredientList.begin();it!=ingredientList.end();++it)
		{
		Element ingredient=*it;
		new IngredientListItem(ingredientListView->listView(),ingredient);
		}
}
<|endoftext|>


<commit_before>#include "staticfunctions.h"
#include <QRegExp>

void
StaticFunctions::initQColorDialog()
{
  int i;

  int nNodes = 16;
  int Nodes[] = { 0,   4,   8,  12,  16,  20,  23,  24,  28,  32,  36,  40,  44,  45,  46,  47 };
  int RED[] = { 255, 255,   0,   0,   0, 255, 255, 255, 255, 128, 255, 128, 128,   0, 128, 255 };
  int GREEN[]={   0, 255, 255, 255,   0,   0,   0, 255, 128, 255, 128, 255, 128,   0, 128, 255 };
  int BLUE[] ={   0,   0,   0, 255, 255, 255,  64, 128, 255, 255, 128, 128, 255,   0, 128, 255 };

  int ai;
  for(i = 0; i < 48; i++)
    {
      ai = i;
      if (ai >= 8 && ai < 16) ai = 15 - ai%8;
      else if (ai >= 32 && ai < 40) ai = 39 - ai%32;

      int n, r, g, b;
      for(n=nNodes-1; n>=0; n--)
	if (ai >= Nodes[n])
	  break;

      r = RED[n];
      g = GREEN[n];
      b = BLUE[n];
      if (n < nNodes-1)
	{
	  float frc;
	  frc = Nodes[n+1]-Nodes[n];
	  frc = (ai-Nodes[n])/frc;
	  r = (int)(r + frc*(RED[n+1]-r));
	  g = (int)(g + frc*(GREEN[n+1]-g));
	  b = (int)(b + frc*(BLUE[n+1]-b));
	}

      int x, y;
      y = i%8;
      x = i/8;

      QColorDialog::setStandardColor(y*6+x, qRgb(r,g,b));
    }
}

bool
StaticFunctions::checkExtension(QString flnm, const char *ext)
{
  bool ok = true;
  int extlen = strlen(ext);

  QFileInfo info(flnm);
  if (info.exists() && info.isFile())
    {
      QByteArray exten = flnm.toLatin1().right(extlen);
      if (exten != ext)
	ok = false;
    }
  else
    ok = false;

  return ok;
}

bool
StaticFunctions::checkRegExp(QString flnm, QString regex)
{
  QRegExp rx(regex);
  rx.setPatternSyntax(QRegExp::Wildcard);

  bool ok = true;

  QFileInfo info(flnm);
  if (info.exists() && info.isFile())
    {
      if (! rx.exactMatch(flnm))
	ok = false;
    }
  else
    ok = false;

  return ok;
}

bool
StaticFunctions::checkURLs(QList<QUrl> urls, const char *ext)
{
  bool ok = true;
  int extlen = strlen(ext);

  for(uint i=0; i<urls.count(); i++)
    {
      QUrl url = urls[i];
      QFileInfo info(url.toLocalFile());
      if (info.exists() && info.isFile())
	{
	  QByteArray exten = url.toLocalFile().toLatin1().right(extlen);
	  if (exten != ext)
	    {
	      ok = false;
	      break;
	    }
	}
      else
	{
	  ok = false;
	  break;
	}
    }
  return ok;
}

bool
StaticFunctions::checkURLsRegExp(QList<QUrl> urls, QString regex)
{
  QRegExp rx(regex);
  rx.setPatternSyntax(QRegExp::Wildcard);

  bool ok = true;

  for(uint i=0; i<urls.count(); i++)
    {
      QUrl url = urls[i];
      QFileInfo info(url.toLocalFile());
      if (info.exists() && info.isFile())
	{
	  if (! rx.exactMatch(url.toLocalFile()))
	    {
	      ok = false;
	      break;
	    }
	}
      else
	{
	  ok = false;
	  break;
	}
    }
  return ok;
}

QGradientStops
StaticFunctions::resampleGradientStops(QGradientStops stops)
{
  QColor colorMap[101];

  int startj, endj;
  for(int i=0; i<stops.size(); i++)
    {
      float pos = stops[i].first;
      QColor color = stops[i].second;
      endj = pos*100;
      colorMap[endj] = color;
      if (i > 0)
	{
	  QColor colStart, colEnd;
	  colStart = colorMap[startj];
	  colEnd = colorMap[endj];
	  float rb,gb,bb,ab, re,ge,be,ae;
	  rb = colStart.red();
	  gb = colStart.green();
	  bb = colStart.blue();
	  ab = colStart.alpha();
	  re = colEnd.red();
	  ge = colEnd.green();
	  be = colEnd.blue();
	  ae = colEnd.alpha();
	  for (int j=startj+1; j<endj; j++)
	    {
	      float frc = (float)(j-startj)/(float)(endj-startj);
	      float r,g,b,a;
	      r = rb + frc*(re-rb);
	      g = gb + frc*(ge-gb);
	      b = bb + frc*(be-bb);
	      a = ab + frc*(ae-ab);
	      colorMap[j] = QColor(r, g, b, a);
	    }
	}
      startj = endj;
    }

  QGradientStops newStops;
  for (int i=0; i<100; i++)
    {
      float pos = (float)i/100.0f;
      newStops << QGradientStop(pos, colorMap[i]);
    }

  return newStops;
}

int
StaticFunctions::getScaledown(int scldn, int nX)
{
  int ttct = 2*scldn-1;
  int ct=0;
  int snx = 0;
  for(int i=0; i<nX; i++)
    {
      ct ++;
      if (ct == ttct)
	{
	  snx ++;
	  ct = ttct/2;
	}
    }
  return snx;
}

void
StaticFunctions::swapbytes(uchar *ptr, int nbytes)
{
  for(uint i=0; i<nbytes/2; i++)
    {
      uchar t;
      t = ptr[i];
      ptr[i] = ptr[nbytes-1-i];
      ptr[nbytes-1-i] = t;
    }
}

void
StaticFunctions::swapbytes(uchar *ptr, int bpv, int nbytes)
{
  int nb = nbytes/bpv;
  for(uint j=0; j<nb; j++)
    {
      uchar *p = ptr + bpv*j;
      for(uint i=0; i<bpv/2; i++)
	{
	  uchar t;
	  t = p[i];
	  p[i] = p[bpv-1-i];
	  p[bpv-1-i] = t;
	}
    }

}
<commit_msg>import : fixed checkExtension<commit_after>#include "staticfunctions.h"
#include <QRegExp>

void
StaticFunctions::initQColorDialog()
{
  int i;

  int nNodes = 16;
  int Nodes[] = { 0,   4,   8,  12,  16,  20,  23,  24,  28,  32,  36,  40,  44,  45,  46,  47 };
  int RED[] = { 255, 255,   0,   0,   0, 255, 255, 255, 255, 128, 255, 128, 128,   0, 128, 255 };
  int GREEN[]={   0, 255, 255, 255,   0,   0,   0, 255, 128, 255, 128, 255, 128,   0, 128, 255 };
  int BLUE[] ={   0,   0,   0, 255, 255, 255,  64, 128, 255, 255, 128, 128, 255,   0, 128, 255 };

  int ai;
  for(i = 0; i < 48; i++)
    {
      ai = i;
      if (ai >= 8 && ai < 16) ai = 15 - ai%8;
      else if (ai >= 32 && ai < 40) ai = 39 - ai%32;

      int n, r, g, b;
      for(n=nNodes-1; n>=0; n--)
	if (ai >= Nodes[n])
	  break;

      r = RED[n];
      g = GREEN[n];
      b = BLUE[n];
      if (n < nNodes-1)
	{
	  float frc;
	  frc = Nodes[n+1]-Nodes[n];
	  frc = (ai-Nodes[n])/frc;
	  r = (int)(r + frc*(RED[n+1]-r));
	  g = (int)(g + frc*(GREEN[n+1]-g));
	  b = (int)(b + frc*(BLUE[n+1]-b));
	}

      int x, y;
      y = i%8;
      x = i/8;

      QColorDialog::setStandardColor(y*6+x, qRgb(r,g,b));
    }
}

bool
StaticFunctions::checkExtension(QString flnm, const char *ext)
{
  bool ok = true;
  int extlen = strlen(ext);

  QByteArray exten = flnm.toLatin1().right(extlen);
  if (exten != ext)
    ok = false;

  return ok;
}

bool
StaticFunctions::checkRegExp(QString flnm, QString regex)
{
  QRegExp rx(regex);
  rx.setPatternSyntax(QRegExp::Wildcard);

  bool ok = true;

  QFileInfo info(flnm);
  if (info.exists() && info.isFile())
    {
      if (! rx.exactMatch(flnm))
	ok = false;
    }
  else
    ok = false;

  return ok;
}

bool
StaticFunctions::checkURLs(QList<QUrl> urls, const char *ext)
{
  bool ok = true;
  int extlen = strlen(ext);

  for(uint i=0; i<urls.count(); i++)
    {
      QUrl url = urls[i];
      QFileInfo info(url.toLocalFile());
      if (info.exists() && info.isFile())
	{
	  QByteArray exten = url.toLocalFile().toLatin1().right(extlen);
	  if (exten != ext)
	    {
	      ok = false;
	      break;
	    }
	}
      else
	{
	  ok = false;
	  break;
	}
    }
  return ok;
}

bool
StaticFunctions::checkURLsRegExp(QList<QUrl> urls, QString regex)
{
  QRegExp rx(regex);
  rx.setPatternSyntax(QRegExp::Wildcard);

  bool ok = true;

  for(uint i=0; i<urls.count(); i++)
    {
      QUrl url = urls[i];
      QFileInfo info(url.toLocalFile());
      if (info.exists() && info.isFile())
	{
	  if (! rx.exactMatch(url.toLocalFile()))
	    {
	      ok = false;
	      break;
	    }
	}
      else
	{
	  ok = false;
	  break;
	}
    }
  return ok;
}

QGradientStops
StaticFunctions::resampleGradientStops(QGradientStops stops)
{
  QColor colorMap[101];

  int startj, endj;
  for(int i=0; i<stops.size(); i++)
    {
      float pos = stops[i].first;
      QColor color = stops[i].second;
      endj = pos*100;
      colorMap[endj] = color;
      if (i > 0)
	{
	  QColor colStart, colEnd;
	  colStart = colorMap[startj];
	  colEnd = colorMap[endj];
	  float rb,gb,bb,ab, re,ge,be,ae;
	  rb = colStart.red();
	  gb = colStart.green();
	  bb = colStart.blue();
	  ab = colStart.alpha();
	  re = colEnd.red();
	  ge = colEnd.green();
	  be = colEnd.blue();
	  ae = colEnd.alpha();
	  for (int j=startj+1; j<endj; j++)
	    {
	      float frc = (float)(j-startj)/(float)(endj-startj);
	      float r,g,b,a;
	      r = rb + frc*(re-rb);
	      g = gb + frc*(ge-gb);
	      b = bb + frc*(be-bb);
	      a = ab + frc*(ae-ab);
	      colorMap[j] = QColor(r, g, b, a);
	    }
	}
      startj = endj;
    }

  QGradientStops newStops;
  for (int i=0; i<100; i++)
    {
      float pos = (float)i/100.0f;
      newStops << QGradientStop(pos, colorMap[i]);
    }

  return newStops;
}

int
StaticFunctions::getScaledown(int scldn, int nX)
{
  int ttct = 2*scldn-1;
  int ct=0;
  int snx = 0;
  for(int i=0; i<nX; i++)
    {
      ct ++;
      if (ct == ttct)
	{
	  snx ++;
	  ct = ttct/2;
	}
    }
  return snx;
}

void
StaticFunctions::swapbytes(uchar *ptr, int nbytes)
{
  for(uint i=0; i<nbytes/2; i++)
    {
      uchar t;
      t = ptr[i];
      ptr[i] = ptr[nbytes-1-i];
      ptr[nbytes-1-i] = t;
    }
}

void
StaticFunctions::swapbytes(uchar *ptr, int bpv, int nbytes)
{
  int nb = nbytes/bpv;
  for(uint j=0; j<nb; j++)
    {
      uchar *p = ptr + bpv*j;
      for(uint i=0; i<bpv/2; i++)
	{
	  uchar t;
	  t = p[i];
	  p[i] = p[bpv-1-i];
	  p[bpv-1-i] = t;
	}
    }

}
<|endoftext|>


<commit_before>#ifdef LIBUS_USE_QUIC

/* Do not rely on this API, it will change */
#include "Http3App.h"
#include <iostream>

/* Example of simple Http3 server handling both GET with headers and POST with body.
 * You might be surprised to find out you can replace uWS::H3App with uWS::SSLApp and
 * serve TCP/TLS-based HTTP instead of QUIC-based HTTP, using the same very code ;) */
int main() {
	uWS::SSLApp({
	  .key_file_name = "misc/key.pem",
	  .cert_file_name = "misc/cert.pem",
	  .passphrase = "1234"
	}).get("/*", [](auto *res, auto *req) {

		/* Printing these should obviously be disabled if doing benchmarking */
		std::cout << req->getHeader(":path") << std::endl;
		std::cout << req->getHeader(":method") << std::endl;

	    res->end("Hello H3 from uWS!");
	}).post("/*", [](auto *res, auto */*req*/) {

		/* You also need to set onAborted if receiving data */
		res->onData([res, bodyBuffer = (std::string *)nullptr](std::string_view chunk, bool isLast) mutable {
			if (isLast) {
				if (bodyBuffer) {
					/* Send back the (chunked) body we got, as response */
					bodyBuffer->append(chunk);
					res->end(*bodyBuffer);
					delete bodyBuffer;
				} else {
					/* Send back the body we got, as response (fast path) */
					res->end(chunk);
				}
			} else {
				/* Slow path */
				if (!bodyBuffer) {
					bodyBuffer = new std::string;
				}
				/* If we got the body in a chunk, buffer it up until whole */
				bodyBuffer->append(chunk);
			}

		});

		/* If you have pending, asynch work, you should abort such work in this callback */
		res->onAborted([]() {
			/* Again, just printing is not enough, you need to abort any pending work here
			 * so that nothing will call res->end, since the request was aborted and deleted */
			printf("Stream was aborted!\n");
		});
	}).listen(9004, [](auto *listen_socket) {
	    if (listen_socket) {
			std::cout << "Listening on port " << 9004 << std::endl;
	    }
	}).run();

	std::cout << "Failed to listen on port 3000" << std::endl;
}

#else

#include <stdio.h>

int main() {
    printf("Compile with WITH_QUIC=1 WITH_BORINGSSL=1 make in order to build this example\n");
}

#endif<commit_msg>That should obviously be h3<commit_after>#ifdef LIBUS_USE_QUIC

/* Do not rely on this API, it will change */
#include "Http3App.h"
#include <iostream>

/* Example of simple Http3 server handling both GET with headers and POST with body.
 * You might be surprised to find out you can replace uWS::H3App with uWS::SSLApp and
 * serve TCP/TLS-based HTTP instead of QUIC-based HTTP, using the same very code ;) */
int main() {
	uWS::H3App({
	  .key_file_name = "misc/key.pem",
	  .cert_file_name = "misc/cert.pem",
	  .passphrase = "1234"
	}).get("/*", [](auto *res, auto *req) {

		/* Printing these should obviously be disabled if doing benchmarking */
		std::cout << req->getHeader(":path") << std::endl;
		std::cout << req->getHeader(":method") << std::endl;

	    res->end("Hello H3 from uWS!");
	}).post("/*", [](auto *res, auto */*req*/) {

		/* You also need to set onAborted if receiving data */
		res->onData([res, bodyBuffer = (std::string *)nullptr](std::string_view chunk, bool isLast) mutable {
			if (isLast) {
				if (bodyBuffer) {
					/* Send back the (chunked) body we got, as response */
					bodyBuffer->append(chunk);
					res->end(*bodyBuffer);
					delete bodyBuffer;
				} else {
					/* Send back the body we got, as response (fast path) */
					res->end(chunk);
				}
			} else {
				/* Slow path */
				if (!bodyBuffer) {
					bodyBuffer = new std::string;
				}
				/* If we got the body in a chunk, buffer it up until whole */
				bodyBuffer->append(chunk);
			}

		});

		/* If you have pending, asynch work, you should abort such work in this callback */
		res->onAborted([]() {
			/* Again, just printing is not enough, you need to abort any pending work here
			 * so that nothing will call res->end, since the request was aborted and deleted */
			printf("Stream was aborted!\n");
		});
	}).listen(9004, [](auto *listen_socket) {
	    if (listen_socket) {
			std::cout << "Listening on port " << 9004 << std::endl;
	    }
	}).run();

	std::cout << "Failed to listen on port 3000" << std::endl;
}

#else

#include <stdio.h>

int main() {
    printf("Compile with WITH_QUIC=1 WITH_BORINGSSL=1 make in order to build this example\n");
}

#endif<|endoftext|>


<commit_before><commit_msg>Don't allow duplicate keys on PropertyMap construction<commit_after><|endoftext|>


<commit_before>#include <common/FSUtils.h>

#include <cstring>
#include <future>
#include <sstream>

#include <common/CapricaConfig.h>
#include <common/CapricaReportingContext.h>
#include <common/CaselessStringComparer.h>
#include <common/Concurrency.h>

namespace caprica { namespace FSUtils {

struct FileReadCacheEntry final
{
  FileReadCacheEntry() :
    read(std::make_unique<std::atomic<bool>>(false)),
    dataMutex(std::make_unique<std::mutex>()),
    taskMutex(std::make_unique<std::mutex>())
  {
  }
  FileReadCacheEntry(FileReadCacheEntry&& o) = default;
  FileReadCacheEntry& operator =(FileReadCacheEntry&&) = default;
  FileReadCacheEntry(const FileReadCacheEntry&) = delete;
  FileReadCacheEntry& operator =(const FileReadCacheEntry&) = delete;
  ~FileReadCacheEntry() = default;

  void wantFile(const std::string& filename, size_t filesize) {
    if (!this->filesize && filesize)
      this->filesize = filesize;
    if (conf::Performance::asyncFileRead) {
      if (!read->load() && !readTask.valid()) {
        std::unique_lock<std::mutex> lock{ *taskMutex };
        if (!read->load() && !readTask.valid()) {
          readTask = std::move(std::async(std::launch::async, [this, filename]() {
            this->readFile(filename);
          }));
        }
      }
    }
  }

  void readFile(const std::string& filename) {
    std::ifstream inFile{ filename, std::ifstream::binary };
    inFile.exceptions(std::ifstream::badbit | std::ifstream::failbit);
    std::string str;
    if (filesize != 0) {
      auto size = filesize;
      auto buf = new char[size];
      inFile.read(buf, size);
      str = std::string(buf, size);
      delete buf;
    }
    // Just because the filesize was one thing when
    // we iterated the directory doesn't mean it's
    // not gotten bigger since then.
    inFile.peek();
    if (!inFile.eof()) {
      std::stringstream strStream{ };
      strStream.exceptions(std::ifstream::badbit | std::ifstream::failbit);
      strStream << inFile.rdbuf();
      str += strStream.str();
    }
    {
      std::unique_lock<std::mutex> lock{ *dataMutex };
      readFileData = std::move(str);
      read->store(true);
    }
  }

  boost::string_ref getData(const std::string& filename) {
    if (read->load())
      return readFileData;

    if (readTask.valid()) {
      std::unique_lock<std::mutex> lock{ *taskMutex };
      if (readTask.valid())
        readTask.get();
      assert(read->load());
      return readFileData;
    }

    readFile(filename);
    assert(read->load());
    return readFileData;
  }

  void waitForRead() {
    if (readTask.valid()) {
      std::unique_lock<std::mutex> lock{ *taskMutex };
      if (readTask.valid())
        readTask.get();
      assert(read->load());
    }
  }

private:
  std::unique_ptr<std::atomic<bool>> read;
  std::unique_ptr<std::mutex> dataMutex;
  std::string readFileData{ "" };
  std::unique_ptr<std::mutex> taskMutex;
  std::future<void> readTask{ };
  size_t filesize{ 0 };
};

static caseless_concurrent_unordered_path_map<std::string, FileReadCacheEntry> readFilesMap{ };

void Cache::waitForAll() {
  for (auto& f : readFilesMap) {
    f.second.waitForRead();
  }
}

void Cache::push_need(const std::string& filename, size_t filesize) {
  pushKnownExists(filename);
  readFilesMap[filename].wantFile(filename, filesize);
}

boost::string_ref Cache::cachedReadFull(const std::string& filename) {
  auto abs = canonical(filename);
  push_need(abs);
  return readFilesMap[abs].getData(abs);
}

boost::string_ref basenameAsRef(boost::string_ref file) {
  auto pos = file.find_last_of("\\/");
  if (pos != boost::string_ref::npos)
    file = file.substr(pos + 1);
  auto pos2 = file.rfind('.');
  if (pos2 != boost::string_ref::npos)
    return file.substr(0, pos2);
  return file;
}

boost::string_ref extensionAsRef(boost::string_ref file) {
  auto pos = file.rfind('.');
  if (pos != boost::string_ref::npos)
    return file.substr(pos);
  return "";
}

boost::string_ref filenameAsRef(boost::string_ref file) {
  auto pos = file.find_last_of("\\/");
  if (pos != boost::string_ref::npos)
    return file.substr(pos + 1);
  return file;
}

boost::string_ref parentPathAsRef(boost::string_ref file) {
  auto pos = file.find_last_of("\\/");
  if (pos != boost::string_ref::npos)
    file = file.substr(0, pos);
  return file;
}

static void writeFile(const std::string& filename, std::string&& value) {
  if (!conf::Performance::performanceTestMode) {
    auto containingDir = boost::filesystem::path(filename).parent_path();
    if (!boost::filesystem::exists(containingDir))
      boost::filesystem::create_directories(containingDir);
    std::ofstream destFile{ filename, std::ifstream::binary };
    destFile.exceptions(std::ifstream::badbit | std::ifstream::failbit);
    destFile << value;
  }
}

void async_write(const std::string& filename, std::string&& value) {
  if (!conf::Performance::asyncFileWrite) {
    writeFile(filename, std::move(value));
  } else {
    std::async(std::launch::async, [](const std::string& filename, std::string&& value) {
      writeFile(filename, std::move(value));
    }, filename, std::move(value));
  }
}

static caseless_unordered_path_map<std::string, caseless_unordered_set<std::string>> directoryContentsMap{ };
void pushKnownInDirectory(const std::string& directory, caseless_unordered_set<std::string>&& files) {
  if (directoryContentsMap.count(directory))
    CapricaReportingContext::logicalFatal("Attempted to push the known directory state of '%s' multiple times!", directory.c_str());
  directoryContentsMap.emplace(directory, std::move(files));
}

static caseless_concurrent_unordered_path_map<std::string, uint8_t> fileExistenceMap{ };
void pushKnownExists(const std::string& path) {
  fileExistenceMap.insert({ path, 2 });
}

static bool checkAndSetExists(const std::string& path) {
  bool exists = false;
  auto toFind = boost::filesystem::path(path).parent_path().string();
  auto f = directoryContentsMap.find(toFind);
  if (f != directoryContentsMap.end()) {
    auto fName = filenameAsRef(path).to_string();
    auto e = f->second.count(fName);
    exists = e != 0;
  } else {
    exists = boost::filesystem::exists(path);
  }
  fileExistenceMap.insert({ path, exists ? 2 : 1 });
  return exists;
}

bool exists(const std::string& path) {
  // These concurrent maps are a pain, as it's possible to get a value
  // in the process of being set.
  if (fileExistenceMap.count(path))
    return fileExistenceMap[path] == 2 ? true : checkAndSetExists(path);
  return checkAndSetExists(path);
}

// Borrowed and modified from http://stackoverflow.com/a/1750710/776797
std::string canonical(const std::string& path) {
  if (path.size() > 3 && path[1] == ':') {
    // Shortcircuit for already canon paths.
    if (path.find("/") == std::string::npos &&
        path.find("..") == std::string::npos &&
        path.find("\\.\\") == std::string::npos &&
        path.find("\\\\") == std::string::npos) {
      return path;
    }
  }
  static boost::filesystem::path currentDirectory = boost::filesystem::current_path();
  auto absPath = boost::filesystem::absolute(path, currentDirectory);
  if (conf::Performance::resolveSymlinks) {
    return boost::filesystem::canonical(absPath).make_preferred().string();
  } else {
    boost::filesystem::path result;
    for (auto it = absPath.begin(); it != absPath.end(); ++it) {
      if (!wcscmp(it->c_str(), L"..")) {
        // /a/b/../.. is not /a/b/.. under most circumstances
        // We can end up with ..s in our result because of symbolic links
        if (!wcscmp(result.filename().c_str(), L".."))
          result /= *it;
        // Otherwise it should be safe to resolve the parent
        else
          result = result.parent_path();
      } else if (!wcscmp(it->c_str(), L".")) {
        // Ignore
      } else {
        // Just cat other path entries
        result /= *it;
      }
    }
    return result.make_preferred().string();
  }
}

}}
<commit_msg>Do the file reads more efficiently.<commit_after>#include <common/FSUtils.h>

#include <io.h>
#include <fcntl.h>

#include <cstring>
#include <future>
#include <sstream>

#include <common/CapricaConfig.h>
#include <common/CapricaReportingContext.h>
#include <common/CaselessStringComparer.h>
#include <common/Concurrency.h>

namespace caprica { namespace FSUtils {

struct FileReadCacheEntry final
{
  FileReadCacheEntry() :
    read(std::make_unique<std::atomic<bool>>(false)),
    dataMutex(std::make_unique<std::mutex>()),
    taskMutex(std::make_unique<std::mutex>())
  {
  }
  FileReadCacheEntry(FileReadCacheEntry&& o) = default;
  FileReadCacheEntry& operator =(FileReadCacheEntry&&) = default;
  FileReadCacheEntry(const FileReadCacheEntry&) = delete;
  FileReadCacheEntry& operator =(const FileReadCacheEntry&) = delete;
  ~FileReadCacheEntry() = default;

  void wantFile(const std::string& filename, size_t filesize) {
    if (!this->filesize && filesize)
      this->filesize = filesize;
    if (conf::Performance::asyncFileRead) {
      if (!read->load() && !readTask.valid()) {
        std::unique_lock<std::mutex> lock{ *taskMutex };
        if (!read->load() && !readTask.valid()) {
          readTask = std::move(std::async(std::launch::async, [this, filename]() {
            this->readFile(filename);
          }));
        }
      }
    }
  }

  void readFile(const std::string& filename) {
    std::string str;
    str.resize(filesize);
    auto fd = _open(filename.c_str(), _O_BINARY | _O_RDONLY | _O_SEQUENTIAL);
    if (fd != -1) {
      auto len = _read(fd, (void*)str.data(), filesize);
      if (len != filesize)
        str.resize(len);
      if (_eof(fd) == 1) {
        _close(fd);
        goto DoMove;
      }
    }
    {
      std::ifstream inFile{ filename, std::ifstream::binary };
      inFile.exceptions(std::ifstream::badbit | std::ifstream::failbit);
      if (filesize != 0)
        inFile.read((char*)str.data(), filesize);
      // Just because the filesize was one thing when
      // we iterated the directory doesn't mean it's
      // not gotten bigger since then.
      inFile.peek();
      if (!inFile.eof()) {
        std::stringstream strStream{ };
        strStream.exceptions(std::ifstream::badbit | std::ifstream::failbit);
        strStream << inFile.rdbuf();
        str += strStream.str();
      }
    }
  DoMove:
    {
      std::unique_lock<std::mutex> lock{ *dataMutex };
      readFileData = std::move(str);
      read->store(true);
    }
  }

  boost::string_ref getData(const std::string& filename) {
    if (read->load())
      return readFileData;

    if (readTask.valid()) {
      std::unique_lock<std::mutex> lock{ *taskMutex };
      if (readTask.valid())
        readTask.get();
      assert(read->load());
      return readFileData;
    }

    readFile(filename);
    assert(read->load());
    return readFileData;
  }

  void waitForRead() {
    if (readTask.valid()) {
      std::unique_lock<std::mutex> lock{ *taskMutex };
      if (readTask.valid())
        readTask.get();
      assert(read->load());
    }
  }

private:
  std::unique_ptr<std::atomic<bool>> read;
  std::unique_ptr<std::mutex> dataMutex;
  std::string readFileData{ "" };
  std::unique_ptr<std::mutex> taskMutex;
  std::future<void> readTask{ };
  size_t filesize{ 0 };
};

static caseless_concurrent_unordered_path_map<std::string, FileReadCacheEntry> readFilesMap{ };

void Cache::waitForAll() {
  for (auto& f : readFilesMap) {
    f.second.waitForRead();
  }
}

void Cache::push_need(const std::string& filename, size_t filesize) {
  pushKnownExists(filename);
  readFilesMap[filename].wantFile(filename, filesize);
}

boost::string_ref Cache::cachedReadFull(const std::string& filename) {
  auto abs = canonical(filename);
  push_need(abs);
  return readFilesMap[abs].getData(abs);
}

boost::string_ref basenameAsRef(boost::string_ref file) {
  auto pos = file.find_last_of("\\/");
  if (pos != boost::string_ref::npos)
    file = file.substr(pos + 1);
  auto pos2 = file.rfind('.');
  if (pos2 != boost::string_ref::npos)
    return file.substr(0, pos2);
  return file;
}

boost::string_ref extensionAsRef(boost::string_ref file) {
  auto pos = file.rfind('.');
  if (pos != boost::string_ref::npos)
    return file.substr(pos);
  return "";
}

boost::string_ref filenameAsRef(boost::string_ref file) {
  auto pos = file.find_last_of("\\/");
  if (pos != boost::string_ref::npos)
    return file.substr(pos + 1);
  return file;
}

boost::string_ref parentPathAsRef(boost::string_ref file) {
  auto pos = file.find_last_of("\\/");
  if (pos != boost::string_ref::npos)
    file = file.substr(0, pos);
  return file;
}

static void writeFile(const std::string& filename, std::string&& value) {
  if (!conf::Performance::performanceTestMode) {
    auto containingDir = boost::filesystem::path(filename).parent_path();
    if (!boost::filesystem::exists(containingDir))
      boost::filesystem::create_directories(containingDir);
    std::ofstream destFile{ filename, std::ifstream::binary };
    destFile.exceptions(std::ifstream::badbit | std::ifstream::failbit);
    destFile << value;
  }
}

void async_write(const std::string& filename, std::string&& value) {
  if (!conf::Performance::asyncFileWrite) {
    writeFile(filename, std::move(value));
  } else {
    std::async(std::launch::async, [](const std::string& filename, std::string&& value) {
      writeFile(filename, std::move(value));
    }, filename, std::move(value));
  }
}

static caseless_unordered_path_map<std::string, caseless_unordered_set<std::string>> directoryContentsMap{ };
void pushKnownInDirectory(const std::string& directory, caseless_unordered_set<std::string>&& files) {
  if (directoryContentsMap.count(directory))
    CapricaReportingContext::logicalFatal("Attempted to push the known directory state of '%s' multiple times!", directory.c_str());
  directoryContentsMap.emplace(directory, std::move(files));
}

static caseless_concurrent_unordered_path_map<std::string, uint8_t> fileExistenceMap{ };
void pushKnownExists(const std::string& path) {
  fileExistenceMap.insert({ path, 2 });
}

static bool checkAndSetExists(const std::string& path) {
  bool exists = false;
  auto toFind = boost::filesystem::path(path).parent_path().string();
  auto f = directoryContentsMap.find(toFind);
  if (f != directoryContentsMap.end()) {
    auto fName = filenameAsRef(path).to_string();
    auto e = f->second.count(fName);
    exists = e != 0;
  } else {
    exists = boost::filesystem::exists(path);
  }
  fileExistenceMap.insert({ path, exists ? 2 : 1 });
  return exists;
}

bool exists(const std::string& path) {
  // These concurrent maps are a pain, as it's possible to get a value
  // in the process of being set.
  if (fileExistenceMap.count(path))
    return fileExistenceMap[path] == 2 ? true : checkAndSetExists(path);
  return checkAndSetExists(path);
}

// Borrowed and modified from http://stackoverflow.com/a/1750710/776797
std::string canonical(const std::string& path) {
  if (path.size() > 3 && path[1] == ':') {
    // Shortcircuit for already canon paths.
    if (path.find("/") == std::string::npos &&
        path.find("..") == std::string::npos &&
        path.find("\\.\\") == std::string::npos &&
        path.find("\\\\") == std::string::npos) {
      return path;
    }
  }
  static boost::filesystem::path currentDirectory = boost::filesystem::current_path();
  auto absPath = boost::filesystem::absolute(path, currentDirectory);
  if (conf::Performance::resolveSymlinks) {
    return boost::filesystem::canonical(absPath).make_preferred().string();
  } else {
    boost::filesystem::path result;
    for (auto it = absPath.begin(); it != absPath.end(); ++it) {
      if (!wcscmp(it->c_str(), L"..")) {
        // /a/b/../.. is not /a/b/.. under most circumstances
        // We can end up with ..s in our result because of symbolic links
        if (!wcscmp(result.filename().c_str(), L".."))
          result /= *it;
        // Otherwise it should be safe to resolve the parent
        else
          result = result.parent_path();
      } else if (!wcscmp(it->c_str(), L".")) {
        // Ignore
      } else {
        // Just cat other path entries
        result /= *it;
      }
    }
    return result.make_preferred().string();
  }
}

}}
<|endoftext|>


<commit_before>/*
 * nt loader
 *
 * Copyright 2006-2008 Mike McCormack
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>
#include <stdlib.h>

#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"

#include "debug.h"
#include "object.h"
#include "mem.h"
#include "ntcall.h"
#include "timer.h"

NTSTATUS copy_to_user( void *dest, const void *src, size_t len )
{
	return current->copy_to_user( dest, src, len );
}

NTSTATUS copy_from_user( void *dest, const void *src, size_t len )
{
	return current->copy_from_user( dest, src, len );
}

NTSTATUS verify_for_write( void *dest, size_t len )
{
	return current->verify_for_write( dest, len );
}

NTSTATUS NTAPI NtRaiseHardError(
	NTSTATUS Status,
	ULONG NumberOfArguments,
	ULONG StringArgumentsMask,
	PULONG Arguments,
	HARDERROR_RESPONSE_OPTION ResponseOption,
	PHARDERROR_RESPONSE Response)
{
	NTSTATUS r;
	ULONG i;

	dprintf("%08lx %lu %lu %p %u %p\n", Status, NumberOfArguments,
			StringArgumentsMask, Arguments, ResponseOption, Response);

	if (NumberOfArguments>32)
		return STATUS_INVALID_PARAMETER;

	dprintf("hard error:\n");
	for (i=0; i<NumberOfArguments; i++)
	{
		void *arg;

		r = copy_from_user( &arg, &Arguments[i], sizeof (PUNICODE_STRING) );
		if (r < STATUS_SUCCESS)
			break;

		if (StringArgumentsMask & (1<<i))
		{
			unicode_string_t us;

			r = us.copy_from_user( (UNICODE_STRING*) arg );
			if (r < STATUS_SUCCESS)
				break;

			dprintf("arg[%ld]: %pus\n", i, &us);
		}
		else
			dprintf("arg[%ld]: %08lx\n", i, (ULONG)arg);
	}

	debugger();

	return STATUS_SUCCESS;
}

#define SET_INFO_LENGTH(len, item) \
	do { \
		(len) = sizeof (item); \
		if ((len) < SystemInformationLength) \
			return STATUS_INFO_LENGTH_MISMATCH; \
		(len) = SystemInformationLength; \
	} while (0)

NTSTATUS NTAPI NtQuerySystemInformation(
	SYSTEM_INFORMATION_CLASS SystemInformationClass,
	PVOID SystemInformation,
	ULONG SystemInformationLength,
	PULONG ReturnLength )
{
	NTSTATUS r = STATUS_SUCCESS;
	union {
		SYSTEM_BASIC_INFORMATION basic;
		SYSTEM_CPU_INFORMATION cpu;
		SYSTEM_THREAD_INFORMATION thread;
		SYSTEM_TIME_OF_DAY_INFORMATION time_of_day;
		SYSTEM_RANGE_START_INFORMATION range_start;
		SYSTEM_GLOBAL_FLAG global_flag;
		SYSTEM_KERNEL_DEBUGGER_INFORMATION kernel_debugger_info;
		SYSTEM_PERFORMANCE_INFORMATION performance_info;
		SYSTEM_CRASH_DUMP_STATE_INFORMATION crash_dump_info;
	} info;
	ULONG len = 0;

	dprintf("%d %p %lu %p\n", SystemInformationClass, SystemInformation,
			SystemInformationLength, ReturnLength);

	if (ReturnLength)
	{
		r = copy_to_user( ReturnLength, &len, sizeof len );
		if (r < STATUS_SUCCESS)
			return r;
	}

	memset( &info, 0, sizeof info );

	switch( SystemInformationClass )
	{
	case SystemBasicInformation:
		SET_INFO_LENGTH( len, info.basic );
		info.basic.dwUnknown1 = 0;
		info.basic.uKeMaximumIncrement = 0x18730;
		info.basic.uPageSize = 0x1000;
		info.basic.uMmNumberOfPhysicalPages = 0xbf6c;
		info.basic.uMmLowestPhysicalPage = 1;
		info.basic.uMmHighestPhysicalPage = 0xbfdf;
		info.basic.uAllocationGranularity = 0x1000;
		info.basic.pLowestUserAddress = (void*)0x1000;
		info.basic.pMmHighestUserAddress = (void*)0x7ffeffff;
		info.basic.uKeActiveProcessors = 1;
		info.basic.uKeNumberProcessors = 1;
		break;

	case SystemCpuInformation:
		SET_INFO_LENGTH( len, info.cpu );
		info.cpu.Architecture = 0;
		info.cpu.Level = 6;
		info.cpu.Revision = 0x0801;
		info.cpu.FeatureSet = 0x2fff;
		break;

	case SystemTimeOfDayInformation:
		SET_INFO_LENGTH( len, info.time_of_day );
		get_system_time_of_day( info.time_of_day );
		break;

	case SystemRangeStartInformation:
		SET_INFO_LENGTH( len, info.range_start );
		info.range_start.SystemRangeStart = (PVOID) 0x80000000;
		break;

	case SystemGlobalFlag:
		SET_INFO_LENGTH( len, info.global_flag );
		info.global_flag.GlobalFlag = 0;
		if (option_trace)
			info.global_flag.GlobalFlag |= FLG_SHOW_LDR_SNAPS | FLG_ENABLE_CSRDEBUG;
		break;

	case SystemKernelDebuggerInformation:
		SET_INFO_LENGTH( len, info.kernel_debugger_info );
		info.kernel_debugger_info.DebuggerEnabled = FALSE;
		info.kernel_debugger_info.DebuggerNotPresent = TRUE;
		break;

	case SystemPerformanceInformation:
		SET_INFO_LENGTH( len, info.performance_info );
		info.performance_info.AvailablePages = 0x80000; // 512Mb
		break;

	case SystemCrashDumpStateInformation:
		SET_INFO_LENGTH( len, info.crash_dump_info );
		info.crash_dump_info.CrashDumpSectionExists = 0;
		info.crash_dump_info.Unknown = 0;
		break;

	default:
		dprintf("SystemInformationClass = %d not handled\n", SystemInformationClass);
		r = STATUS_INVALID_INFO_CLASS;
	}

	r = copy_to_user( SystemInformation, &info, len );
	if (ReturnLength)
		r = copy_to_user( ReturnLength, &len, sizeof len );

	return r;
}

NTSTATUS NTAPI NtSetSystemInformation(
	SYSTEM_INFORMATION_CLASS SystemInformationClass,
	PVOID SystemInformation,
	ULONG SystemInformationLength )
{
	dprintf("%d %p %lu\n", SystemInformationClass, SystemInformation, SystemInformationLength );
	return STATUS_SUCCESS;
}

NTSTATUS NTAPI NtFlushInstructionCache(
	HANDLE Process,
	PVOID BaseAddress,
	SIZE_T NumberOfBytesToFlush )
{
	dprintf("%p %p %08lx\n", Process, BaseAddress, NumberOfBytesToFlush);
	return STATUS_SUCCESS;
}

NTSTATUS NTAPI NtDisplayString( PUNICODE_STRING String )
{
	unicode_string_t us;
	NTSTATUS r;

	r = us.copy_from_user( String );
	if (r < STATUS_SUCCESS)
		return r;

	dprintf("%pus\n", &us );

	return STATUS_SUCCESS;
}

NTSTATUS NTAPI NtCreatePagingFile(
	PUNICODE_STRING FileName,
	PULARGE_INTEGER InitialSize,
	PULARGE_INTEGER MaximumSize,
	ULONG Reserved)
{
	unicode_string_t us;
	NTSTATUS r;

	r = us.copy_from_user( FileName );
	if (r < STATUS_SUCCESS)
		return r;

	ULARGE_INTEGER init_sz, max_sz;

	r = copy_from_user( &init_sz, InitialSize, sizeof init_sz );
	if (r < STATUS_SUCCESS)
		return r;

	r = copy_from_user( &max_sz, MaximumSize, sizeof max_sz );
	if (r < STATUS_SUCCESS)
		return r;

	dprintf("unimplemented - %pus %llu %llu %08lx\n",
			 &us, init_sz.QuadPart, max_sz.QuadPart, Reserved);

	return STATUS_SUCCESS;
}

NTSTATUS NTAPI NtShutdownSystem(
	SHUTDOWN_ACTION Action)
{
	const char *action = 0;
	switch (Action)
	{
	case ShutdownNoReboot:
		action = "ShutdownNoReboot";
		break;
	case ShutdownReboot:
		action = "ShutdownReboot";
		break;
	case ShutdownPowerOff:
		action = "ShutdownPowerOff";
		break;
	default:
		return STATUS_INVALID_PARAMETER;
	}
	dprintf("%s\n", action);
	exit(1);
}

NTSTATUS NTAPI NtQueryPerformanceCounter(
	PLARGE_INTEGER PerformanceCount,
	PLARGE_INTEGER PerformanceFrequency)
{
	LARGE_INTEGER now = timeout_t::current_time();
	LARGE_INTEGER freq;
	NTSTATUS r;
	freq.QuadPart = 1000LL;
	r = copy_to_user( PerformanceCount, &now, sizeof now );
	if (r < STATUS_SUCCESS)
		return r;
	r = copy_to_user( PerformanceFrequency, &freq, sizeof freq );
	return r;
}

NTSTATUS NTAPI NtAllocateLocallyUniqueId(
	PLUID Luid)
{
	static LARGE_INTEGER id;
	LUID luid;
	luid.HighPart = id.QuadPart >> 32;
	luid.LowPart = id.QuadPart & 0xffffffffLL;
	NTSTATUS r = copy_to_user( Luid, &luid, sizeof luid );
	if (r == STATUS_SUCCESS)
		id.QuadPart++;
	return r;
}

NTSTATUS NTAPI NtQueryDebugFilterState(
	ULONG Component,
	ULONG Level)
{
	dprintf("%08lx %08lx\n", Component, Level);
	return STATUS_SUCCESS;
}
<commit_msg>Improve upon starting the debugger when minitris quits...<commit_after>/*
 * nt loader
 *
 * Copyright 2006-2008 Mike McCormack
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>

#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"

#include "debug.h"
#include "object.h"
#include "mem.h"
#include "ntcall.h"
#include "timer.h"

NTSTATUS copy_to_user( void *dest, const void *src, size_t len )
{
	return current->copy_to_user( dest, src, len );
}

NTSTATUS copy_from_user( void *dest, const void *src, size_t len )
{
	return current->copy_from_user( dest, src, len );
}

NTSTATUS verify_for_write( void *dest, size_t len )
{
	return current->verify_for_write( dest, len );
}

NTSTATUS NTAPI NtRaiseHardError(
	NTSTATUS Status,
	ULONG NumberOfArguments,
	ULONG StringArgumentsMask,
	PULONG Arguments,
	HARDERROR_RESPONSE_OPTION ResponseOption,
	PHARDERROR_RESPONSE Response)
{
	NTSTATUS r;
	ULONG i;

	dprintf("%08lx %lu %lu %p %u %p\n", Status, NumberOfArguments,
			StringArgumentsMask, Arguments, ResponseOption, Response);

	if (NumberOfArguments>32)
		return STATUS_INVALID_PARAMETER;

	// set color to white on blue - appologies for the lame hack ;)
	fprintf(stderr, "\x1b[37;44m");
	fprintf(stderr, "Hard error (Blue screen of death!):\n");
	for (i=0; i<NumberOfArguments; i++)
	{
		void *arg;

		r = copy_from_user( &arg, &Arguments[i], sizeof (PUNICODE_STRING) );
		if (r < STATUS_SUCCESS)
			break;

		if (StringArgumentsMask & (1<<i))
		{
			char buffer[0x100];
			unicode_string_t us;

			r = us.copy_from_user( (UNICODE_STRING*) arg );
			if (r < STATUS_SUCCESS)
				break;

			us.wchar_to_utf8( buffer, sizeof buffer );
			fprintf(stderr, "arg[%ld]: %s\n", i, buffer);
		}
		else
			fprintf(stderr, "arg[%ld]: %08lx\n", i, (ULONG)arg);
	}
	// restore the color
	fprintf(stderr, "\x1b[0m");

	exit(1);

	return STATUS_SUCCESS;
}

#define SET_INFO_LENGTH(len, item) \
	do { \
		(len) = sizeof (item); \
		if ((len) < SystemInformationLength) \
			return STATUS_INFO_LENGTH_MISMATCH; \
		(len) = SystemInformationLength; \
	} while (0)

NTSTATUS NTAPI NtQuerySystemInformation(
	SYSTEM_INFORMATION_CLASS SystemInformationClass,
	PVOID SystemInformation,
	ULONG SystemInformationLength,
	PULONG ReturnLength )
{
	NTSTATUS r = STATUS_SUCCESS;
	union {
		SYSTEM_BASIC_INFORMATION basic;
		SYSTEM_CPU_INFORMATION cpu;
		SYSTEM_THREAD_INFORMATION thread;
		SYSTEM_TIME_OF_DAY_INFORMATION time_of_day;
		SYSTEM_RANGE_START_INFORMATION range_start;
		SYSTEM_GLOBAL_FLAG global_flag;
		SYSTEM_KERNEL_DEBUGGER_INFORMATION kernel_debugger_info;
		SYSTEM_PERFORMANCE_INFORMATION performance_info;
		SYSTEM_CRASH_DUMP_STATE_INFORMATION crash_dump_info;
	} info;
	ULONG len = 0;

	dprintf("%d %p %lu %p\n", SystemInformationClass, SystemInformation,
			SystemInformationLength, ReturnLength);

	if (ReturnLength)
	{
		r = copy_to_user( ReturnLength, &len, sizeof len );
		if (r < STATUS_SUCCESS)
			return r;
	}

	memset( &info, 0, sizeof info );

	switch( SystemInformationClass )
	{
	case SystemBasicInformation:
		SET_INFO_LENGTH( len, info.basic );
		info.basic.dwUnknown1 = 0;
		info.basic.uKeMaximumIncrement = 0x18730;
		info.basic.uPageSize = 0x1000;
		info.basic.uMmNumberOfPhysicalPages = 0xbf6c;
		info.basic.uMmLowestPhysicalPage = 1;
		info.basic.uMmHighestPhysicalPage = 0xbfdf;
		info.basic.uAllocationGranularity = 0x1000;
		info.basic.pLowestUserAddress = (void*)0x1000;
		info.basic.pMmHighestUserAddress = (void*)0x7ffeffff;
		info.basic.uKeActiveProcessors = 1;
		info.basic.uKeNumberProcessors = 1;
		break;

	case SystemCpuInformation:
		SET_INFO_LENGTH( len, info.cpu );
		info.cpu.Architecture = 0;
		info.cpu.Level = 6;
		info.cpu.Revision = 0x0801;
		info.cpu.FeatureSet = 0x2fff;
		break;

	case SystemTimeOfDayInformation:
		SET_INFO_LENGTH( len, info.time_of_day );
		get_system_time_of_day( info.time_of_day );
		break;

	case SystemRangeStartInformation:
		SET_INFO_LENGTH( len, info.range_start );
		info.range_start.SystemRangeStart = (PVOID) 0x80000000;
		break;

	case SystemGlobalFlag:
		SET_INFO_LENGTH( len, info.global_flag );
		info.global_flag.GlobalFlag = 0;
		if (option_trace)
			info.global_flag.GlobalFlag |= FLG_SHOW_LDR_SNAPS | FLG_ENABLE_CSRDEBUG;
		break;

	case SystemKernelDebuggerInformation:
		SET_INFO_LENGTH( len, info.kernel_debugger_info );
		info.kernel_debugger_info.DebuggerEnabled = FALSE;
		info.kernel_debugger_info.DebuggerNotPresent = TRUE;
		break;

	case SystemPerformanceInformation:
		SET_INFO_LENGTH( len, info.performance_info );
		info.performance_info.AvailablePages = 0x80000; // 512Mb
		break;

	case SystemCrashDumpStateInformation:
		SET_INFO_LENGTH( len, info.crash_dump_info );
		info.crash_dump_info.CrashDumpSectionExists = 0;
		info.crash_dump_info.Unknown = 0;
		break;

	default:
		dprintf("SystemInformationClass = %d not handled\n", SystemInformationClass);
		r = STATUS_INVALID_INFO_CLASS;
	}

	r = copy_to_user( SystemInformation, &info, len );
	if (ReturnLength)
		r = copy_to_user( ReturnLength, &len, sizeof len );

	return r;
}

NTSTATUS NTAPI NtSetSystemInformation(
	SYSTEM_INFORMATION_CLASS SystemInformationClass,
	PVOID SystemInformation,
	ULONG SystemInformationLength )
{
	dprintf("%d %p %lu\n", SystemInformationClass, SystemInformation, SystemInformationLength );
	return STATUS_SUCCESS;
}

NTSTATUS NTAPI NtFlushInstructionCache(
	HANDLE Process,
	PVOID BaseAddress,
	SIZE_T NumberOfBytesToFlush )
{
	dprintf("%p %p %08lx\n", Process, BaseAddress, NumberOfBytesToFlush);
	return STATUS_SUCCESS;
}

NTSTATUS NTAPI NtDisplayString( PUNICODE_STRING String )
{
	unicode_string_t us;
	NTSTATUS r;

	r = us.copy_from_user( String );
	if (r < STATUS_SUCCESS)
		return r;

	dprintf("%pus\n", &us );

	return STATUS_SUCCESS;
}

NTSTATUS NTAPI NtCreatePagingFile(
	PUNICODE_STRING FileName,
	PULARGE_INTEGER InitialSize,
	PULARGE_INTEGER MaximumSize,
	ULONG Reserved)
{
	unicode_string_t us;
	NTSTATUS r;

	r = us.copy_from_user( FileName );
	if (r < STATUS_SUCCESS)
		return r;

	ULARGE_INTEGER init_sz, max_sz;

	r = copy_from_user( &init_sz, InitialSize, sizeof init_sz );
	if (r < STATUS_SUCCESS)
		return r;

	r = copy_from_user( &max_sz, MaximumSize, sizeof max_sz );
	if (r < STATUS_SUCCESS)
		return r;

	dprintf("unimplemented - %pus %llu %llu %08lx\n",
			 &us, init_sz.QuadPart, max_sz.QuadPart, Reserved);

	return STATUS_SUCCESS;
}

NTSTATUS NTAPI NtShutdownSystem(
	SHUTDOWN_ACTION Action)
{
	const char *action = 0;
	switch (Action)
	{
	case ShutdownNoReboot:
		action = "ShutdownNoReboot";
		break;
	case ShutdownReboot:
		action = "ShutdownReboot";
		break;
	case ShutdownPowerOff:
		action = "ShutdownPowerOff";
		break;
	default:
		return STATUS_INVALID_PARAMETER;
	}
	dprintf("%s\n", action);
	exit(1);
}

NTSTATUS NTAPI NtQueryPerformanceCounter(
	PLARGE_INTEGER PerformanceCount,
	PLARGE_INTEGER PerformanceFrequency)
{
	LARGE_INTEGER now = timeout_t::current_time();
	LARGE_INTEGER freq;
	NTSTATUS r;
	freq.QuadPart = 1000LL;
	r = copy_to_user( PerformanceCount, &now, sizeof now );
	if (r < STATUS_SUCCESS)
		return r;
	r = copy_to_user( PerformanceFrequency, &freq, sizeof freq );
	return r;
}

NTSTATUS NTAPI NtAllocateLocallyUniqueId(
	PLUID Luid)
{
	static LARGE_INTEGER id;
	LUID luid;
	luid.HighPart = id.QuadPart >> 32;
	luid.LowPart = id.QuadPart & 0xffffffffLL;
	NTSTATUS r = copy_to_user( Luid, &luid, sizeof luid );
	if (r == STATUS_SUCCESS)
		id.QuadPart++;
	return r;
}

NTSTATUS NTAPI NtQueryDebugFilterState(
	ULONG Component,
	ULONG Level)
{
	dprintf("%08lx %08lx\n", Component, Level);
	return STATUS_SUCCESS;
}
<|endoftext|>


<commit_before>#include "drake/solvers/MosekSolver.h"

#include <iostream>

#include <Eigen/Core>

#include "gtest/gtest.h"

#include "drake/util/testUtil.h"
#include "drake/solvers/MathematicalProgram.h"
#include "drake/solvers/Optimization.h"
#include "drake/util/eigen_matrix_compare.h"
#include "drake/solvers/Constraint.h"

using drake::util::MatrixCompareType;
namespace drake {
namespace solvers {
namespace {

GTEST_TEST(testMosek, MosekLinearProgram) {
  // Taken from http://docs.mosek.com/7.1/capi/Linear_optimization.html
  OptimizationProblem prog;
  auto x = prog.AddContinuousVariables(4);
  Eigen::Vector4d A;
  A << 3, 1, 5, 1;
  LinearConstraint obj(A, Eigen::Vector4d::Constant(0),
                       Eigen::Vector4d::Constant(0));
  std::shared_ptr<LinearConstraint> ptrtoobj =
      std::make_shared<LinearConstraint>(obj);

  prog.AddCost(ptrtoobj);
  Eigen::MatrixXd constraint1(1, 4);
  Eigen::MatrixXd constraint2(1, 4);
  constraint1 << 2, 1, 3, 1;
  constraint2 << 0, 2, 0, 3;
  Eigen::MatrixXd lineqconstraint(1,4);
  lineqconstraint << 3, 1, 2, 0;
  Eigen::MatrixXd lb1(1, 1), ub1(1, 1);
  Eigen::MatrixXd lb2(1, 1), ub2(1, 1);
  lb1 << 15;
  ub1 << +std::numeric_limits<double>::infinity();
  lb2 << -std::numeric_limits<double>::infinity();
  ub2 << 25;
  Eigen::MatrixXd lineqbounds(1,1);
  lineqbounds << 30;

  prog.AddLinearConstraint(constraint1, lb1, ub1);
  prog.AddLinearConstraint(constraint2, lb2, ub2);
  prog.AddLinearEqualityConstraint(lineqconstraint, lineqbounds);
  Eigen::Vector4d bboxlow, bboxhigh;
  bboxlow << 0, 0, 0, 0;
  bboxhigh << std::numeric_limits<double>::infinity(),
              10,
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  prog.SetSolverOption("Mosek", "maxormin", "max");
  prog.SetSolverOption("Mosek", "problemtype", "linear");
  SolutionResult result = SolutionResult::kUnknownError;
  MosekSolver msk;
  ASSERT_NO_THROW(result = msk.Solve(prog)) << "Using solver: Mosek";
  EXPECT_EQ(result, SolutionResult::kSolutionFound) << "Using solver: Mosek";
  Eigen::Vector4d solutions;
  solutions << 0, 0, 15, 8.33333333333333333333;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-10,
                              MatrixCompareType::absolute));
}

}
}
}
<commit_msg>Added spaces between commas in parenthesis.<commit_after>#include "drake/solvers/MosekSolver.h"

#include <iostream>

#include <Eigen/Core>

#include "gtest/gtest.h"

#include "drake/util/testUtil.h"
#include "drake/solvers/MathematicalProgram.h"
#include "drake/solvers/Optimization.h"
#include "drake/util/eigen_matrix_compare.h"
#include "drake/solvers/Constraint.h"

using drake::util::MatrixCompareType;
namespace drake {
namespace solvers {
namespace {

GTEST_TEST(testMosek, MosekLinearProgram) {
  // Taken from http://docs.mosek.com/7.1/capi/Linear_optimization.html
  OptimizationProblem prog;
  auto x = prog.AddContinuousVariables(4);
  Eigen::Vector4d A;
  A << 3, 1, 5, 1;
  LinearConstraint obj(A, Eigen::Vector4d::Constant(0),
                       Eigen::Vector4d::Constant(0));
  std::shared_ptr<LinearConstraint> ptrtoobj =
      std::make_shared<LinearConstraint>(obj);

  prog.AddCost(ptrtoobj);
  Eigen::MatrixXd constraint1(1, 4);
  Eigen::MatrixXd constraint2(1, 4);
  constraint1 << 2, 1, 3, 1;
  constraint2 << 0, 2, 0, 3;
  Eigen::MatrixXd lineqconstraint(1, 4);
  lineqconstraint << 3, 1, 2, 0;
  Eigen::MatrixXd lb1(1, 1), ub1(1, 1);
  Eigen::MatrixXd lb2(1, 1), ub2(1, 1);
  lb1 << 15;
  ub1 << +std::numeric_limits<double>::infinity();
  lb2 << -std::numeric_limits<double>::infinity();
  ub2 << 25;
  Eigen::MatrixXd lineqbounds(1, 1);
  lineqbounds << 30;

  prog.AddLinearConstraint(constraint1, lb1, ub1);
  prog.AddLinearConstraint(constraint2, lb2, ub2);
  prog.AddLinearEqualityConstraint(lineqconstraint, lineqbounds);
  Eigen::Vector4d bboxlow, bboxhigh;
  bboxlow << 0, 0, 0, 0;
  bboxhigh << std::numeric_limits<double>::infinity(),
              10,
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  prog.SetSolverOption("Mosek", "maxormin", "max");
  prog.SetSolverOption("Mosek", "problemtype", "linear");
  SolutionResult result = SolutionResult::kUnknownError;
  MosekSolver msk;
  ASSERT_NO_THROW(result = msk.Solve(prog)) << "Using solver: Mosek";
  EXPECT_EQ(result, SolutionResult::kSolutionFound) << "Using solver: Mosek";
  Eigen::Vector4d solutions;
  solutions << 0, 0, 15, 8.33333333333333333333;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-10,
                              MatrixCompareType::absolute));
}

}
}
}
<|endoftext|>


<commit_before>#include <sstream>
#include <map>

#include "gtest/gtest.h"

#include "drake/util/Polynomial.h"
#include "drake/util/TrigPoly.h"

using namespace Eigen;
using namespace std;

namespace drake {
namespace util {
namespace {

typedef std::map<TrigPolyd::VarType, double> MapType;
const double kPi = 3.1415926535897;

void test_serialization(TrigPolyd dut, std::string expect) {
  std::stringstream test_stream;
  test_stream << dut;
  EXPECT_EQ(test_stream.str(), expect);
}

TEST(TrigPolyTest, SmokeTest) {
  // Confirm that these conversions compile okay.
  TrigPolyd x(1.0);
  TrigPolyd y = 2.0;
  TrigPolyd z = 3;

  // Test something else.
  Polynomiald q("q");
  Polynomiald s("s");
  Polynomiald c("c");

  TrigPolyd p(q, s, c);

  test_serialization(p, "q1");
  test_serialization(sin(p), "s1");
  test_serialization(cos(p), "c1");
  test_serialization(sin(p) * p * p + cos(p), "s1*q1^2+c1");
  test_serialization(sin(p + p), "s1*c1+c1*s1");
}

TEST(TrigPolyTest, EvaluateMultivariateTest) {
  const TrigPolyd theta(Polynomiald("th"),
                        Polynomiald("sth"), Polynomiald("cth"));
  const TrigPolyd::VarType theta_var =
      theta.getPolynomial().getSimpleVariable();

  // Check some basic evaluations.
  EXPECT_EQ(theta.evaluateMultivariate(MapType {{theta_var, 1}}), 1);
  EXPECT_EQ(sin(theta).evaluateMultivariate(MapType {{theta_var, 0}}), 0);
  EXPECT_EQ(cos(theta).evaluateMultivariate(MapType {{theta_var, 0}}), 1);

  // Test that the pythagorean theorem is true for various angles.
  for (const double angle : {0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4}) {
    EXPECT_NEAR((sin(theta) * sin(theta) + cos(theta) * cos(theta) - 1)
                .evaluateMultivariate(MapType {{theta_var, angle}}),
                0,
                1e-6);
  }

  // Test an arbitrary multivariate polynomial.
  const TrigPolyd phi(Polynomiald("phi"),
                      Polynomiald("sphi"), Polynomiald("cphi"));
  const TrigPolyd::VarType phi_var = phi.getPolynomial().getSimpleVariable();
  const TrigPolyd multivariate = theta + phi * cos(theta) + sin(phi + theta);
  for (const double theta_value : {0., 1., kPi / 4, kPi / 2, -kPi / 4}) {
    for (const double phi_value : {0., 1., kPi / 4, kPi / 2, -kPi / 4}) {
      EXPECT_NEAR(multivariate.evaluateMultivariate(
          MapType {{theta_var, theta_value}, {phi_var, phi_value}}),
                  (theta_value + (phi_value * std::cos(theta_value)) +
                   std::sin(phi_value + theta_value)),
                  1e-6) << "phi: " << phi_value << " theta: " << theta_value;
    }
  }
}

TEST(TrigPolyTest, EvaluatePartialTest) {
  const TrigPolyd theta(Polynomiald("th"),
                        Polynomiald("sth"), Polynomiald("cth"));
  const TrigPolyd::VarType theta_var =
      theta.getPolynomial().getSimpleVariable();
  const TrigPolyd phi(Polynomiald("phi"),
                      Polynomiald("sphi"), Polynomiald("cphi"));
  const TrigPolyd::VarType phi_var = phi.getPolynomial().getSimpleVariable();
  const TrigPolyd multivariate = theta + phi * cos(theta) + sin(phi + theta);

  EXPECT_EQ(multivariate.evaluatePartial(MapType {{theta_var, 0}}),
            phi + sin(phi));
}

}  // anonymous namespace
}  // namespace test
}  // namespace drake
<commit_msg>Add a couple more tests.<commit_after>#include <sstream>
#include <map>

#include "gtest/gtest.h"

#include "drake/util/Polynomial.h"
#include "drake/util/TrigPoly.h"

using namespace Eigen;
using namespace std;

namespace drake {
namespace util {
namespace {

typedef std::map<TrigPolyd::VarType, double> MapType;
const double kPi = 3.1415926535897;

void test_serialization(TrigPolyd dut, std::string expect) {
  std::stringstream test_stream;
  test_stream << dut;
  EXPECT_EQ(test_stream.str(), expect);
}

TEST(TrigPolyTest, SmokeTest) {
  // Confirm that these conversions compile okay.
  TrigPolyd x(1.0);
  TrigPolyd y = 2.0;
  TrigPolyd z = 3;

  // Test something else.
  Polynomiald q("q");
  Polynomiald s("s");
  Polynomiald c("c");

  TrigPolyd p(q, s, c);

  test_serialization(p, "q1");
  test_serialization(sin(p), "s1");
  test_serialization(cos(p), "c1");
  test_serialization(sin(p) * p * p + cos(p), "s1*q1^2+c1");
  test_serialization(sin(p + p), "s1*c1+c1*s1");
}

TEST(TrigPolyTest, EvaluateMultivariateTest) {
  const TrigPolyd theta(Polynomiald("th"),
                        Polynomiald("sth"), Polynomiald("cth"));
  const TrigPolyd::VarType theta_var =
      theta.getPolynomial().getSimpleVariable();

  // Check some basic evaluations.
  EXPECT_EQ(theta.evaluateMultivariate(MapType {{theta_var, 1}}), 1);
  EXPECT_EQ(sin(theta).evaluateMultivariate(MapType {{theta_var, 0}}), 0);
  EXPECT_EQ(cos(theta).evaluateMultivariate(MapType {{theta_var, 0}}), 1);

  // Test that the pythagorean theorem is true for various angles.
  for (const double angle : {0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4}) {
    EXPECT_NEAR((sin(theta) * sin(theta) + cos(theta) * cos(theta) - 1)
                .evaluateMultivariate(MapType {{theta_var, angle}}),
                0,
                1e-6);
  }

  // Test an arbitrary multivariate polynomial.
  const TrigPolyd phi(Polynomiald("phi"),
                      Polynomiald("sphi"), Polynomiald("cphi"));
  const TrigPolyd::VarType phi_var = phi.getPolynomial().getSimpleVariable();
  const TrigPolyd multivariate = theta + phi * cos(theta) + sin(phi + theta);
  for (const double theta_value : {0., 1., kPi / 4, kPi / 2, -kPi / 4}) {
    for (const double phi_value : {0., 1., kPi / 4, kPi / 2, -kPi / 4}) {
      EXPECT_NEAR(multivariate.evaluateMultivariate(
          MapType {{theta_var, theta_value}, {phi_var, phi_value}}),
                  (theta_value + (phi_value * std::cos(theta_value)) +
                   std::sin(phi_value + theta_value)),
                  1e-6) << "phi: " << phi_value << " theta: " << theta_value;
    }
  }
}

TEST(TrigPolyTest, EvaluatePartialTest) {
  const TrigPolyd theta(Polynomiald("th"),
                        Polynomiald("sth"), Polynomiald("cth"));
  const TrigPolyd::VarType theta_var =
      theta.getPolynomial().getSimpleVariable();
  const TrigPolyd phi(Polynomiald("phi"),
                      Polynomiald("sphi"), Polynomiald("cphi"));
  const TrigPolyd::VarType phi_var = phi.getPolynomial().getSimpleVariable();
  const TrigPolyd multivariate = theta + phi * cos(theta) + sin(phi + theta);

  EXPECT_EQ(multivariate.evaluatePartial(MapType {{theta_var, 0}}),
            phi + sin(phi));
  EXPECT_EQ(multivariate.evaluatePartial(MapType {{phi_var, 0}}),
            theta + sin(theta));
  // TODO(#2216) This fails due to a known drake bug:
#if 0
  EXPECT_EQ(multivariate.evaluatePartial(MapType {{phi_var, 1}}),
            theta + cos(theta) + sin(theta + 1));
#endif
}

}  // anonymous namespace
}  // namespace test
}  // namespace drake
<|endoftext|>