<commit_before>/***************************************************************************
                          constraintmaxdaysbetweenactivitiesform.cpp  -  description
                             -------------------
    begin                : 2009
    copyright            : (C) 2009 by Lalescu Liviu
    email                : Please see https://lalescu.ro/liviu/ for details about contacting Liviu Lalescu (in particular, you can find here the e-mail address)
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software: you can redistribute it and/or modify  *
 *   it under the terms of the GNU Affero General Public License as        *
 *   published by the Free Software Foundation, either version 3 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

#include <QMessageBox>

#include "longtextmessagebox.h"

#include "constraintmaxdaysbetweenactivitiesform.h"
#include "addconstraintmaxdaysbetweenactivitiesform.h"
#include "modifyconstraintmaxdaysbetweenactivitiesform.h"

#include <QListWidget>
#include <QScrollBar>
#include <QAbstractItemView>

ConstraintMaxDaysBetweenActivitiesForm::ConstraintMaxDaysBetweenActivitiesForm(QWidget* parent): QDialog(parent)
{
	setupUi(this);

	currentConstraintTextEdit->setReadOnly(true);

	modifyConstraintPushButton->setDefault(true);

	constraintsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);

	connect(constraintsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(constraintChanged(int)));
	connect(addConstraintPushButton, SIGNAL(clicked()), this, SLOT(addConstraint()));
	connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
	connect(removeConstraintPushButton, SIGNAL(clicked()), this, SLOT(removeConstraint()));
	connect(modifyConstraintPushButton, SIGNAL(clicked()), this, SLOT(modifyConstraint()));
	connect(constraintsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(modifyConstraint()));
	connect(teachersComboBox, SIGNAL(activated(QString)), this, SLOT(filterChanged()));
	connect(studentsComboBox, SIGNAL(activated(QString)), this, SLOT(filterChanged()));
	connect(subjectsComboBox, SIGNAL(activated(QString)), this, SLOT(filterChanged()));
	connect(activityTagsComboBox, SIGNAL(activated(QString)), this, SLOT(filterChanged()));
	connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));

	centerWidgetOnScreen(this);
	restoreFETDialogGeometry(this);

	QSize tmp1=teachersComboBox->minimumSizeHint();
	Q_UNUSED(tmp1);
	QSize tmp2=studentsComboBox->minimumSizeHint();
	Q_UNUSED(tmp2);
	QSize tmp3=subjectsComboBox->minimumSizeHint();
	Q_UNUSED(tmp3);
	QSize tmp4=activityTagsComboBox->minimumSizeHint();
	Q_UNUSED(tmp4);

/////////////
	teachersComboBox->addItem("");
	for(int i=0; i<gt.rules.teachersList.size(); i++){
		Teacher* tch=gt.rules.teachersList[i];
		teachersComboBox->addItem(tch->name);
	}
	teachersComboBox->setCurrentIndex(0);

	subjectsComboBox->addItem("");
	for(int i=0; i<gt.rules.subjectsList.size(); i++){
		Subject* sb=gt.rules.subjectsList[i];
		subjectsComboBox->addItem(sb->name);
	}
	subjectsComboBox->setCurrentIndex(0);

	activityTagsComboBox->addItem("");
	for(int i=0; i<gt.rules.activityTagsList.size(); i++){
		ActivityTag* st=gt.rules.activityTagsList[i];
		activityTagsComboBox->addItem(st->name);
	}
	activityTagsComboBox->setCurrentIndex(0);

	studentsComboBox->addItem("");
	for(int i=0; i<gt.rules.yearsList.size(); i++){
		StudentsYear* sty=gt.rules.yearsList[i];
		studentsComboBox->addItem(sty->name);
		for(int j=0; j<sty->groupsList.size(); j++){
			StudentsGroup* stg=sty->groupsList[j];
			studentsComboBox->addItem(stg->name);
			if(SHOW_SUBGROUPS_IN_COMBO_BOXES) for(int k=0; k<stg->subgroupsList.size(); k++){
				StudentsSubgroup* sts=stg->subgroupsList[k];
				studentsComboBox->addItem(sts->name);
			}
		}
	}
	studentsComboBox->setCurrentIndex(0);
///////////////

	this->filterChanged();
}

ConstraintMaxDaysBetweenActivitiesForm::~ConstraintMaxDaysBetweenActivitiesForm()
{
	saveFETDialogGeometry(this);
}

bool ConstraintMaxDaysBetweenActivitiesForm::filterOk(TimeConstraint* ctr)
{
	if(ctr->type!=CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES)
		return false;

	ConstraintMaxDaysBetweenActivities* c=(ConstraintMaxDaysBetweenActivities*) ctr;

	QString tn=teachersComboBox->currentText();
	QString sbn=subjectsComboBox->currentText();
	QString sbtn=activityTagsComboBox->currentText();
	QString stn=studentsComboBox->currentText();

	if(tn=="" && sbn=="" && sbtn=="" && stn=="")
		return true;

	bool foundTeacher=false, foundStudents=false, foundSubject=false, foundActivityTag=false;

	for(int i=0; i<c->n_activities; i++){
		int id=c->activitiesId[i];
		Activity* act=NULL;
		foreach(Activity* a, gt.rules.activitiesList)
			if(a->id==id)
				act=a;

		if(act!=NULL){
			//teacher
			if(tn!=""){
				bool ok2=false;
				for(QStringList::Iterator it=act->teachersNames.begin(); it!=act->teachersNames.end(); it++)
					if(*it == tn){
						ok2=true;
						break;
					}
				if(ok2)
					foundTeacher=true;
			}
			else
				foundTeacher=true;

			//subject
			if(sbn!="" && sbn!=act->subjectName)
				;
			else
				foundSubject=true;

			//activity tag
			if(sbtn!="" && !act->activityTagsNames.contains(sbtn))
				;
			else
				foundActivityTag=true;

			//students
			if(stn!=""){
				bool ok2=false;
				for(QStringList::Iterator it=act->studentsNames.begin(); it!=act->studentsNames.end(); it++)
					if(*it == stn){
						ok2=true;
						break;
				}
				if(ok2)
					foundStudents=true;
			}
			else
				foundStudents=true;
		}
	}

	if(foundTeacher && foundStudents && foundSubject && foundActivityTag)
		return true;
	else
		return false;
}

void ConstraintMaxDaysBetweenActivitiesForm::filterChanged()
{
	this->visibleConstraintsList.clear();
	constraintsListWidget->clear();
	for(int i=0; i<gt.rules.timeConstraintsList.size(); i++){
		TimeConstraint* ctr=gt.rules.timeConstraintsList[i];
		if(filterOk(ctr)){
			visibleConstraintsList.append(ctr);
			constraintsListWidget->addItem(ctr->getDescription(gt.rules));
		}
	}

	if(constraintsListWidget->count()>0)
		constraintsListWidget->setCurrentRow(0);
	else
		currentConstraintTextEdit->setPlainText(QString(""));
}

void ConstraintMaxDaysBetweenActivitiesForm::constraintChanged(int index)
{
	if(index<0){
		currentConstraintTextEdit->setPlainText("");

		return;
	}
	assert(index<this->visibleConstraintsList.size());
	TimeConstraint* ctr=this->visibleConstraintsList.at(index);
	assert(ctr!=NULL);
	currentConstraintTextEdit->setPlainText(ctr->getDetailedDescription(gt.rules));
}

void ConstraintMaxDaysBetweenActivitiesForm::addConstraint()
{
	AddConstraintMaxDaysBetweenActivitiesForm form(this);
	setParentAndOtherThings(&form, this);
	form.exec();

	filterChanged();

	constraintsListWidget->setCurrentRow(constraintsListWidget->count()-1);
}

void ConstraintMaxDaysBetweenActivitiesForm::modifyConstraint()
{
	int valv=constraintsListWidget->verticalScrollBar()->value();
	int valh=constraintsListWidget->horizontalScrollBar()->value();

	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}
	TimeConstraint* ctr=this->visibleConstraintsList.at(i);

	ModifyConstraintMaxDaysBetweenActivitiesForm form(this, (ConstraintMaxDaysBetweenActivities*)ctr);
	setParentAndOtherThings(&form, this);
	form.exec();

	filterChanged();

	constraintsListWidget->verticalScrollBar()->setValue(valv);
	constraintsListWidget->horizontalScrollBar()->setValue(valh);

	if(i>=constraintsListWidget->count())
		i=constraintsListWidget->count()-1;

	if(i>=0)
		constraintsListWidget->setCurrentRow(i);
	else
		this->constraintChanged(-1);
}

void ConstraintMaxDaysBetweenActivitiesForm::removeConstraint()
{
	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}
	TimeConstraint* ctr=this->visibleConstraintsList.at(i);
	QString s;
	s=tr("Remove constraint?");
	s+="\n\n";
	s+=ctr->getDetailedDescription(gt.rules);

	QListWidgetItem* item;

	switch( LongTextMessageBox::confirmation( this, tr("FET confirmation"),
		s, tr("Yes"), tr("No"), 0, 0, 1 ) ){
	case 0: // The user clicked the OK button or pressed Enter
		gt.rules.removeTimeConstraint(ctr);

		visibleConstraintsList.removeAt(i);
		constraintsListWidget->setCurrentRow(-1);
		item=constraintsListWidget->takeItem(i);
		delete item;

		break;
	case 1: // The user clicked the Cancel button or pressed Escape
		break;
	}

	if(i>=constraintsListWidget->count())
		i=constraintsListWidget->count()-1;
	if(i>=0)
		constraintsListWidget->setCurrentRow(i);
	else
		this->constraintChanged(-1);
}

void ConstraintMaxDaysBetweenActivitiesForm::help()
{
	QString s;

	s+=tr("Help about the constraint max days between activities:");
	s+="\n\n";
	s+=tr("This constraint was suggested for the following situation: a user needed that activities A1, A2 and A3 to be in follower days"
	 " (like: A1 on Tuesday, A2 on Wednesday and A3 on Thursday. So, they must be in 3 consecutive days). This is simple: add a constraint"
	 " max days between activities for A1, A2 and A3, with max 2 days between them. It is supposed that these activities are constrained"
	 " not to be in the same day by a constraint min days between activities.");
	s+="\n\n";
	s+=tr("So, the general situation: this constraint ensures that between each pair from the selected activities, the distance in days is at most the selected value."
	" Distance = 1 day between a pair A1 and A2 means that A1 and A2 are in consecutive days (like Thursday and Friday)."
	" Distance = 3 days means that A1 and A2 are 3 days apart, for instance Monday and Thursday.");
	s+="\n\n";
	s+=tr("Another example: teacher T wants to ensure that his activities take place in at most 4 consecutive days (so, from Monday to Thursday or from"
	" Tuesday to Friday). Then, add all his activities and max days between them = 3.");

	LongTextMessageBox::largeInformation(this, tr("FET help"), s);
}
<commit_msg>Minor string bug fix<commit_after>/***************************************************************************
                          constraintmaxdaysbetweenactivitiesform.cpp  -  description
                             -------------------
    begin                : 2009
    copyright            : (C) 2009 by Lalescu Liviu
    email                : Please see https://lalescu.ro/liviu/ for details about contacting Liviu Lalescu (in particular, you can find here the e-mail address)
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software: you can redistribute it and/or modify  *
 *   it under the terms of the GNU Affero General Public License as        *
 *   published by the Free Software Foundation, either version 3 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

#include <QMessageBox>

#include "longtextmessagebox.h"

#include "constraintmaxdaysbetweenactivitiesform.h"
#include "addconstraintmaxdaysbetweenactivitiesform.h"
#include "modifyconstraintmaxdaysbetweenactivitiesform.h"

#include <QListWidget>
#include <QScrollBar>
#include <QAbstractItemView>

ConstraintMaxDaysBetweenActivitiesForm::ConstraintMaxDaysBetweenActivitiesForm(QWidget* parent): QDialog(parent)
{
	setupUi(this);

	currentConstraintTextEdit->setReadOnly(true);

	modifyConstraintPushButton->setDefault(true);

	constraintsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);

	connect(constraintsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(constraintChanged(int)));
	connect(addConstraintPushButton, SIGNAL(clicked()), this, SLOT(addConstraint()));
	connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
	connect(removeConstraintPushButton, SIGNAL(clicked()), this, SLOT(removeConstraint()));
	connect(modifyConstraintPushButton, SIGNAL(clicked()), this, SLOT(modifyConstraint()));
	connect(constraintsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(modifyConstraint()));
	connect(teachersComboBox, SIGNAL(activated(QString)), this, SLOT(filterChanged()));
	connect(studentsComboBox, SIGNAL(activated(QString)), this, SLOT(filterChanged()));
	connect(subjectsComboBox, SIGNAL(activated(QString)), this, SLOT(filterChanged()));
	connect(activityTagsComboBox, SIGNAL(activated(QString)), this, SLOT(filterChanged()));
	connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));

	centerWidgetOnScreen(this);
	restoreFETDialogGeometry(this);

	QSize tmp1=teachersComboBox->minimumSizeHint();
	Q_UNUSED(tmp1);
	QSize tmp2=studentsComboBox->minimumSizeHint();
	Q_UNUSED(tmp2);
	QSize tmp3=subjectsComboBox->minimumSizeHint();
	Q_UNUSED(tmp3);
	QSize tmp4=activityTagsComboBox->minimumSizeHint();
	Q_UNUSED(tmp4);

/////////////
	teachersComboBox->addItem("");
	for(int i=0; i<gt.rules.teachersList.size(); i++){
		Teacher* tch=gt.rules.teachersList[i];
		teachersComboBox->addItem(tch->name);
	}
	teachersComboBox->setCurrentIndex(0);

	subjectsComboBox->addItem("");
	for(int i=0; i<gt.rules.subjectsList.size(); i++){
		Subject* sb=gt.rules.subjectsList[i];
		subjectsComboBox->addItem(sb->name);
	}
	subjectsComboBox->setCurrentIndex(0);

	activityTagsComboBox->addItem("");
	for(int i=0; i<gt.rules.activityTagsList.size(); i++){
		ActivityTag* st=gt.rules.activityTagsList[i];
		activityTagsComboBox->addItem(st->name);
	}
	activityTagsComboBox->setCurrentIndex(0);

	studentsComboBox->addItem("");
	for(int i=0; i<gt.rules.yearsList.size(); i++){
		StudentsYear* sty=gt.rules.yearsList[i];
		studentsComboBox->addItem(sty->name);
		for(int j=0; j<sty->groupsList.size(); j++){
			StudentsGroup* stg=sty->groupsList[j];
			studentsComboBox->addItem(stg->name);
			if(SHOW_SUBGROUPS_IN_COMBO_BOXES) for(int k=0; k<stg->subgroupsList.size(); k++){
				StudentsSubgroup* sts=stg->subgroupsList[k];
				studentsComboBox->addItem(sts->name);
			}
		}
	}
	studentsComboBox->setCurrentIndex(0);
///////////////

	this->filterChanged();
}

ConstraintMaxDaysBetweenActivitiesForm::~ConstraintMaxDaysBetweenActivitiesForm()
{
	saveFETDialogGeometry(this);
}

bool ConstraintMaxDaysBetweenActivitiesForm::filterOk(TimeConstraint* ctr)
{
	if(ctr->type!=CONSTRAINT_MAX_DAYS_BETWEEN_ACTIVITIES)
		return false;

	ConstraintMaxDaysBetweenActivities* c=(ConstraintMaxDaysBetweenActivities*) ctr;

	QString tn=teachersComboBox->currentText();
	QString sbn=subjectsComboBox->currentText();
	QString sbtn=activityTagsComboBox->currentText();
	QString stn=studentsComboBox->currentText();

	if(tn=="" && sbn=="" && sbtn=="" && stn=="")
		return true;

	bool foundTeacher=false, foundStudents=false, foundSubject=false, foundActivityTag=false;

	for(int i=0; i<c->n_activities; i++){
		int id=c->activitiesId[i];
		Activity* act=NULL;
		foreach(Activity* a, gt.rules.activitiesList)
			if(a->id==id)
				act=a;

		if(act!=NULL){
			//teacher
			if(tn!=""){
				bool ok2=false;
				for(QStringList::Iterator it=act->teachersNames.begin(); it!=act->teachersNames.end(); it++)
					if(*it == tn){
						ok2=true;
						break;
					}
				if(ok2)
					foundTeacher=true;
			}
			else
				foundTeacher=true;

			//subject
			if(sbn!="" && sbn!=act->subjectName)
				;
			else
				foundSubject=true;

			//activity tag
			if(sbtn!="" && !act->activityTagsNames.contains(sbtn))
				;
			else
				foundActivityTag=true;

			//students
			if(stn!=""){
				bool ok2=false;
				for(QStringList::Iterator it=act->studentsNames.begin(); it!=act->studentsNames.end(); it++)
					if(*it == stn){
						ok2=true;
						break;
				}
				if(ok2)
					foundStudents=true;
			}
			else
				foundStudents=true;
		}
	}

	if(foundTeacher && foundStudents && foundSubject && foundActivityTag)
		return true;
	else
		return false;
}

void ConstraintMaxDaysBetweenActivitiesForm::filterChanged()
{
	this->visibleConstraintsList.clear();
	constraintsListWidget->clear();
	for(int i=0; i<gt.rules.timeConstraintsList.size(); i++){
		TimeConstraint* ctr=gt.rules.timeConstraintsList[i];
		if(filterOk(ctr)){
			visibleConstraintsList.append(ctr);
			constraintsListWidget->addItem(ctr->getDescription(gt.rules));
		}
	}

	if(constraintsListWidget->count()>0)
		constraintsListWidget->setCurrentRow(0);
	else
		currentConstraintTextEdit->setPlainText(QString(""));
}

void ConstraintMaxDaysBetweenActivitiesForm::constraintChanged(int index)
{
	if(index<0){
		currentConstraintTextEdit->setPlainText("");

		return;
	}
	assert(index<this->visibleConstraintsList.size());
	TimeConstraint* ctr=this->visibleConstraintsList.at(index);
	assert(ctr!=NULL);
	currentConstraintTextEdit->setPlainText(ctr->getDetailedDescription(gt.rules));
}

void ConstraintMaxDaysBetweenActivitiesForm::addConstraint()
{
	AddConstraintMaxDaysBetweenActivitiesForm form(this);
	setParentAndOtherThings(&form, this);
	form.exec();

	filterChanged();

	constraintsListWidget->setCurrentRow(constraintsListWidget->count()-1);
}

void ConstraintMaxDaysBetweenActivitiesForm::modifyConstraint()
{
	int valv=constraintsListWidget->verticalScrollBar()->value();
	int valh=constraintsListWidget->horizontalScrollBar()->value();

	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}
	TimeConstraint* ctr=this->visibleConstraintsList.at(i);

	ModifyConstraintMaxDaysBetweenActivitiesForm form(this, (ConstraintMaxDaysBetweenActivities*)ctr);
	setParentAndOtherThings(&form, this);
	form.exec();

	filterChanged();

	constraintsListWidget->verticalScrollBar()->setValue(valv);
	constraintsListWidget->horizontalScrollBar()->setValue(valh);

	if(i>=constraintsListWidget->count())
		i=constraintsListWidget->count()-1;

	if(i>=0)
		constraintsListWidget->setCurrentRow(i);
	else
		this->constraintChanged(-1);
}

void ConstraintMaxDaysBetweenActivitiesForm::removeConstraint()
{
	int i=constraintsListWidget->currentRow();
	if(i<0){
		QMessageBox::information(this, tr("FET information"), tr("Invalid selected constraint"));
		return;
	}
	TimeConstraint* ctr=this->visibleConstraintsList.at(i);
	QString s;
	s=tr("Remove constraint?");
	s+="\n\n";
	s+=ctr->getDetailedDescription(gt.rules);

	QListWidgetItem* item;

	switch( LongTextMessageBox::confirmation( this, tr("FET confirmation"),
		s, tr("Yes"), tr("No"), 0, 0, 1 ) ){
	case 0: // The user clicked the OK button or pressed Enter
		gt.rules.removeTimeConstraint(ctr);

		visibleConstraintsList.removeAt(i);
		constraintsListWidget->setCurrentRow(-1);
		item=constraintsListWidget->takeItem(i);
		delete item;

		break;
	case 1: // The user clicked the Cancel button or pressed Escape
		break;
	}

	if(i>=constraintsListWidget->count())
		i=constraintsListWidget->count()-1;
	if(i>=0)
		constraintsListWidget->setCurrentRow(i);
	else
		this->constraintChanged(-1);
}

void ConstraintMaxDaysBetweenActivitiesForm::help()
{
	QString s;

	s+=tr("Help about the constraint max days between activities:");
	s+="\n\n";
	s+=tr("This constraint was suggested for the following situation: a user needed that activities A1, A2 and A3 to be in consecutive days"
	 " (like: A1 on Tuesday, A2 on Wednesday and A3 on Thursday. So, they must be in 3 consecutive days). This is simple: add a constraint"
	 " max days between activities for A1, A2 and A3, with max 2 days between them. It is supposed that these activities are constrained"
	 " not to be in the same day by a constraint min days between activities.");
	s+="\n\n";
	s+=tr("So, the general situation: this constraint ensures that between each pair from the selected activities, the distance in days is at most the selected value."
	" Distance = 1 day between a pair A1 and A2 means that A1 and A2 are in consecutive days (like Thursday and Friday)."
	" Distance = 3 days means that A1 and A2 are 3 days apart, for instance Monday and Thursday.");
	s+="\n\n";
	s+=tr("Another example: teacher T wants to ensure that his activities take place in at most 4 consecutive days (so, from Monday to Thursday or from"
	" Tuesday to Friday). Then, add all his activities and max days between them = 3.");

	LongTextMessageBox::largeInformation(this, tr("FET help"), s);
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
using namespace std;

#define L 6
#define INPUTFILE "mod6-10m.txt"
#define OUTPUTFILE "output.csv"
#define INTSIZE 30

//#define DEBUG

/*
	For n = 5/6, each 10 3-bit values uses one unsigned int as bit representation.
		I.e. For any n = 10k, n -> number, such that
		p(n + offset) = (number >> (3 * offset)) & 0x7.
*/

/*
	Database format (n == 5/6):

	CREATE TABLE  `*************`.`p_n_mod5` (
	`n` INT( 12 ) UNSIGNED NOT NULL COMMENT  'independent variable',
	`parity` INT( 30 ) UNSIGNED NOT NULL COMMENT  'PartitionsP(n) mod 5',
	UNIQUE (
	`n`
	)
	) ENGINE = MYISAM COMMENT =  'Data access: p(10k+offset) = p(10k)''s ''offset * 3 + 2~0'' bit.';
*/

int main()
{
	ifstream input(INPUTFILE);
	if (!input.is_open())
	{
		cerr << "Cannot open input file \"" << INPUTFILE << "\"!" << endl;
		system("pause");
		return -1;
	}

	ofstream output(OUTPUTFILE);
	if (!output.is_open())
	{
		cerr << "Cannot open output file \"" << OUTPUTFILE << "\"!" << endl;
		system("pause");
		return -1;
	}

#ifdef DEBUG
	const long limit = 20;
#endif

	long count = 0;
	unsigned int temp = 0;
	char curr;

	while (true)
	{
		++count;
		input >> curr;
#ifdef DEBUG
		cout << curr << endl;
		if (count > limit)
			break;
#else
		if (input.eof())
			break;

		if (curr < '0' || curr > '0' + L)
		{
			--count;	// Discard current character
			cerr << "Illegal character detected!" << endl;
			continue;
		}

		if ((3 * count) % INTSIZE == 0)
		{
			output << "'" << (count - INTSIZE / 3) << "','" << temp << "'" << endl;
			temp = 0;
		}

		switch (curr)
		{
			case '1':
				temp |= (1 << (3 * (count % INTSIZE)));
				break;
			case '2':
				temp |= (2 << (3 * (count % INTSIZE)));
				break;
			case '3':
				temp |= (3 << (3 * (count % INTSIZE)));
				break;
			case '4':
				temp |= (4 << (3 * (count % INTSIZE)));
				break;
#if (L == 6)
			case '5':
				temp |= (5 << (3 * (count % INTSIZE)));
				break;
#endif
		}
#endif
	}

#ifndef DEBUG
	output << "'" << (count - count % (INTSIZE / 3)) << "','" << temp << "'" << endl;
#endif

	system("pause");
	return 0;
}<commit_msg>Fixed shift bug.<commit_after>#include <iostream>
#include <fstream>
#include <cassert>
using namespace std;

#define L 6
#define INPUTFILE "mod6-10m.txt"
#define OUTPUTFILE "output.csv"
#define INTSIZE 30

//#define DEBUG

/*
	For n = 5/6, each 10 3-bit values uses one unsigned int as bit representation.
		I.e. For any n = 10k, n -> number, such that
		p(n + offset) = (number >> (3 * offset)) & 0x7.
*/

/*
	Database format (n == 5/6):

	CREATE TABLE  `*************`.`p_n_mod5` (
	`n` INT( 12 ) UNSIGNED NOT NULL COMMENT  'independent variable',
	`parity` INT( 30 ) UNSIGNED NOT NULL COMMENT  'PartitionsP(n) mod 5',
	UNIQUE (
	`n`
	)
	) ENGINE = MYISAM COMMENT =  'Data access: p(10k+offset) = p(10k)''s ''offset * 3 + 2~0'' bit.';
*/

int main()
{
	ifstream input(INPUTFILE);
	if (!input.is_open())
	{
		cerr << "Cannot open input file \"" << INPUTFILE << "\"!" << endl;
		system("pause");
		return -1;
	}

	ofstream output(OUTPUTFILE);
	if (!output.is_open())
	{
		cerr << "Cannot open output file \"" << OUTPUTFILE << "\"!" << endl;
		system("pause");
		return -1;
	}

#ifdef DEBUG
	const long limit = 20;
#endif

	long count = 0;
	unsigned int temp = 0;
	char curr;

	while (true)
	{
		++count;
		input >> curr;
#ifdef DEBUG
		cout << curr << endl;
		if (count > limit)
			break;
#else
		if (input.eof())
			break;

		if (curr < '0' || curr > '0' + L)
		{
			--count;	// Discard current character
			cerr << "Illegal character detected!" << endl;
			continue;
		}

		if ((3 * count) % INTSIZE == 0)
		{
			output << "'" << (count - INTSIZE / 3) << "','" << temp << "'" << endl;
			temp = 0;
		}

		switch (curr)
		{
			case '1':
				temp |= (1 << ((3 * count) % INTSIZE));
				break;
			case '2':
				temp |= (2 << ((3 * count) % INTSIZE));
				break;
			case '3':
				temp |= (3 << ((3 * count) % INTSIZE));
				break;
			case '4':
				temp |= (4 << ((3 * count) % INTSIZE));
				break;
#if (L == 6)
			case '5':
				temp |= (5 << ((3 * count) % INTSIZE));
				break;
#endif
		}
#endif
	}

#ifndef DEBUG
	output << "'" << (count - count % (INTSIZE / 3)) << "','" << temp << "'" << endl;
#endif

	system("pause");
	return 0;
}<|endoftext|>


<commit_before>/** \file    add_subsystem_tags.cc
 *  \brief   Add additional tags for interfaces to identitify subset views of
             IxTheo like RelBib and Bibstudies
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2018, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <fstream>
#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "File.h"
#include "FileUtil.h"
#include "MARC.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

namespace {

const std::string RELBIB_TAG("REL");
const std::string BIBSTUDIES_TAG("BIB");

enum SubSystem { RELBIB, BIBSTUDIES };
const unsigned NUM_OF_SUBSYSTEMS(2);

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

bool HasRelBibSSGN(const MARC::Record &record) {
    for (const auto& field : record.getTagRange("084")) {
        const MARC::Subfields subfields(field.getSubfields());
        if (subfields.hasSubfieldWithValue('2', "ssgn") and subfields.hasSubfieldWithValue('a', "0"))
            return true;
    }
    return false;
}

bool HasRelBibIxTheoNotation(const MARC::Record &record) {
    // Integrate IxTheo Notations A*.B*,T*,V*,X*,Z*
    static const std::string RELBIB_IXTHEO_NOTATION_PATTERN("^[ABTVXZ][A-Z].*|.*:[ABTVXZ][A-Z].*");
    static RegexMatcher * const relbib_ixtheo_notations_matcher(RegexMatcher::RegexMatcherFactory(RELBIB_IXTHEO_NOTATION_PATTERN));
    for (const auto& field : record.getTagRange("652")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields("a")) {
            if (relbib_ixtheo_notations_matcher->matched(subfield_a))
                return true;
        }
    }
    return false;
}

bool HasPlausibleDDCPrefix(const std::string &ddc_string) {
    // Exclude records that where the entry in the DCC field is not plausible
    static const std::string PLAUSIBLE_DDC_PREFIX_PATTERN("^\\d\\d");
    static RegexMatcher * const plausible_ddc_prefix_matcher(RegexMatcher::RegexMatcherFactoryOrDie(PLAUSIBLE_DDC_PREFIX_PATTERN));
    return plausible_ddc_prefix_matcher->matched(ddc_string);
}

// Additional criteria that prevent the exclusion of a record that has a 220-289 field
bool HasAdditionalRelbibAdmissionDDC(const MARC::Record &record) {
    static const std::string RELBIB_ADMIT_DDC_PATTERN("^([12][01][0-9]|2[9][0-9]|[3-9][0-9][0-9]).*$");
    static RegexMatcher * const relbib_admit_ddc_range_matcher(RegexMatcher::RegexMatcherFactoryOrDie(RELBIB_ADMIT_DDC_PATTERN));
    for (const auto &field : record.getTagRange("082")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields("a")) {
            if (HasPlausibleDDCPrefix(subfield_a) and relbib_admit_ddc_range_matcher->matched(subfield_a))
                return true;
        }
    }
    return false;
}

bool HasRelBibExcludeDDC(const MARC::Record &record) {
    if (not record.hasTag("082"))
        return true;
    // Exclude DDC 220-289
    static const std::string RELBIB_EXCLUDE_DDC_RANGE_PATTERN("^2[2-8][0-9](/|\\.){0,2}[^.]*$");
    static RegexMatcher * const relbib_exclude_ddc_range_matcher(RegexMatcher::RegexMatcherFactoryOrDie(RELBIB_EXCLUDE_DDC_RANGE_PATTERN));

    // Make sure we have 082-fields to examine
    if (not record.hasTag("082"))
        return false;

    // In general we exclude if the exclude range is matched
    // But we include it anyway if we find another reasonable DDC-code
    for (const auto &field : record.getTagRange("082")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields("a")) {
            if (relbib_exclude_ddc_range_matcher->matched(subfield_a)) {
                if (not HasAdditionalRelbibAdmissionDDC(record))
                    return true;
            }
        }
    }

    // Exclude item if it has only a 400 or 800 DDC notation
    static const std::string RELBIB_EXCLUDE_DDC_CATEGORIES_PATTERN("^[48][0-9][0-9]$");
    static RegexMatcher * const relbib_exclude_ddc_categories_matcher(RegexMatcher::RegexMatcherFactoryOrDie(RELBIB_EXCLUDE_DDC_CATEGORIES_PATTERN));
    for (const auto &field : record.getTagRange("082")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields('a')) {
            if (HasPlausibleDDCPrefix(subfield_a) and not relbib_exclude_ddc_categories_matcher->matched(subfield_a))
                return false;
        }
    }
    return true;
}

bool MatchesRelBibDDC(const MARC::Record &record) {
    return not HasRelBibExcludeDDC(record);
}

bool IsDefinitelyRelBib(const MARC::Record &record) {
   return HasRelBibSSGN(record) or HasRelBibIxTheoNotation(record) or MatchesRelBibDDC(record);
}

bool IsProbablyRelBib(const MARC::Record &record) {
    for (const auto& field : record.getTagRange("191")) {
        for (const auto& subfield : field.getSubfields().extractSubfields("a")) {
            if (subfield == "1")
                return true;
        }
    }
    return false;
}

std::set<std::string> GetTemporarySuperiorRelBibList() {
    const std::string relbib_superior_temporary_file("/usr/local/ub_tools/cpp/data/relbib_superior_temporary.txt");
    std::set<std::string> superior_temporary_list;
    File superior_temporary(relbib_superior_temporary_file, "r");
    std::string line;
    while (superior_temporary.getline(&line) and not superior_temporary.eof())
        superior_temporary_list.emplace(line);
    return superior_temporary_list;
}

bool IsTemporaryRelBibSuperior(const MARC::Record &record) {
    static std::set<std::string> superior_temporary_list(GetTemporarySuperiorRelBibList());
    if (superior_temporary_list.find(record.getControlNumber()) != superior_temporary_list.end())
        return true;
    return false;
}

bool ExcludeBecauseOfRWEX(const MARC::Record &record) {
    for (const auto &field : record.getTagRange("LOK")) {
        const auto &subfields(field.getSubfields());
        for (const auto &subfield0: subfields.extractSubfields('0')) {
            if (not StringUtil::StartsWith(subfield0, "935"))
                continue;
            for (const auto &subfield_a : subfields.extractSubfields('a')) {
                if (subfield_a == "rwex")
                    return true;
            }
        }
    }
    return false;
}

bool IsRelBibRecord(const MARC::Record &record) {
    return ((IsDefinitelyRelBib(record) or
             IsProbablyRelBib(record) or
             IsTemporaryRelBibSuperior(record))
             and not ExcludeBecauseOfRWEX(record));
}

bool HasBibStudiesIxTheoNotation(const MARC::Record &record) {
    static const std::string BIBSTUDIES_IXTHEO_PATTERN("^[H][A-Z].*|.*:[H][A-Z].*");
    static RegexMatcher * const relbib_ixtheo_notations_matcher(RegexMatcher::RegexMatcherFactory(BIBSTUDIES_IXTHEO_PATTERN));
    for (const auto &field : record.getTagRange("652")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields("a")) {
            if (relbib_ixtheo_notations_matcher->matched(subfield_a))
                return true;
        }
    }
    return false;
}

bool IsBibStudiesRecord(const MARC::Record &record) {
    return HasBibStudiesIxTheoNotation(record);
}

void AddSubsystemTag(MARC::Record * const record, const std::string &tag) {
    // Don't insert twice
    if (record->getFirstField(tag) != record->end())
        return;
    MARC::Subfields subfields;
    subfields.addSubfield('a', "1");
    record->insertField(tag, subfields);
}

void CollectSuperiorOrParallelWorks(const MARC::Record &record, std::unordered_set<std::string> * const superior_or_parallel_works) {
    const std::set<std::string> parallel(MARC::ExtractCrossReferencePPNs(record));
    superior_or_parallel_works->insert(parallel.begin(), parallel.end());
    superior_or_parallel_works->insert(record.getSuperiorControlNumber());
}

// Get set of immediately belonging or superior or parallel records
void GetSubsystemPPNSet(MARC::Reader * const marc_reader, MARC::Writer * const marc_writer,
                      std::vector<std::unordered_set<std::string>> * const subsystem_sets) {
    while (const MARC::Record record = marc_reader->read()) {
        if (IsRelBibRecord(record)) {
            ((*subsystem_sets)[RELBIB]).emplace(record.getControlNumber());
            CollectSuperiorOrParallelWorks(record, &((*subsystem_sets)[RELBIB]));
        }
        if (IsBibStudiesRecord(record)) {
            ((*subsystem_sets)[BIBSTUDIES]).emplace(record.getControlNumber());
            CollectSuperiorOrParallelWorks(record, &((*subsystem_sets)[BIBSTUDIES]));
        }
        marc_writer->write(record);
    }
}

void AddSubsystemTags(MARC::Reader * const marc_reader, MARC::Writer * const marc_writer,
                      const std::vector<std::unordered_set<std::string>> &subsystem_sets) {
    unsigned record_count(0), modified_count(0);
    while (MARC::Record record = marc_reader->read()) {
        ++record_count;
        bool modified_record(false);
        if ((subsystem_sets[RELBIB]).find(record.getControlNumber()) != subsystem_sets[RELBIB].end()) {
            AddSubsystemTag(&record, RELBIB_TAG);
            modified_record = true;
        }
        if ((subsystem_sets[BIBSTUDIES]).find(record.getControlNumber()) != subsystem_sets[RELBIB].end()) {
            AddSubsystemTag(&record, BIBSTUDIES_TAG);
            modified_record = true;
        }
        if (modified_record)
            ++modified_count;
         marc_writer->write(record);
    }
    LOG_INFO("Modified " + std::to_string(modified_count) + " of " + std::to_string(record_count) + " records.");
}

void InitializeSubsystemPPNSets(std::vector<std::unordered_set<std::string>> * const subsystem_ppn_sets) {
    for (unsigned i(0); i < NUM_OF_SUBSYSTEMS; ++i)
        subsystem_ppn_sets->push_back(std::unordered_set<std::string>());
}

} //unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 3)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string marc_output_filename(argv[2]);
    if (unlikely(marc_input_filename == marc_output_filename))
        LOG_ERROR("Title data input file name equals output file name!");

    std::vector<std::unordered_set<std::string>> subsystem_sets;
    InitializeSubsystemPPNSets(&subsystem_sets);

    std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(marc_input_filename));
    FileUtil::AutoTempFile tmp_marc_file("/dev/shm/", ".mrc");
    std::unique_ptr<MARC::Writer> marc_tmp_writer(MARC::Writer::Factory(tmp_marc_file.getFilePath()));
    GetSubsystemPPNSet(marc_reader.get(), marc_tmp_writer.get(), &subsystem_sets);
    if (not marc_tmp_writer->flush())
        LOG_ERROR("Could not flush to " + tmp_marc_file.getFilePath());
    std::unique_ptr<MARC::Reader> marc_tmp_reader(MARC::Reader::Factory(tmp_marc_file.getFilePath()));
    std::unique_ptr<MARC::Writer> marc_writer(MARC::Writer::Factory(marc_output_filename));
    AddSubsystemTags(marc_tmp_reader.get(), marc_writer.get(), subsystem_sets);

    return EXIT_SUCCESS;
}
<commit_msg>Cosmetic.<commit_after>/** \file    add_subsystem_tags.cc
 *  \brief   Add additional tags for interfaces to identitify subset views of
             IxTheo like RelBib and Bibstudies
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2018,2019 Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <fstream>
#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "File.h"
#include "FileUtil.h"
#include "MARC.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

namespace {

const std::string RELBIB_TAG("REL");
const std::string BIBSTUDIES_TAG("BIB");

enum SubSystem { RELBIB, BIBSTUDIES };
const unsigned NUM_OF_SUBSYSTEMS(2);

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

bool HasRelBibSSGN(const MARC::Record &record) {
    for (const auto& field : record.getTagRange("084")) {
        const MARC::Subfields subfields(field.getSubfields());
        if (subfields.hasSubfieldWithValue('2', "ssgn") and subfields.hasSubfieldWithValue('a', "0"))
            return true;
    }
    return false;
}

bool HasRelBibIxTheoNotation(const MARC::Record &record) {
    // Integrate IxTheo Notations A*.B*,T*,V*,X*,Z*
    static const std::string RELBIB_IXTHEO_NOTATION_PATTERN("^[ABTVXZ][A-Z].*|.*:[ABTVXZ][A-Z].*");
    static RegexMatcher * const relbib_ixtheo_notations_matcher(RegexMatcher::RegexMatcherFactory(RELBIB_IXTHEO_NOTATION_PATTERN));
    for (const auto& field : record.getTagRange("652")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields("a")) {
            if (relbib_ixtheo_notations_matcher->matched(subfield_a))
                return true;
        }
    }
    return false;
}

bool HasPlausibleDDCPrefix(const std::string &ddc_string) {
    // Exclude records that where the entry in the DCC field is not plausible
    static const std::string PLAUSIBLE_DDC_PREFIX_PATTERN("^\\d\\d");
    static RegexMatcher * const plausible_ddc_prefix_matcher(RegexMatcher::RegexMatcherFactoryOrDie(PLAUSIBLE_DDC_PREFIX_PATTERN));
    return plausible_ddc_prefix_matcher->matched(ddc_string);
}

// Additional criteria that prevent the exclusion of a record that has a 220-289 field
bool HasAdditionalRelbibAdmissionDDC(const MARC::Record &record) {
    static const std::string RELBIB_ADMIT_DDC_PATTERN("^([12][01][0-9]|2[9][0-9]|[3-9][0-9][0-9]).*$");
    static RegexMatcher * const relbib_admit_ddc_range_matcher(RegexMatcher::RegexMatcherFactoryOrDie(RELBIB_ADMIT_DDC_PATTERN));
    for (const auto &field : record.getTagRange("082")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields("a")) {
            if (HasPlausibleDDCPrefix(subfield_a) and relbib_admit_ddc_range_matcher->matched(subfield_a))
                return true;
        }
    }
    return false;
}

bool HasRelBibExcludeDDC(const MARC::Record &record) {
    if (not record.hasTag("082"))
        return true;
    // Exclude DDC 220-289
    static const std::string RELBIB_EXCLUDE_DDC_RANGE_PATTERN("^2[2-8][0-9](/|\\.){0,2}[^.]*$");
    static RegexMatcher * const relbib_exclude_ddc_range_matcher(RegexMatcher::RegexMatcherFactoryOrDie(RELBIB_EXCLUDE_DDC_RANGE_PATTERN));

    // Make sure we have 082-fields to examine
    if (not record.hasTag("082"))
        return false;

    // In general we exclude if the exclude range is matched
    // But we include it anyway if we find another reasonable DDC-code
    for (const auto &field : record.getTagRange("082")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields("a")) {
            if (relbib_exclude_ddc_range_matcher->matched(subfield_a)) {
                if (not HasAdditionalRelbibAdmissionDDC(record))
                    return true;
            }
        }
    }

    // Exclude item if it has only a 400 or 800 DDC notation
    static const std::string RELBIB_EXCLUDE_DDC_CATEGORIES_PATTERN("^[48][0-9][0-9]$");
    static RegexMatcher * const relbib_exclude_ddc_categories_matcher(RegexMatcher::RegexMatcherFactoryOrDie(RELBIB_EXCLUDE_DDC_CATEGORIES_PATTERN));
    for (const auto &field : record.getTagRange("082")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields('a')) {
            if (HasPlausibleDDCPrefix(subfield_a) and not relbib_exclude_ddc_categories_matcher->matched(subfield_a))
                return false;
        }
    }
    return true;
}

bool MatchesRelBibDDC(const MARC::Record &record) {
    return not HasRelBibExcludeDDC(record);
}

bool IsDefinitelyRelBib(const MARC::Record &record) {
   return HasRelBibSSGN(record) or HasRelBibIxTheoNotation(record) or MatchesRelBibDDC(record);
}

bool IsProbablyRelBib(const MARC::Record &record) {
    for (const auto& field : record.getTagRange("191")) {
        for (const auto& subfield : field.getSubfields().extractSubfields("a")) {
            if (subfield == "1")
                return true;
        }
    }
    return false;
}

std::set<std::string> GetTemporarySuperiorRelBibList() {
    const std::string relbib_superior_temporary_file("/usr/local/ub_tools/cpp/data/relbib_superior_temporary.txt");
    std::set<std::string> superior_temporary_list;
    File superior_temporary(relbib_superior_temporary_file, "r");
    std::string line;
    while (superior_temporary.getline(&line) and not superior_temporary.eof())
        superior_temporary_list.emplace(line);
    return superior_temporary_list;
}

bool IsTemporaryRelBibSuperior(const MARC::Record &record) {
    static std::set<std::string> superior_temporary_list(GetTemporarySuperiorRelBibList());
    if (superior_temporary_list.find(record.getControlNumber()) != superior_temporary_list.end())
        return true;
    return false;
}

bool ExcludeBecauseOfRWEX(const MARC::Record &record) {
    for (const auto &field : record.getTagRange("LOK")) {
        const auto &subfields(field.getSubfields());
        for (const auto &subfield0: subfields.extractSubfields('0')) {
            if (not StringUtil::StartsWith(subfield0, "935"))
                continue;
            for (const auto &subfield_a : subfields.extractSubfields('a')) {
                if (subfield_a == "rwex")
                    return true;
            }
        }
    }
    return false;
}

bool IsRelBibRecord(const MARC::Record &record) {
    return ((IsDefinitelyRelBib(record) or
             IsProbablyRelBib(record) or
             IsTemporaryRelBibSuperior(record))
             and not ExcludeBecauseOfRWEX(record));
}

bool HasBibStudiesIxTheoNotation(const MARC::Record &record) {
    static const std::string BIBSTUDIES_IXTHEO_PATTERN("^[H][A-Z].*|.*:[H][A-Z].*");
    static RegexMatcher * const relbib_ixtheo_notations_matcher(RegexMatcher::RegexMatcherFactory(BIBSTUDIES_IXTHEO_PATTERN));
    for (const auto &field : record.getTagRange("652")) {
        for (const auto &subfield_a : field.getSubfields().extractSubfields("a")) {
            if (relbib_ixtheo_notations_matcher->matched(subfield_a))
                return true;
        }
    }
    return false;
}

bool IsBibStudiesRecord(const MARC::Record &record) {
    return HasBibStudiesIxTheoNotation(record);
}

void AddSubsystemTag(MARC::Record * const record, const std::string &tag) {
    // Don't insert twice
    if (record->getFirstField(tag) != record->end())
        return;
    MARC::Subfields subfields;
    subfields.addSubfield('a', "1");
    record->insertField(tag, subfields);
}

void CollectSuperiorOrParallelWorks(const MARC::Record &record, std::unordered_set<std::string> * const superior_or_parallel_works) {
    const std::set<std::string> parallel(MARC::ExtractCrossReferencePPNs(record));
    superior_or_parallel_works->insert(parallel.begin(), parallel.end());
    superior_or_parallel_works->insert(record.getSuperiorControlNumber());
}

// Get set of immediately belonging or superior or parallel records
void GetSubsystemPPNSet(MARC::Reader * const marc_reader, MARC::Writer * const marc_writer,
                        std::vector<std::unordered_set<std::string>> * const subsystem_sets)
{
    while (const MARC::Record record = marc_reader->read()) {
        if (IsRelBibRecord(record)) {
            ((*subsystem_sets)[RELBIB]).emplace(record.getControlNumber());
            CollectSuperiorOrParallelWorks(record, &((*subsystem_sets)[RELBIB]));
        }
        if (IsBibStudiesRecord(record)) {
            ((*subsystem_sets)[BIBSTUDIES]).emplace(record.getControlNumber());
            CollectSuperiorOrParallelWorks(record, &((*subsystem_sets)[BIBSTUDIES]));
        }
        marc_writer->write(record);
    }
}

void AddSubsystemTags(MARC::Reader * const marc_reader, MARC::Writer * const marc_writer,
                      const std::vector<std::unordered_set<std::string>> &subsystem_sets) {
    unsigned record_count(0), modified_count(0);
    while (MARC::Record record = marc_reader->read()) {
        ++record_count;
        bool modified_record(false);
        if ((subsystem_sets[RELBIB]).find(record.getControlNumber()) != subsystem_sets[RELBIB].end()) {
            AddSubsystemTag(&record, RELBIB_TAG);
            modified_record = true;
        }
        if ((subsystem_sets[BIBSTUDIES]).find(record.getControlNumber()) != subsystem_sets[RELBIB].end()) {
            AddSubsystemTag(&record, BIBSTUDIES_TAG);
            modified_record = true;
        }
        if (modified_record)
            ++modified_count;
         marc_writer->write(record);
    }
    LOG_INFO("Modified " + std::to_string(modified_count) + " of " + std::to_string(record_count) + " records.");
}

void InitializeSubsystemPPNSets(std::vector<std::unordered_set<std::string>> * const subsystem_ppn_sets) {
    for (unsigned i(0); i < NUM_OF_SUBSYSTEMS; ++i)
        subsystem_ppn_sets->push_back(std::unordered_set<std::string>());
}

} //unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 3)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string marc_output_filename(argv[2]);
    if (unlikely(marc_input_filename == marc_output_filename))
        LOG_ERROR("Title data input file name equals output file name!");

    std::vector<std::unordered_set<std::string>> subsystem_sets;
    InitializeSubsystemPPNSets(&subsystem_sets);

    std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(marc_input_filename));
    FileUtil::AutoTempFile tmp_marc_file("/dev/shm/", ".mrc");
    std::unique_ptr<MARC::Writer> marc_tmp_writer(MARC::Writer::Factory(tmp_marc_file.getFilePath()));
    GetSubsystemPPNSet(marc_reader.get(), marc_tmp_writer.get(), &subsystem_sets);
    if (not marc_tmp_writer->flush())
        LOG_ERROR("Could not flush to " + tmp_marc_file.getFilePath());
    std::unique_ptr<MARC::Reader> marc_tmp_reader(MARC::Reader::Factory(tmp_marc_file.getFilePath()));
    std::unique_ptr<MARC::Writer> marc_writer(MARC::Writer::Factory(marc_output_filename));
    AddSubsystemTags(marc_tmp_reader.get(), marc_writer.get(), subsystem_sets);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright 2015 Cloudius Systems
 *
 * Modified by Cloudius Systems
 */

#include "update_statement.hh"
#include "unimplemented.hh"

#include "cql3/operation_impl.hh"

namespace cql3 {

namespace statements {

void update_statement::add_update_for_key(mutation& m, const exploded_clustering_prefix& prefix, const update_parameters& params) {
    if (s->is_dense()) {
        throw std::runtime_error("Dense tables not supported yet");
#if 0
            if (prefix.isEmpty())
                throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().get(0)));

            // An empty name for the compact value is what we use to recognize the case where there is not column
            // outside the PK, see CreateStatement.
            if (!cfm.compactValueColumn().name.bytes.hasRemaining())
            {
                // There is no column outside the PK. So no operation could have passed through validation
                assert updates.isEmpty();
                new Constants.Setter(cfm.compactValueColumn(), EMPTY).execute(key, cf, prefix, params);
            }
            else
            {
                // dense means we don't have a row marker, so don't accept to set only the PK. See CASSANDRA-5648.
                if (updates.isEmpty())
                    throw new InvalidRequestException(String.format("Column %s is mandatory for this COMPACT STORAGE table", cfm.compactValueColumn().name));

                for (Operation update : updates)
                    update.execute(key, cf, prefix, params);
            }
#endif
    } else {
        if (type == statement_type::INSERT && prefix) {
            auto& row = m.partition().clustered_row(clustering_key::from_clustering_prefix(*s, prefix));
            row.apply(params.timestamp());
        }
    }

    for (auto&& update : _column_operations) {
        update->execute(m, prefix, params);
    }

    warn(unimplemented::cause::INDEXES);
#if 0
        SecondaryIndexManager indexManager = Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfId).indexManager;
        if (indexManager.hasIndexes())
        {
            for (Cell cell : cf)
            {
                // Indexed values must be validated by any applicable index. See CASSANDRA-3057/4240/8081 for more details
                if (!indexManager.validate(cell))
                    throw new InvalidRequestException(String.format("Can't index column value of size %d for index %s on %s.%s",
                                                                    cell.value().remaining(),
                                                                    cfm.getColumnDefinition(cell.name()).getIndexName(),
                                                                    cfm.ksName,
                                                                    cfm.cfName));
            }
        }
    }
#endif
}

::shared_ptr<modification_statement>
update_statement::parsed_insert::prepare_internal(database& db, schema_ptr schema,
    ::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
    auto stmt = ::make_shared<update_statement>(statement_type::INSERT, bound_names->size(), schema, std::move(attrs));

    // Created from an INSERT
    if (stmt->is_counter()) {
        throw exceptions::invalid_request_exception("INSERT statement are not allowed on counter tables, use UPDATE instead");
    }

    if (_column_names.size() != _column_values.size()) {
        throw exceptions::invalid_request_exception("Unmatched column names/values");
    }

    if (_column_names.empty()) {
        throw exceptions::invalid_request_exception("No columns provided to INSERT");
    }

    for (size_t i = 0; i < _column_names.size(); i++) {
        auto id = _column_names[i]->prepare_column_identifier(schema);
        auto def = get_column_definition(schema, *id);
        if (!def) {
            throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *id));
        }

        for (size_t j = 0; j < i; j++) {
            auto other_id = _column_names[j]->prepare_column_identifier(schema);
            if (*id == *other_id) {
                throw exceptions::invalid_request_exception(sprint("Multiple definitions found for column %s", *id));
            }
        }

        auto&& value = _column_values[i];

        if (def->is_primary_key()) {
            auto t = value->prepare(db, keyspace(), def->column_specification);
            t->collect_marker_specification(bound_names);
            stmt->add_key_value(*def, std::move(t));
        } else {
            auto operation = operation::set_value(value).prepare(db, keyspace(), *def);
            operation->collect_marker_specification(bound_names);
            stmt->add_operation(std::move(operation));
        };
    }
    return stmt;
}

::shared_ptr<modification_statement>
update_statement::parsed_update::prepare_internal(database& db, schema_ptr schema,
    ::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
    auto stmt = ::make_shared<update_statement>(statement_type::UPDATE, bound_names->size(), schema, std::move(attrs));

    for (auto&& entry : _updates) {
        auto id = entry.first->prepare_column_identifier(schema);
        auto def = get_column_definition(schema, *id);
        if (!def) {
            throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *entry.first));
        }

        auto operation = entry.second->prepare(db, keyspace(), *def);
        operation->collect_marker_specification(bound_names);

        if (def->is_primary_key()) {
            throw exceptions::invalid_request_exception(sprint("PRIMARY KEY part %s found in SET part", *entry.first));
        }
        stmt->add_operation(std::move(operation));
    }

    stmt->process_where_clause(db, _where_clause, bound_names);
    return stmt;
}

}

}
<commit_msg>cql: Fix 'insert' statement not creating row marker for non-clustered tables<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright 2015 Cloudius Systems
 *
 * Modified by Cloudius Systems
 */

#include "update_statement.hh"
#include "unimplemented.hh"

#include "cql3/operation_impl.hh"

namespace cql3 {

namespace statements {

void update_statement::add_update_for_key(mutation& m, const exploded_clustering_prefix& prefix, const update_parameters& params) {
    if (s->is_dense()) {
        throw std::runtime_error("Dense tables not supported yet");
#if 0
            if (prefix.isEmpty())
                throw new InvalidRequestException(String.format("Missing PRIMARY KEY part %s", cfm.clusteringColumns().get(0)));

            // An empty name for the compact value is what we use to recognize the case where there is not column
            // outside the PK, see CreateStatement.
            if (!cfm.compactValueColumn().name.bytes.hasRemaining())
            {
                // There is no column outside the PK. So no operation could have passed through validation
                assert updates.isEmpty();
                new Constants.Setter(cfm.compactValueColumn(), EMPTY).execute(key, cf, prefix, params);
            }
            else
            {
                // dense means we don't have a row marker, so don't accept to set only the PK. See CASSANDRA-5648.
                if (updates.isEmpty())
                    throw new InvalidRequestException(String.format("Column %s is mandatory for this COMPACT STORAGE table", cfm.compactValueColumn().name));

                for (Operation update : updates)
                    update.execute(key, cf, prefix, params);
            }
#endif
    } else {
        // If there are static columns, there also must be clustering columns, in which
        // case empty prefix can only refer to the static row.
        bool is_static_prefix = s->has_static_columns() && !prefix;
        if (type == statement_type::INSERT && !is_static_prefix) {
            auto& row = m.partition().clustered_row(clustering_key::from_clustering_prefix(*s, prefix));
            row.apply(params.timestamp());
        }
    }

    for (auto&& update : _column_operations) {
        update->execute(m, prefix, params);
    }

    warn(unimplemented::cause::INDEXES);
#if 0
        SecondaryIndexManager indexManager = Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfId).indexManager;
        if (indexManager.hasIndexes())
        {
            for (Cell cell : cf)
            {
                // Indexed values must be validated by any applicable index. See CASSANDRA-3057/4240/8081 for more details
                if (!indexManager.validate(cell))
                    throw new InvalidRequestException(String.format("Can't index column value of size %d for index %s on %s.%s",
                                                                    cell.value().remaining(),
                                                                    cfm.getColumnDefinition(cell.name()).getIndexName(),
                                                                    cfm.ksName,
                                                                    cfm.cfName));
            }
        }
    }
#endif
}

::shared_ptr<modification_statement>
update_statement::parsed_insert::prepare_internal(database& db, schema_ptr schema,
    ::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
    auto stmt = ::make_shared<update_statement>(statement_type::INSERT, bound_names->size(), schema, std::move(attrs));

    // Created from an INSERT
    if (stmt->is_counter()) {
        throw exceptions::invalid_request_exception("INSERT statement are not allowed on counter tables, use UPDATE instead");
    }

    if (_column_names.size() != _column_values.size()) {
        throw exceptions::invalid_request_exception("Unmatched column names/values");
    }

    if (_column_names.empty()) {
        throw exceptions::invalid_request_exception("No columns provided to INSERT");
    }

    for (size_t i = 0; i < _column_names.size(); i++) {
        auto id = _column_names[i]->prepare_column_identifier(schema);
        auto def = get_column_definition(schema, *id);
        if (!def) {
            throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *id));
        }

        for (size_t j = 0; j < i; j++) {
            auto other_id = _column_names[j]->prepare_column_identifier(schema);
            if (*id == *other_id) {
                throw exceptions::invalid_request_exception(sprint("Multiple definitions found for column %s", *id));
            }
        }

        auto&& value = _column_values[i];

        if (def->is_primary_key()) {
            auto t = value->prepare(db, keyspace(), def->column_specification);
            t->collect_marker_specification(bound_names);
            stmt->add_key_value(*def, std::move(t));
        } else {
            auto operation = operation::set_value(value).prepare(db, keyspace(), *def);
            operation->collect_marker_specification(bound_names);
            stmt->add_operation(std::move(operation));
        };
    }
    return stmt;
}

::shared_ptr<modification_statement>
update_statement::parsed_update::prepare_internal(database& db, schema_ptr schema,
    ::shared_ptr<variable_specifications> bound_names, std::unique_ptr<attributes> attrs)
{
    auto stmt = ::make_shared<update_statement>(statement_type::UPDATE, bound_names->size(), schema, std::move(attrs));

    for (auto&& entry : _updates) {
        auto id = entry.first->prepare_column_identifier(schema);
        auto def = get_column_definition(schema, *id);
        if (!def) {
            throw exceptions::invalid_request_exception(sprint("Unknown identifier %s", *entry.first));
        }

        auto operation = entry.second->prepare(db, keyspace(), *def);
        operation->collect_marker_specification(bound_names);

        if (def->is_primary_key()) {
            throw exceptions::invalid_request_exception(sprint("PRIMARY KEY part %s found in SET part", *entry.first));
        }
        stmt->add_operation(std::move(operation));
    }

    stmt->process_where_clause(db, _where_clause, bound_names);
    return stmt;
}

}

}
<|endoftext|>


<commit_before>/** Copyright (C) 2017 European Spallation Source ERIC */

#include <memory>
#include <stdio.h>
#include <unistd.h>
#include <gdgem/clustering/HitSorter.h>
#include <gdgem/clustering/DoroClusterer.h>
#include <gdgem/clustering/ClusterMatcher.h>
#include <test/TestBase.h>
#include <functional>

#include <gdgem/clustering/TestDataShort.h>
#include <gdgem/nmx/ReadoutFile.h>

#define UNUSED __attribute__((unused))

class ClusterMatcherTest : public TestBase {
protected:
  std::vector<Readout> long_data;

  uint16_t pADCThreshold = 0;
  size_t pMinClusterSize = 3;
  // Maximum time difference between hits in time sorted cluster (x or y)
  double pMaxTimeGap = 200;
  // Maximum number of missing strips in strip sorted cluster (x or y)
  uint16_t pMaxStripGap = 2;
  //Maximum cluster time difference between matching clusters in x and y
  //Cluster time is either calculated with center-of-mass or uTPC method
  double pDeltaTimePlanes = 200;
  // Maximum time span for total cluster (x or y)
  // double pDeltaTimeSpan = 500;

  SRSMappings mapping;

  std::shared_ptr<ClusterMatcher> matcher;
  std::shared_ptr<HitSorter> sorter_x;
  std::shared_ptr<HitSorter> sorter_y;

  virtual void SetUp() {
    std::string DataPath = TEST_DATA_PATH;
    ReadoutFile::read(DataPath + "run16long.h5", long_data);

    mapping.set_mapping(1, 0, 0, 0);
    mapping.set_mapping(1, 1, 0, 64);
    mapping.set_mapping(1, 6, 0, 128);
    mapping.set_mapping(1, 7, 0, 192);

    mapping.set_mapping(1, 10, 1, 0);
    mapping.set_mapping(1, 11, 1, 64);
    mapping.set_mapping(1, 14, 1, 128);
    mapping.set_mapping(1, 15, 1, 192);

    SRSTime srstime;
    srstime.set_bc_clock(20);
    srstime.set_tac_slope(60);
    srstime.set_trigger_resolution_ns(3.125);
    srstime.set_acquisition_window(4000);

    matcher = std::make_shared<ClusterMatcher>(pDeltaTimePlanes);
    sorter_x = std::make_shared<HitSorter>(srstime, mapping, pADCThreshold, pMaxTimeGap);
    sorter_y = std::make_shared<HitSorter>(srstime, mapping, pADCThreshold, pMaxTimeGap);
    sorter_x->clusterer = std::make_shared<DoroClusterer>(pMaxTimeGap, pMaxStripGap, pMinClusterSize);
    sorter_y->clusterer = std::make_shared<DoroClusterer>(pMaxTimeGap, pMaxStripGap, pMinClusterSize);
  }

  virtual void TearDown() {
  }

  void store_hit(const Readout& readout)
  {
    uint8_t planeID = mapping.get_plane(readout);
    if (planeID == 1) {
      sorter_y->insert(readout);
    } else {
      sorter_x->insert(readout);
    }
  }

  Cluster mock_cluster(uint8_t plane, uint16_t strip_start, uint16_t strip_end,
                       double time_start, double time_end) {
    Cluster ret;
    Hit e;
    e.plane_id = plane;
    e.adc = 1;
    double time_step = (time_end - time_start) / 10.0;
    for (e.time = time_start; e.time <= time_end; e.time += time_step)
      for (e.strip = strip_start; e.strip <= strip_end; ++e.strip)
        ret.insert_hit(e);
    e.time = time_end;
    ret.insert_hit(e);
    return ret;
  }

};

TEST_F(ClusterMatcherTest, OneX) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0, 10, 0, 200));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 1);
  ASSERT_EQ(matcher->matched_clusters.size(), 1);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 0);
}

TEST_F(ClusterMatcherTest, OneY) {
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0, 10, 0, 200));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 1);
  ASSERT_EQ(matcher->matched_clusters.size(), 1);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, TwoX) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0, 10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0, 10, 500, 700));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 2);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.back().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.back().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.back().y.entries.size(), 0);
}

TEST_F(ClusterMatcherTest, TwoY) {
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 500, 700));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 2);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.back().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.back().x.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.back().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, OneXOneY) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 500, 700));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 2);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.back().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.back().x.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.back().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, OneXY) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 0, 200));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 1);
  ASSERT_EQ(matcher->matched_clusters.size(), 1);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, TwoXY) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 1, 300));
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 600, 800));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 650, 850));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 2);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 300);
  EXPECT_EQ(matcher->matched_clusters.back().time_span(), 250);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.back().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.back().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, JustIntside) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 200, 400));
  matcher->match_end(true);
  ASSERT_EQ(matcher->matched_clusters.size(), 1);
}

TEST_F(ClusterMatcherTest, JustOutside) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 200, 401));
  matcher->match_end(true);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
}

TEST_F(ClusterMatcherTest, DontForce) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 200, 401));
  matcher->match_end(false);
  ASSERT_EQ(matcher->matched_clusters.size(), 0);

  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 800, 1000));
  matcher->match_end(false);
  ASSERT_EQ(matcher->matched_clusters.size(), 0);

  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 900, 1000));
  matcher->match_end(false);
  ASSERT_EQ(matcher->matched_clusters.size(), 0);
}

TEST_F(ClusterMatcherTest, Run16_Short) {
  uint32_t bonus = 0;
  uint32_t old = 0;
  for (auto readout : Run16) {
    if (readout.srs_timestamp < old)
      bonus++;
    old = readout.srs_timestamp;
    readout.bonus_timestamp = bonus;
    store_hit(readout);
  }
  EXPECT_EQ(sorter_x->clusterer->stats_cluster_count, 3);
  EXPECT_EQ(sorter_y->clusterer->stats_cluster_count, 4);
  matcher->merge(0, sorter_x->clusterer->clusters);
  matcher->merge(1, sorter_y->clusterer->clusters);
  matcher->match_end(false);
  EXPECT_EQ(matcher->stats_cluster_count, 1);

  sorter_x->flush();
  sorter_y->flush();
  EXPECT_EQ(sorter_x->clusterer->stats_cluster_count, 7);
  EXPECT_EQ(sorter_y->clusterer->stats_cluster_count, 11);
  matcher->merge(0, sorter_x->clusterer->clusters);
  matcher->merge(1, sorter_y->clusterer->clusters);
  matcher->match_end(true);
  EXPECT_EQ(matcher->stats_cluster_count, 6);
}

TEST_F(ClusterMatcherTest, Run16_Long_identical) {
  uint32_t bonus = 0;
  uint32_t old = 0;
  for (auto readout : long_data) {
    if (readout.srs_timestamp < old)
      bonus++;
    old = readout.srs_timestamp;
    readout.bonus_timestamp = bonus;
    sorter_y->insert(readout);
    sorter_x->insert(readout);
  }

  sorter_x->flush();
  sorter_y->flush();
  EXPECT_EQ(sorter_x->clusterer->clusters.size(), 20282);
  EXPECT_EQ(sorter_y->clusterer->clusters.size(), 20282);

  matcher = std::make_shared<ClusterMatcher>(0);
  matcher->merge(0, sorter_x->clusterer->clusters);
  matcher->merge(1, sorter_y->clusterer->clusters);

  EXPECT_EQ(matcher->unmatched_clusters.size(), 40564);

  matcher->match_end(true);
  EXPECT_EQ(matcher->stats_cluster_count, 16963);
}

TEST_F(ClusterMatcherTest, Run16_Long) {
  uint32_t bonus = 0;
  uint32_t old = 0;
  for (auto readout : long_data) {
    if (readout.srs_timestamp < old)
      bonus++;
    old = readout.srs_timestamp;
    readout.bonus_timestamp = bonus;
    store_hit(readout);
  }
  sorter_x->flush();
  sorter_y->flush();
  EXPECT_EQ(sorter_x->clusterer->stats_cluster_count, 10206);
  EXPECT_EQ(sorter_y->clusterer->stats_cluster_count, 12443);

  matcher = std::make_shared<ClusterMatcher>(10);
  matcher->merge(0, sorter_x->clusterer->clusters);
  matcher->merge(1, sorter_y->clusterer->clusters);
  EXPECT_EQ(matcher->unmatched_clusters.size(), 22649);
  matcher->match_end(true);
//  EXPECT_EQ(matcher->stats_cluster_count, 6250);
  EXPECT_EQ(matcher->stats_cluster_count, 19073);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>retro fitting gtest asserts after changing tdc calculation - this is a hack<commit_after>/** Copyright (C) 2017 European Spallation Source ERIC */

#include <memory>
#include <stdio.h>
#include <unistd.h>
#include <gdgem/clustering/HitSorter.h>
#include <gdgem/clustering/DoroClusterer.h>
#include <gdgem/clustering/ClusterMatcher.h>
#include <test/TestBase.h>
#include <functional>

#include <gdgem/clustering/TestDataShort.h>
#include <gdgem/nmx/ReadoutFile.h>

#define UNUSED __attribute__((unused))

class ClusterMatcherTest : public TestBase {
protected:
  std::vector<Readout> long_data;

  uint16_t pADCThreshold = 0;
  size_t pMinClusterSize = 3;
  // Maximum time difference between hits in time sorted cluster (x or y)
  double pMaxTimeGap = 200;
  // Maximum number of missing strips in strip sorted cluster (x or y)
  uint16_t pMaxStripGap = 2;
  //Maximum cluster time difference between matching clusters in x and y
  //Cluster time is either calculated with center-of-mass or uTPC method
  double pDeltaTimePlanes = 200;
  // Maximum time span for total cluster (x or y)
  // double pDeltaTimeSpan = 500;

  SRSMappings mapping;

  std::shared_ptr<ClusterMatcher> matcher;
  std::shared_ptr<HitSorter> sorter_x;
  std::shared_ptr<HitSorter> sorter_y;

  virtual void SetUp() {
    std::string DataPath = TEST_DATA_PATH;
    ReadoutFile::read(DataPath + "run16long.h5", long_data);

    mapping.set_mapping(1, 0, 0, 0);
    mapping.set_mapping(1, 1, 0, 64);
    mapping.set_mapping(1, 6, 0, 128);
    mapping.set_mapping(1, 7, 0, 192);

    mapping.set_mapping(1, 10, 1, 0);
    mapping.set_mapping(1, 11, 1, 64);
    mapping.set_mapping(1, 14, 1, 128);
    mapping.set_mapping(1, 15, 1, 192);

    SRSTime srstime;
    srstime.set_bc_clock(20);
    srstime.set_tac_slope(60);
    srstime.set_trigger_resolution_ns(3.125);
    srstime.set_acquisition_window(4000);

    matcher = std::make_shared<ClusterMatcher>(pDeltaTimePlanes);
    sorter_x = std::make_shared<HitSorter>(srstime, mapping, pADCThreshold, pMaxTimeGap);
    sorter_y = std::make_shared<HitSorter>(srstime, mapping, pADCThreshold, pMaxTimeGap);
    sorter_x->clusterer = std::make_shared<DoroClusterer>(pMaxTimeGap, pMaxStripGap, pMinClusterSize);
    sorter_y->clusterer = std::make_shared<DoroClusterer>(pMaxTimeGap, pMaxStripGap, pMinClusterSize);
  }

  virtual void TearDown() {
  }

  void store_hit(const Readout& readout)
  {
    uint8_t planeID = mapping.get_plane(readout);
    if (planeID == 1) {
      sorter_y->insert(readout);
    } else {
      sorter_x->insert(readout);
    }
  }

  Cluster mock_cluster(uint8_t plane, uint16_t strip_start, uint16_t strip_end,
                       double time_start, double time_end) {
    Cluster ret;
    Hit e;
    e.plane_id = plane;
    e.adc = 1;
    double time_step = (time_end - time_start) / 10.0;
    for (e.time = time_start; e.time <= time_end; e.time += time_step)
      for (e.strip = strip_start; e.strip <= strip_end; ++e.strip)
        ret.insert_hit(e);
    e.time = time_end;
    ret.insert_hit(e);
    return ret;
  }

};

TEST_F(ClusterMatcherTest, OneX) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0, 10, 0, 200));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 1);
  ASSERT_EQ(matcher->matched_clusters.size(), 1);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 0);
}

TEST_F(ClusterMatcherTest, OneY) {
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0, 10, 0, 200));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 1);
  ASSERT_EQ(matcher->matched_clusters.size(), 1);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, TwoX) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0, 10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0, 10, 500, 700));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 2);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.back().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.back().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.back().y.entries.size(), 0);
}

TEST_F(ClusterMatcherTest, TwoY) {
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 500, 700));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 2);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.back().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.back().x.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.back().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, OneXOneY) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 500, 700));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 2);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.back().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.back().x.entries.size(), 0);
  EXPECT_EQ(matcher->matched_clusters.back().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, OneXY) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 0, 200));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 1);
  ASSERT_EQ(matcher->matched_clusters.size(), 1);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 200);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, TwoXY) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 1, 300));
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 600, 800));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 650, 850));
  matcher->match_end(true);
  ASSERT_EQ(matcher->stats_cluster_count, 2);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
  EXPECT_EQ(matcher->matched_clusters.front().time_span(), 300);
  EXPECT_EQ(matcher->matched_clusters.back().time_span(), 250);
  EXPECT_EQ(matcher->matched_clusters.front().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.front().y.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.back().x.entries.size(), 122);
  EXPECT_EQ(matcher->matched_clusters.back().y.entries.size(), 122);
}

TEST_F(ClusterMatcherTest, JustIntside) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 200, 400));
  matcher->match_end(true);
  ASSERT_EQ(matcher->matched_clusters.size(), 1);
}

TEST_F(ClusterMatcherTest, JustOutside) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 200, 401));
  matcher->match_end(true);
  ASSERT_EQ(matcher->matched_clusters.size(), 2);
}

TEST_F(ClusterMatcherTest, DontForce) {
  matcher->unmatched_clusters.push_back(mock_cluster(0, 0,10, 0, 200));
  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 200, 401));
  matcher->match_end(false);
  ASSERT_EQ(matcher->matched_clusters.size(), 0);

  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 800, 1000));
  matcher->match_end(false);
  ASSERT_EQ(matcher->matched_clusters.size(), 0);

  matcher->unmatched_clusters.push_back(mock_cluster(1, 0,10, 900, 1000));
  matcher->match_end(false);
  ASSERT_EQ(matcher->matched_clusters.size(), 0);
}

TEST_F(ClusterMatcherTest, Run16_Short) {
  uint32_t bonus = 0;
  uint32_t old = 0;
  for (auto readout : Run16) {
    if (readout.srs_timestamp < old)
      bonus++;
    old = readout.srs_timestamp;
    readout.bonus_timestamp = bonus;
    store_hit(readout);
  }
  EXPECT_EQ(sorter_x->clusterer->stats_cluster_count, 3);
  EXPECT_EQ(sorter_y->clusterer->stats_cluster_count, 4);
  matcher->merge(0, sorter_x->clusterer->clusters);
  matcher->merge(1, sorter_y->clusterer->clusters);
  matcher->match_end(false);
  EXPECT_EQ(matcher->stats_cluster_count, 1);

  sorter_x->flush();
  sorter_y->flush();
  EXPECT_EQ(sorter_x->clusterer->stats_cluster_count, 7);
  EXPECT_EQ(sorter_y->clusterer->stats_cluster_count, 11);
  matcher->merge(0, sorter_x->clusterer->clusters);
  matcher->merge(1, sorter_y->clusterer->clusters);
  matcher->match_end(true);
  EXPECT_EQ(matcher->stats_cluster_count, 6);
}

TEST_F(ClusterMatcherTest, Run16_Long_identical) {
  uint32_t bonus = 0;
  uint32_t old = 0;
  for (auto readout : long_data) {
    if (readout.srs_timestamp < old)
      bonus++;
    old = readout.srs_timestamp;
    readout.bonus_timestamp = bonus;
    sorter_y->insert(readout);
    sorter_x->insert(readout);
  }

  sorter_x->flush();
  sorter_y->flush();
  EXPECT_EQ(sorter_x->clusterer->clusters.size(), 20293);
  EXPECT_EQ(sorter_y->clusterer->clusters.size(), 20293);

  matcher = std::make_shared<ClusterMatcher>(0);
  matcher->merge(0, sorter_x->clusterer->clusters);
  matcher->merge(1, sorter_y->clusterer->clusters);

  EXPECT_EQ(matcher->unmatched_clusters.size(), 40586);

  matcher->match_end(true);
  EXPECT_EQ(matcher->stats_cluster_count, 16954);
}

TEST_F(ClusterMatcherTest, Run16_Long) {
  uint32_t bonus = 0;
  uint32_t old = 0;
  for (auto readout : long_data) {
    if (readout.srs_timestamp < old)
      bonus++;
    old = readout.srs_timestamp;
    readout.bonus_timestamp = bonus;
    store_hit(readout);
  }
  sorter_x->flush();
  sorter_y->flush();
  EXPECT_EQ(sorter_x->clusterer->stats_cluster_count, 10226);
  EXPECT_EQ(sorter_y->clusterer->stats_cluster_count, 12467);

  matcher = std::make_shared<ClusterMatcher>(10);
  matcher->merge(0, sorter_x->clusterer->clusters);
  matcher->merge(1, sorter_y->clusterer->clusters);
  EXPECT_EQ(matcher->unmatched_clusters.size(), 22693);
  matcher->match_end(true);
//  EXPECT_EQ(matcher->stats_cluster_count, 6250);
  EXPECT_EQ(matcher->stats_cluster_count, 19080);
}

int main(int argc, char **argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>