<commit_before>/**
  This file contains a sequence of tests to perform for different instances of a templatized fixture.
  It is thus inlined several times in the .cpp test file.
  */

// ==============================
// Set/get value tests
TEST_F(TestMatrix, set_fullMat ) { ASSERT_TRUE( matrixMaxDiff(mat,fullMat) < 100*epsilon() ); }
TEST_F(TestMatrix, set_crs1 ) { ASSERT_TRUE( matrixMaxDiff( fullMat,crs1 ) < 100*epsilon() ); }
TEST_F(TestMatrix, set_crs2 ) { ASSERT_TRUE( matrixMaxDiff(fullMat,crs2) < 100*epsilon() ); }
TEST_F(TestMatrix, set_mapMat ) { ASSERT_TRUE( matrixMaxDiff(fullMat,mapMat) < 100*epsilon() ); }
TEST_F(TestMatrix, set_eiBlock1 ) { ASSERT_TRUE( matrixMaxDiff(fullMat,eiBlock1) < 100*epsilon() ); }
TEST_F(TestMatrix, set_eiBlock2 ) { ASSERT_TRUE( matrixMaxDiff(fullMat,eiBlock2) < 100*epsilon() ); }
TEST_F(TestMatrix, set_eiBase ) { ASSERT_TRUE( matrixMaxDiff(fullMat,eiBase) < 100*epsilon() ); }
TEST_F(TestMatrix, eigenMatrix_update ) { ASSERT_TRUE( checkEigenMatrixUpdate() ); }
TEST_F(TestMatrix, eigenMatrix_block_row_filling ) { ASSERT_TRUE( checkEigenMatrixBlockRowFilling() ); }
TEST_F(TestMatrix, eigenMatrixBlockFromCompressedRowSparseMatrix ) { ASSERT_TRUE( checkEigenMatrixBlockFromCompressedRowSparseMatrix() ); }
TEST_F(TestMatrix, eigenMapToDenseMatrix ) { ASSERT_TRUE( checkEigenDenseMatrix() ); }

// ==============================
// Matrix-Vector product tests
TEST_F(TestMatrix, set_fullVec_nrows_reference )
{
    ASSERT_TRUE(vectorMaxDiff(vecM,fullVec_nrows_reference) < epsilon() );
}
TEST_F(TestMatrix, fullMat_vector_product )
{
//    fullMat.opMulV(&fullVec_nrows_result,&fullVec_ncols);
    fullVec_nrows_result = fullMat * fullVec_ncols;
    ASSERT_TRUE(vectorMaxDiff(fullVec_nrows_reference,fullVec_nrows_result) < epsilon() );
}
TEST_F(TestMatrix, mapMat_vector_product )
{
//    mapMat.opMulV(&fullVec_nrows_result,&fullVec_ncols);
    fullVec_nrows_result = mapMat * fullVec_ncols;
    ASSERT_TRUE(vectorMaxDiff(fullVec_nrows_reference,fullVec_nrows_result) < epsilon() );
}
TEST_F(TestMatrix, eiBlock1_vector_product )
{
//    eiBlock1.opMulV(&fullVec_nrows_result,&fullVec_ncols);
//    eiBlock1.multVector(fullVec_nrows_result,fullVec_ncols);
    fullVec_nrows_result = eiBlock1 * fullVec_ncols;
    ASSERT_TRUE(vectorMaxDiff(fullVec_nrows_reference,fullVec_nrows_result) < epsilon() );

}
TEST_F(TestMatrix, crs1_vector_product )
{
//    EXPECT_TRUE(NROWS%BROWS==0 && NCOLS%BCOLS==0) << "Error: CompressedRowSparseMatrix * Vector crashes when the size of the matrix is not a multiple of the size of the matrix blocks. Aborting this test, and reporting a failure."; // otherwise the product crashes
//    crs1.opMulV(&fullVec_nrows_result,&fullVec_ncols);
    fullVec_nrows_result = crs1 * fullVec_ncols;
    ASSERT_FALSE(vectorMaxDiff(fullVec_nrows_reference,fullVec_nrows_result) < epsilon() ); // create an error to check if I get a message from Jenkins
//    ASSERT_TRUE(vectorMaxDiff(fullVec_nrows_reference,fullVec_nrows_result) < epsilon() );
}

// ==============================
// Matrix product tests
TEST_F(TestMatrix, full_matrix_product ) { ASSERT_TRUE( matrixMaxDiff(matMultiplication,fullMultiplication) < 100*epsilon() );  }
TEST_F(TestMatrix, crs_matrix_product ) { ASSERT_TRUE( matrixMaxDiff(fullMultiplication,crsMultiplication) < 100*epsilon() ); }
TEST_F(TestMatrix, EigenBase_matrix_product ) { ASSERT_TRUE( matrixMaxDiff(fullMultiplication,eiBaseMultiplication) < 100*epsilon() ); }
TEST_F(TestMatrix, EigenSparseDense_matrix_product ) { ASSERT_TRUE( EigenDenseMatrix(eiBaseMultiplication.compressedMatrix) == eiDenseMultiplication ); }
TEST_F(TestMatrix, full_matrix_transposeproduct ) { ASSERT_TRUE( matrixMaxDiff(matTransposeMultiplication,fullTransposeMultiplication) < 100*epsilon() ); }
TEST_F(TestMatrix, crs_matrix_transposeproduct ) { ASSERT_TRUE( matrixMaxDiff(fullTransposeMultiplication,crsTransposeMultiplication) < 100*epsilon() ); }

// Matrix addition
TEST_F(TestMatrix, crs_matrix_addition )
{
    crs2 = crs1 + crs1;
    ASSERT_TRUE( matrixMaxDiff(mat*2,crs2) < 100*epsilon() );

    crs2 += crs1;
    ASSERT_TRUE( matrixMaxDiff(mat*3,crs2) < 100*epsilon() );

    crs2 -= crs1;
    ASSERT_FALSE( matrixMaxDiff(mat*2,crs2) < 100*epsilon() ); // create an error to check if I get a message from Jenkins
    //    ASSERT_TRUE( matrixMaxDiff(mat*2,crs2) < 100*epsilon() );
}
<commit_msg>META-TEST clean up<commit_after>/**
  This file contains a sequence of tests to perform for different instances of a templatized fixture.
  It is thus inlined several times in the .cpp test file.
  */

// ==============================
// Set/get value tests
TEST_F(TestMatrix, set_fullMat ) { ASSERT_TRUE( matrixMaxDiff(mat,fullMat) < 100*epsilon() ); }
TEST_F(TestMatrix, set_crs1 ) { ASSERT_TRUE( matrixMaxDiff( fullMat,crs1 ) < 100*epsilon() ); }
TEST_F(TestMatrix, set_crs2 ) { ASSERT_TRUE( matrixMaxDiff(fullMat,crs2) < 100*epsilon() ); }
TEST_F(TestMatrix, set_mapMat ) { ASSERT_TRUE( matrixMaxDiff(fullMat,mapMat) < 100*epsilon() ); }
TEST_F(TestMatrix, set_eiBlock1 ) { ASSERT_TRUE( matrixMaxDiff(fullMat,eiBlock1) < 100*epsilon() ); }
TEST_F(TestMatrix, set_eiBlock2 ) { ASSERT_TRUE( matrixMaxDiff(fullMat,eiBlock2) < 100*epsilon() ); }
TEST_F(TestMatrix, set_eiBase ) { ASSERT_TRUE( matrixMaxDiff(fullMat,eiBase) < 100*epsilon() ); }
TEST_F(TestMatrix, eigenMatrix_update ) { ASSERT_TRUE( checkEigenMatrixUpdate() ); }
TEST_F(TestMatrix, eigenMatrix_block_row_filling ) { ASSERT_TRUE( checkEigenMatrixBlockRowFilling() ); }
TEST_F(TestMatrix, eigenMatrixBlockFromCompressedRowSparseMatrix ) { ASSERT_TRUE( checkEigenMatrixBlockFromCompressedRowSparseMatrix() ); }
TEST_F(TestMatrix, eigenMapToDenseMatrix ) { ASSERT_TRUE( checkEigenDenseMatrix() ); }

// ==============================
// Matrix-Vector product tests
TEST_F(TestMatrix, set_fullVec_nrows_reference )
{
    ASSERT_TRUE(vectorMaxDiff(vecM,fullVec_nrows_reference) < epsilon() );
}
TEST_F(TestMatrix, fullMat_vector_product )
{
//    fullMat.opMulV(&fullVec_nrows_result,&fullVec_ncols);
    fullVec_nrows_result = fullMat * fullVec_ncols;
    ASSERT_TRUE(vectorMaxDiff(fullVec_nrows_reference,fullVec_nrows_result) < epsilon() );
}
TEST_F(TestMatrix, mapMat_vector_product )
{
//    mapMat.opMulV(&fullVec_nrows_result,&fullVec_ncols);
    fullVec_nrows_result = mapMat * fullVec_ncols;
    ASSERT_TRUE(vectorMaxDiff(fullVec_nrows_reference,fullVec_nrows_result) < epsilon() );
}
TEST_F(TestMatrix, eiBlock1_vector_product )
{
//    eiBlock1.opMulV(&fullVec_nrows_result,&fullVec_ncols);
//    eiBlock1.multVector(fullVec_nrows_result,fullVec_ncols);
    fullVec_nrows_result = eiBlock1 * fullVec_ncols;
    ASSERT_TRUE(vectorMaxDiff(fullVec_nrows_reference,fullVec_nrows_result) < epsilon() );

}
TEST_F(TestMatrix, crs1_vector_product )
{
//    EXPECT_TRUE(NROWS%BROWS==0 && NCOLS%BCOLS==0) << "Error: CompressedRowSparseMatrix * Vector crashes when the size of the matrix is not a multiple of the size of the matrix blocks. Aborting this test, and reporting a failure."; // otherwise the product crashes
//    crs1.opMulV(&fullVec_nrows_result,&fullVec_ncols);
    fullVec_nrows_result = crs1 * fullVec_ncols;
    ASSERT_TRUE(vectorMaxDiff(fullVec_nrows_reference,fullVec_nrows_result) < epsilon() );
}

// ==============================
// Matrix product tests
TEST_F(TestMatrix, full_matrix_product ) { ASSERT_TRUE( matrixMaxDiff(matMultiplication,fullMultiplication) < 100*epsilon() );  }
TEST_F(TestMatrix, crs_matrix_product ) { ASSERT_TRUE( matrixMaxDiff(fullMultiplication,crsMultiplication) < 100*epsilon() ); }
TEST_F(TestMatrix, EigenBase_matrix_product ) { ASSERT_TRUE( matrixMaxDiff(fullMultiplication,eiBaseMultiplication) < 100*epsilon() ); }
TEST_F(TestMatrix, EigenSparseDense_matrix_product ) { ASSERT_TRUE( EigenDenseMatrix(eiBaseMultiplication.compressedMatrix) == eiDenseMultiplication ); }
TEST_F(TestMatrix, full_matrix_transposeproduct ) { ASSERT_TRUE( matrixMaxDiff(matTransposeMultiplication,fullTransposeMultiplication) < 100*epsilon() ); }
TEST_F(TestMatrix, crs_matrix_transposeproduct ) { ASSERT_TRUE( matrixMaxDiff(fullTransposeMultiplication,crsTransposeMultiplication) < 100*epsilon() ); }

// Matrix addition
TEST_F(TestMatrix, crs_matrix_addition )
{
    crs2 = crs1 + crs1;
    ASSERT_TRUE( matrixMaxDiff(mat*2,crs2) < 100*epsilon() );

    crs2 += crs1;
    ASSERT_TRUE( matrixMaxDiff(mat*3,crs2) < 100*epsilon() );

    crs2 -= crs1;
    ASSERT_TRUE( matrixMaxDiff(mat*2,crs2) < 100*epsilon() );
}
<|endoftext|>


<commit_before>#include <fstream>
#include <iostream>
#include <omp.h>
#include <stdio.h>
#include <string>
#include <time.h>

#include "Neural_Networks.h"

using namespace std;

void Read_MNIST(string training_set_images, string training_set_labels, string test_set_images, string test_set_labels, int number_training, int number_test, float **input, float **output) {
	ifstream file(training_set_images, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 4; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = 0; h < number_training; h++) {
			unsigned char pixel;

			for (int j = 0; j < 28 * 28; j++) {
				file.read((char*)(&pixel), 1);
				input[h][j] = pixel / 255.0;
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + training_set_images + " not found" << endl;
	}

	file.open(training_set_labels, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 2; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = 0; h < number_training; h++) {
			unsigned char label;

			file.read((char*)(&label), 1);

			for (int j = 0; j < 10; j++) {
				output[h][j] = (j == label);
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + training_set_labels + " not found" << endl;
	}

	file.open(test_set_images, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 4; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = number_training; h < number_training + number_test; h++) {
			unsigned char pixel;

			for (int j = 0; j < 28 * 28; j++) {
				file.read((char*)(&pixel), 1);
				input[h][j] = pixel / 255.0;
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + test_set_images + " not found" << endl;
	}

	file.open(test_set_labels, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 2; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = number_training; h < number_training + number_test; h++) {
			unsigned char label;

			file.read((char*)(&label), 1);

			for (int j = 0; j < 10; j++) {
				output[h][j] = (j == label);
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + test_set_labels + " not found" << endl;
	}
}

int main() {
	int batch_size = 128;
	int epochs = 100;
	int number_threads = 6;
	int number_training = 60000;
	int number_test = 10000;
	int number_nodes[] = { 784, 10 };
	int time_step = 28;

	float **x_data = new float*[number_training + number_test];
	float **y_data = new float*[number_training + number_test];
	float **x_train = x_data;
	float **y_train = y_data;
	float **x_test = &x_data[number_training];
	float **y_test = &y_data[number_training];

	double decay = 0.000001;
	double learning_rate = 0.03;

	string path;

	Neural_Networks NN = Neural_Networks();

	cout << "path where MNIST handwritten digits dataset is : ";
	getline(cin, path);

	for (int h = 0; h < number_training + number_test; h++) {
		x_data[h] = new float[number_nodes[0]];
		y_data[h] = new float[number_nodes[1]];
	}
	Read_MNIST(path + "train-images.idx3-ubyte", path + "train-labels.idx1-ubyte", path + "t10k-images.idx3-ubyte", path + "t10k-labels.idx1-ubyte", number_training, number_test, x_data, y_data);
	omp_set_num_threads(number_threads);

	srand(1);

	NN.Add(Layer(time_step, number_nodes[0] / time_step));
	NN.Add(RNN(time_step, 128))->Activation(Activation::relu)->Direction(+1);
	NN.Add(RNN(time_step, 128))->Activation(Activation::relu)->Direction(-1);
	NN.Add(Layer(2, 128));
	NN.Add(Layer(1, 256));
	NN.Add(number_nodes[1])->Activation(Activation::softmax);

	NN.Connect(1, 0, "W");
	NN.Connect(1, 1, "W,recurrent");
	NN.Connect(2, 0, "W");
	NN.Connect(2, 2, "W,recurrent");
	{
		unordered_multimap<int, int> time_connection;

		time_connection.insert(pair<int, int>(0, time_step - 1));
		NN.Connect(3, 1, "copy", &time_connection);

		time_connection.clear();
		time_connection.insert(pair<int, int>(1, 0));
		NN.Connect(3, 2, "copy", &time_connection);
	}
	NN.Connect(4, 3, "copy");
	NN.Connect(5, 4, "W");

	NN.Compile(Loss::cross_entropy, new Optimizer(SGD(learning_rate, decay)));

	for (int e = 0, time = clock(); e < epochs; e++) {
		int score[2] = { 0, };

		float **_input = new float*[batch_size];
		float **output = new float*[batch_size];

		double loss[2] = { NN.Fit(NN.Shuffle(x_train, number_training), NN.Shuffle(y_train, number_training), number_training, batch_size), NN.Evaluate(x_test, y_test, number_test, batch_size) };

		for (int h = 0; h < batch_size; h++) {
			output[h] = new float[number_nodes[1]];
		}
		for (int h = 0, i = 0; i < number_training + number_test; i++) {
			_input[h] = x_data[i];

			if (++h == batch_size || i == number_training + number_test - 1) {
				NN.Predict(_input, output, h);

				for (int argmax, index = i - h + 1; --h >= 0;) {
					double max = 0;

					for (int j = 0; j < number_nodes[1]; j++) {
						if (j == 0 || max < output[h][j]) {
							max = output[h][argmax = j];
						}
					}
					score[(index + h < number_training) ? (0) : (1)] += (int)y_data[index + h][argmax];
				}
				h = 0;
			}
		}
		printf("loss: %.4f / %.4f	accuracy: %.4f / %.4f	step %d  %.2f sec\n", loss[0], loss[1], 1.0 * score[0] / number_training, 1.0 * score[1] / number_test, e + 1, (double)(clock() - time) / CLOCKS_PER_SEC);

		for (int h = 0; h < batch_size; h++) {
			delete[] output[h];
		}
		delete[] _input;
		delete[] output;
	}

	for (int i = 0; i < number_training + number_test; i++) {
		delete[] x_data[i];
		delete[] y_data[i];
	}
	delete[] x_data;
	delete[] y_data;
}
<commit_msg>Update main.cpp<commit_after>#include <fstream>
#include <iostream>
#include <omp.h>
#include <stdio.h>
#include <string>
#include <time.h>

#include "Neural_Networks.h"

using namespace std;

void Read_MNIST(string training_set_images, string training_set_labels, string test_set_images, string test_set_labels, int number_training, int number_test, float **input, float **output) {
	ifstream file(training_set_images, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 4; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = 0; h < number_training; h++) {
			unsigned char pixel;

			for (int j = 0; j < 28 * 28; j++) {
				file.read((char*)(&pixel), 1);
				input[h][j] = pixel / 255.0;
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + training_set_images + " not found" << endl;
	}

	file.open(training_set_labels, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 2; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = 0; h < number_training; h++) {
			unsigned char label;

			file.read((char*)(&label), 1);

			for (int j = 0; j < 10; j++) {
				output[h][j] = (j == label);
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + training_set_labels + " not found" << endl;
	}

	file.open(test_set_images, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 4; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = number_training; h < number_training + number_test; h++) {
			unsigned char pixel;

			for (int j = 0; j < 28 * 28; j++) {
				file.read((char*)(&pixel), 1);
				input[h][j] = pixel / 255.0;
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + test_set_images + " not found" << endl;
	}

	file.open(test_set_labels, ifstream::binary);

	if (file.is_open()) {
		for (int h = 0, value; h < 2; h++) {
			file.read((char*)(&value), sizeof(int));
		}
		for (int h = number_training; h < number_training + number_test; h++) {
			unsigned char label;

			file.read((char*)(&label), 1);

			for (int j = 0; j < 10; j++) {
				output[h][j] = (j == label);
			}
		}
		file.close();
	}
	else {
		cerr << "[Read_MNIST], " + test_set_labels + " not found" << endl;
	}
}

int main() {
	int batch_size = 128;
	int epochs = 100;
	int number_threads = 6;
	int number_training = 60000;
	int number_test = 10000;
	int number_nodes[] = { 784, 10 };
	int time_step = 28;

	float **x_data = new float*[number_training + number_test];
	float **y_data = new float*[number_training + number_test];
	float **x_train = x_data;
	float **y_train = y_data;
	float **x_test = &x_data[number_training];
	float **y_test = &y_data[number_training];

	double decay = 0.000001;
	double learning_rate = 0.03;

	string path;

	Neural_Networks NN = Neural_Networks();

	cout << "path where MNIST handwritten digits dataset is : ";
	getline(cin, path);

	for (int h = 0; h < number_training + number_test; h++) {
		x_data[h] = new float[number_nodes[0]];
		memset(y_data[h] = new float[time_step * number_nodes[1]], 0, sizeof(float) * time_step * number_nodes[1]);
	}
	Read_MNIST(path + "train-images.idx3-ubyte", path + "train-labels.idx1-ubyte", path + "t10k-images.idx3-ubyte", path + "t10k-labels.idx1-ubyte", number_training, number_test, x_data, y_data);
	omp_set_num_threads(number_threads);

	srand(2);

	NN.Add(Layer(time_step, number_nodes[0] / time_step));
	NN.Add(RNN(time_step, 128))->Activation(Activation::relu)->Direction(+1);
	NN.Add(RNN(time_step, 128))->Activation(Activation::relu)->Direction(-1);
	NN.Add(Layer(2, 128));
	NN.Add(Layer(1, 256));
	NN.Add(number_nodes[1])->Activation(Activation::softmax);

	NN.Connect(1, 0, "W");
	NN.Connect(1, 1, "W,recurrent");
	NN.Connect(2, 0, "W");
	NN.Connect(2, 2, "W,recurrent");
	{
		unordered_multimap<int, int> time_connection;

		time_connection.insert(pair<int, int>(0, time_step - 1));
		NN.Connect(3, 1, "copy", &time_connection);

		time_connection.clear();
		time_connection.insert(pair<int, int>(1, 0));
		NN.Connect(3, 2, "copy", &time_connection);
	}
	NN.Connect(4, 3, "copy");
	NN.Connect(5, 4, "W");

	NN.Compile(Loss::cross_entropy, Optimizer(SGD(learning_rate, decay)));

	for (int e = 0, time = clock(); e < epochs; e++) {
		int score[2] = { 0, };

		float **_input = new float*[batch_size];
		float **output = new float*[batch_size];

		double loss[2] = { NN.Fit(NN.Shuffle(x_train, number_training), NN.Shuffle(y_train, number_training), number_training, batch_size), NN.Evaluate(x_test, y_test, number_test, batch_size) };

		for (int h = 0; h < batch_size; h++) {
			output[h] = new float[time_step * number_nodes[1]];
		}
		for (int h = 0, i = 0; i < number_training + number_test; i++) {
			_input[h] = x_data[i];

			if (++h == batch_size || i == number_training + number_test - 1) {
				NN.Predict(_input, output, h);

				for (int argmax, index = i - h + 1; --h >= 0;) {
					double max = 0;

					for (int j = 0; j < number_nodes[1]; j++) {
						if (j == 0 || max < output[h][j]) {
							max = output[h][argmax = j];
						}
					}
					score[(index + h < number_training) ? (0) : (1)] += (int)y_data[index + h][argmax];
				}
				h = 0;
			}
		}
		printf("loss: %.4f / %.4f	accuracy: %.4f / %.4f	step %d  %.2f sec\n", loss[0], loss[1], 1.0 * score[0] / number_training, 1.0 * score[1] / number_test, e + 1, (double)(clock() - time) / CLOCKS_PER_SEC);

		for (int h = 0; h < batch_size; h++) {
			delete[] output[h];
		}
		delete[] _input;
		delete[] output;
	}

	for (int i = 0; i < number_training + number_test; i++) {
		delete[] x_data[i];
		delete[] y_data[i];
	}
	delete[] x_data;
	delete[] y_data;
}
<|endoftext|>


<commit_before>#include "match.h"
#include <vector>
#include "globalsettings.h"

Match::Match(Read* r, int pos, int distance, bool reversed){
    mRead = r;
    mSequence = NULL;
    mDistance = distance;
    mPos = pos;
    mReversed = reversed;
}

Match::Match(char* seq, char meanQual, int pos, int distance, bool reversed){
    mRead = NULL;
    mSequence = seq;
    mMeanQual = meanQual;
    mDistance = distance;
    mPos = pos;
    mReversed = reversed;
}

Match::~Match(){
    // we don't delete mRead or mSequence here since they are shared by different objects
    // and will be deleted in other places
    for(int i=0;i<mOriginalReads.size();i++){
        delete mOriginalReads[i];
        mOriginalReads[i] = NULL;
    }
}

int Match::readlength() const {
    if(GlobalSettings::simplifiedMode)
        return strlen(mSequence);
    else
        return mRead->length();
}

void Match::addOriginalRead(Read* r){
    mOriginalReads.push_back(new Read(*r));
}

void Match::addOriginalPair(ReadPair* pair){
    mOriginalReads.push_back(new Read(*pair->mLeft));
    mOriginalReads.push_back(new Read(*pair->mRight));
}

void Match::print(int leftlen, int centerlen, int rightlen){
    if(GlobalSettings::simplifiedMode)
        mRead = new Read(mSequence, mMeanQual);
    cout<<"pos: "<<mPos<<", distance: "<<mDistance;
    if(mReversed)
        cout<<", reverse";
    else
        cout<<", forward";
    cout<<endl;
    vector<int> breaks;
    breaks.push_back(max(mPos-leftlen, 0));
    breaks.push_back( mPos );
    breaks.push_back( mPos+centerlen );
    breaks.push_back( min(mPos+centerlen+rightlen, mRead->length()));
    mRead->printWithBreaks(breaks);
    if(GlobalSettings::simplifiedMode) {
        delete mRead;
        mRead = NULL;
    }
}

void Match::printHtmlTD(ofstream& file, int leftlen, int centerlen, int rightlen, int mutid, int matchid){
    if(GlobalSettings::simplifiedMode)
        mRead = new Read(mSequence, mMeanQual);
    file<<"<a title='"<<mRead->mName<<"'>";
    file<<"d:" << mDistance;
    if(mReversed)
        file<<", <--";
    else
        file<<", -->";

    file<<"";

    vector<int> breaks;
    breaks.push_back(max(mPos-leftlen, 0));
    breaks.push_back( mPos );
    breaks.push_back( mPos+centerlen );
    breaks.push_back( min(mPos+centerlen+rightlen, mRead->length()));
    mRead->printHtmlTDWithBreaks(file, breaks, mutid, matchid);
    if(GlobalSettings::simplifiedMode) {
        delete mRead;
        mRead = NULL;
    }
}

void Match::printJS(ofstream& file, int leftlen, int centerlen, int rightlen) {
    if(GlobalSettings::simplifiedMode)
        mRead = new Read(mSequence, mMeanQual);
    vector<int> breaks;
    breaks.push_back(max(mPos-leftlen, 0));
    breaks.push_back( mPos );
    breaks.push_back( mPos+centerlen );
    breaks.push_back( min(mPos+centerlen+rightlen, mRead->length()));
    mRead->printJSWithBreaks(file, breaks);
    if(GlobalSettings::simplifiedMode) {
        delete mRead;
        mRead = NULL;
    }
}

void Match::printReadsToFile(ofstream& file){
    for(int i=0;i<mOriginalReads.size();i++){
        mOriginalReads[i]->printFile(file);
    }
}

void Match::setReversed(bool flag){
    mReversed = flag;
}

int Match::countUnique(vector<Match*>& matches) {
    if(matches.size()==0)
        return 0;
    int count = 1;
    Match* cur = matches[0];
    for(int i=1;i<matches.size();i++){
        Match* m = matches[i];
        if( *m > *cur || *m < *cur) {
            cur = m;
            count++;
        }
    }
    return count;
}
<commit_msg>fix Linux compile<commit_after>#include "match.h"
#include <vector>
#include "globalsettings.h"
#include <memory.h>

Match::Match(Read* r, int pos, int distance, bool reversed){
    mRead = r;
    mSequence = NULL;
    mDistance = distance;
    mPos = pos;
    mReversed = reversed;
}

Match::Match(char* seq, char meanQual, int pos, int distance, bool reversed){
    mRead = NULL;
    mSequence = seq;
    mMeanQual = meanQual;
    mDistance = distance;
    mPos = pos;
    mReversed = reversed;
}

Match::~Match(){
    // we don't delete mRead or mSequence here since they are shared by different objects
    // and will be deleted in other places
    for(int i=0;i<mOriginalReads.size();i++){
        delete mOriginalReads[i];
        mOriginalReads[i] = NULL;
    }
}

int Match::readlength() const {
    if(GlobalSettings::simplifiedMode)
        return strlen(mSequence);
    else
        return mRead->length();
}

void Match::addOriginalRead(Read* r){
    mOriginalReads.push_back(new Read(*r));
}

void Match::addOriginalPair(ReadPair* pair){
    mOriginalReads.push_back(new Read(*pair->mLeft));
    mOriginalReads.push_back(new Read(*pair->mRight));
}

void Match::print(int leftlen, int centerlen, int rightlen){
    if(GlobalSettings::simplifiedMode)
        mRead = new Read(mSequence, mMeanQual);
    cout<<"pos: "<<mPos<<", distance: "<<mDistance;
    if(mReversed)
        cout<<", reverse";
    else
        cout<<", forward";
    cout<<endl;
    vector<int> breaks;
    breaks.push_back(max(mPos-leftlen, 0));
    breaks.push_back( mPos );
    breaks.push_back( mPos+centerlen );
    breaks.push_back( min(mPos+centerlen+rightlen, mRead->length()));
    mRead->printWithBreaks(breaks);
    if(GlobalSettings::simplifiedMode) {
        delete mRead;
        mRead = NULL;
    }
}

void Match::printHtmlTD(ofstream& file, int leftlen, int centerlen, int rightlen, int mutid, int matchid){
    if(GlobalSettings::simplifiedMode)
        mRead = new Read(mSequence, mMeanQual);
    file<<"<a title='"<<mRead->mName<<"'>";
    file<<"d:" << mDistance;
    if(mReversed)
        file<<", <--";
    else
        file<<", -->";

    file<<"";

    vector<int> breaks;
    breaks.push_back(max(mPos-leftlen, 0));
    breaks.push_back( mPos );
    breaks.push_back( mPos+centerlen );
    breaks.push_back( min(mPos+centerlen+rightlen, mRead->length()));
    mRead->printHtmlTDWithBreaks(file, breaks, mutid, matchid);
    if(GlobalSettings::simplifiedMode) {
        delete mRead;
        mRead = NULL;
    }
}

void Match::printJS(ofstream& file, int leftlen, int centerlen, int rightlen) {
    if(GlobalSettings::simplifiedMode)
        mRead = new Read(mSequence, mMeanQual);
    vector<int> breaks;
    breaks.push_back(max(mPos-leftlen, 0));
    breaks.push_back( mPos );
    breaks.push_back( mPos+centerlen );
    breaks.push_back( min(mPos+centerlen+rightlen, mRead->length()));
    mRead->printJSWithBreaks(file, breaks);
    if(GlobalSettings::simplifiedMode) {
        delete mRead;
        mRead = NULL;
    }
}

void Match::printReadsToFile(ofstream& file){
    for(int i=0;i<mOriginalReads.size();i++){
        mOriginalReads[i]->printFile(file);
    }
}

void Match::setReversed(bool flag){
    mReversed = flag;
}

int Match::countUnique(vector<Match*>& matches) {
    if(matches.size()==0)
        return 0;
    int count = 1;
    Match* cur = matches[0];
    for(int i=1;i<matches.size();i++){
        Match* m = matches[i];
        if( *m > *cur || *m < *cur) {
            cur = m;
            count++;
        }
    }
    return count;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2009 Stephen Liu
 * For license terms, see the file COPYING along with this library.
 */

#ifndef __spprotobuf_hpp__
#define __spprotobuf_hpp__

#include <stdint.h>

class SP_ProtoBufEncoder
{
public:
	SP_ProtoBufEncoder( int initLen = 0 );
	~SP_ProtoBufEncoder();

	int addVarint( int fieldNumber, uint64_t value );
	int addZigZagInt( int fieldNumber, int64_t value );

	int addDouble( int fieldNumber, double value );
	int addFloat( int fieldNumber, float value );

	int add64Bit( int fieldNumber, uint64_t value );
	int addBinary( int fieldNumber, const char * buffer, int len );
	int add32Bit( int fieldNumber, uint32_t value );

	int addPacked( int fieldNumber, uint16_t * array, int size );
	int addPacked( int fieldNumber, uint32_t * array, int size );
	int addPacked( int fieldNumber, uint64_t * array, int size );
	int addPacked( int fieldNumber, float * array, int size );
	int addPacked( int fieldNumber, double * array, int size );

	const char * getBuffer();
	int getSize();

	void reset();

private:
	static int encodeVarint( uint64_t value, char *buffer );

	static int encode32Bit( uint32_t value, char * buffer );

	static int encode64Bit( uint64_t value, char * buffer );

	int ensureSpace( int space );

private:
	char * mBuffer;
	int mTotal, mSize;
};

/**
 * a simple decoder for protobuf
 */
class SP_ProtoBufDecoder
{
public:

	enum {
		eWireVarint = 0,
		eWire64Bit  = 1,
		eWireBinary = 2,
		eWire32Bit  = 5
	};

	typedef struct tagKeyValPair {
		int mFieldNumber;
		int mWireType;

		union {              // wire type 0
			uint64_t u;
			int64_t  s;
		} mVarint;

		int64_t  mZigZagInt; // wire type 0

		union {              // wire type 1
			uint64_t u;
			int64_t  s;
		} m64Bit;

		struct {             // wire type 2
			const char * mBuffer;
			int mLen;
		} mBinary;

		union {              // wire type 5
			uint32_t u;
			int32_t  s;
		} m32Bit;
	} KeyValPair_t;

public:

	SP_ProtoBufDecoder( const char * buffer, int len );
	~SP_ProtoBufDecoder();

	bool getNext( KeyValPair_t * pair );

	bool find( int fieldNumber, KeyValPair_t * pair, int index = 0 );

	void rewind();

	static char * dup( const char * buffer, int len );

	static int getPacked( const char * buffer, int len, uint16_t * array, int size );
	static int getPacked( const char * buffer, int len, uint32_t * array, int size );
	static int getPacked( const char * buffer, int len, uint64_t * array, int size );
	static int getPacked( const char * buffer, int len, float * array, int size );
	static int getPacked( const char * buffer, int len, double * array, int size );

private:

	// @return > 0 : parse ok, consume how many bytes, -1 : unknown type
	static int decodeVarint( uint64_t *value, const char *buffer );

	static uint32_t decode32Bit( const char * buffer );

	// @return > 0 : parse ok, consume how many bytes, -1 : unknown type
	static int getPair( const char * buffer, KeyValPair_t * pair );

private:
	const char * mBuffer, * mEnd;
	const char * mCurr;
};

#endif

<commit_msg>support to read double/float<commit_after>/*
 * Copyright 2009 Stephen Liu
 * For license terms, see the file COPYING along with this library.
 */

#ifndef __spprotobuf_hpp__
#define __spprotobuf_hpp__

#include <stdint.h>

class SP_ProtoBufEncoder
{
public:
	SP_ProtoBufEncoder( int initLen = 0 );
	~SP_ProtoBufEncoder();

	int addVarint( int fieldNumber, uint64_t value );
	int addZigZagInt( int fieldNumber, int64_t value );

	int addDouble( int fieldNumber, double value );
	int addFloat( int fieldNumber, float value );

	int add64Bit( int fieldNumber, uint64_t value );
	int addBinary( int fieldNumber, const char * buffer, int len );
	int add32Bit( int fieldNumber, uint32_t value );

	int addPacked( int fieldNumber, uint16_t * array, int size );
	int addPacked( int fieldNumber, uint32_t * array, int size );
	int addPacked( int fieldNumber, uint64_t * array, int size );
	int addPacked( int fieldNumber, float * array, int size );
	int addPacked( int fieldNumber, double * array, int size );

	const char * getBuffer();
	int getSize();

	void reset();

private:
	static int encodeVarint( uint64_t value, char *buffer );

	static int encode32Bit( uint32_t value, char * buffer );

	static int encode64Bit( uint64_t value, char * buffer );

	int ensureSpace( int space );

private:
	char * mBuffer;
	int mTotal, mSize;
};

/**
 * a simple decoder for protobuf
 */
class SP_ProtoBufDecoder
{
public:

	enum {
		eWireVarint = 0,
		eWire64Bit  = 1,
		eWireBinary = 2,
		eWire32Bit  = 5
	};

	typedef struct tagKeyValPair {
		int mFieldNumber;
		int mWireType;

		union {              // wire type 0
			uint64_t u;
			int64_t  s;
		} mVarint;

		int64_t  mZigZagInt; // wire type 0

		union {              // wire type 1
			uint64_t u;
			int64_t  s;
			double   d;
		} m64Bit;

		struct {             // wire type 2
			const char * mBuffer;
			int mLen;
		} mBinary;

		union {              // wire type 5
			uint32_t u;
			int32_t  s;
			float    f;
		} m32Bit;
	} KeyValPair_t;

public:

	SP_ProtoBufDecoder( const char * buffer, int len );
	~SP_ProtoBufDecoder();

	bool getNext( KeyValPair_t * pair );

	bool find( int fieldNumber, KeyValPair_t * pair, int index = 0 );

	void rewind();

	static char * dup( const char * buffer, int len );

	static int getPacked( const char * buffer, int len, uint16_t * array, int size );
	static int getPacked( const char * buffer, int len, uint32_t * array, int size );
	static int getPacked( const char * buffer, int len, uint64_t * array, int size );
	static int getPacked( const char * buffer, int len, float * array, int size );
	static int getPacked( const char * buffer, int len, double * array, int size );

private:

	// @return > 0 : parse ok, consume how many bytes, -1 : unknown type
	static int decodeVarint( uint64_t *value, const char *buffer );

	static uint32_t decode32Bit( const char * buffer );

	// @return > 0 : parse ok, consume how many bytes, -1 : unknown type
	static int getPair( const char * buffer, KeyValPair_t * pair );

private:
	const char * mBuffer, * mEnd;
	const char * mCurr;
};

#endif

<|endoftext|>


<commit_before>#include "infer.hpp"
#include "ast.hpp"
#include "package.hpp"

#include <sstream>

namespace type {

template<class Func>
static void iter_rows(mono self, Func func) {
  assert(self.kind() == kind::row());
  self.match
    ([&](const app& self) {
      const auto e = extension::unpack(self);
      func(e.attr, e.head);
      iter_rows(e.tail, func);
    }, [](const mono& ) { });
}

  struct let {
    const ::list<ast::bind> defs;
    const ast::expr body;

    static symbol fix() { return "__fix__"; }

    // rewrite let as non-recursive let + fix **when defining functions**
    static let rewrite(const ast::let& self) {
      using ::list;
      const list<ast::bind> defs = map(self.defs, [](ast::bind self) {
          return self.value.match<ast::bind>
          ([&](const ast::abs& abs) {
            return ast::bind{self.name,
                             ast::app{ast::var{fix()},
                                      ast::abs{self.name >>= list<ast::abs::arg>(),
                                               self.value}
                                      >>= list<ast::expr>()}};
          },
            [&](const ast::expr& expr) { return self; });
        });

      return {defs, *self.body};
    }
  };

  // rewrite app as nested unary applications
  static ast::app rewrite(const ast::app& self) {
    const ast::expr& init = *self.func;
    return foldl(init, self.args, [](ast::expr func, ast::expr arg) {
        return ast::app(func, arg >>= list<ast::expr>());
      }).cast<ast::app>();
  }

  // reconstruct actual type from reified type: ... -> (type 'a) yields 'a
  // note: t must be substituted
  static mono reconstruct(ref<state> s, mono t) {
    // std::clog << "reconstructing: " << s->generalize(t) << std::endl;

    if(auto self = t.get<app>()) {
      // std::clog << "ctor: " << s->generalize((*self)->ctor) << std::endl;

      if((*self)->ctor == ty) {
        return (*self)->arg;
      }
    }

    auto from = s->fresh();
    auto to = s->fresh();

    s->unify(from >>= to, t);
    return reconstruct(s, s->substitute(to));
  }

  static cst constructor(mono t) {
    if(auto self = t.get<app>()) {
      return constructor((*self)->ctor);
    }

    if(auto self = t.get<cst>()) {
      return *self;
    }

    // TODO restrictive?
    throw error("constructor must be a constant");
  }

  struct infer_visitor {
    using type = mono;

    template<class T>
    mono operator()(const T& self, const ref<state>&) const {
      throw std::logic_error("infer unimplemented: " + tool::type_name(typeid(T)));
    }

    // var
    mono operator()(const ast::var& self, const ref<state>& s) const {
      try {
        return s->instantiate(s->vars->find(self.name));
      } catch(std::out_of_range& e) {
        throw error("unbound variable " + tool::quote(self.name.get()));
      }
    }

    // abs
    mono operator()(const ast::abs& self, const ref<state>& s) const {

      // function scope
      const auto sub = scope(s);

      // construct function type
      const mono result = s->fresh();

      const mono res = foldr(result, self.args, [&](ast::abs::arg arg, mono tail) {
          const mono sig = arg.match<mono>
          ([&](symbol self) {
            // untyped arguments: trivial signature
            const mono a = sub->fresh();
            return a;
          },
            [&](ast::abs::typed self) {
              // obtain reified type from annoatation
              const mono t = infer(s, self.type);

              // extract actual type from reified
              const mono r = reconstruct(s, s->substitute(t));

              // obtain type constructor from type
              const cst c = constructor(r);

              try {
                // fetch associated signature, if any
                const auto sig = s->sigs->at(c);

                // instantiate signature
                return sub->instantiate(sig);

              } catch(std::out_of_range&) {
                throw error("unknown signature " + tool::quote(c->name.get()));
              }
            });

          const mono outer = s->fresh();
          const mono inner = sub->fresh();

          sub->unify(outer >>= inner, sig);
          sub->def(arg.name(), outer);

          // std::clog << "inner: " << sub->vars->find(arg.name()) << std::endl;

          return outer >>= tail;
        });

      // infer lambda body with augmented environment
      s->unify(result, infer(sub, *self.body));

      return res;
    }

    // app
    mono operator()(const ast::app& self, const ref<state>& s) const {
      // normalize application as unary
      const ast::app rw = rewrite(self);
      assert(size(rw.args) == 1);

      // infer func/arg types for application
      const auto with_inferred = [&](auto cont) {
        const mono func = infer(s, *rw.func);
        const mono arg = infer(s, rw.args->head);

        return cont(func, arg);
      };

      // obtain inner type from a type with associated signature
      const auto inner_type = [&](mono t) {
        // obtain type constructor from argument type
        const cst c = constructor(s->substitute(t));

        try {
          auto sig = s->sigs->at(c);

          const mono inner = s->fresh();
          s->unify(t >>= inner, s->instantiate(sig));

          return inner;
        } catch(std::out_of_range&) {
          throw error("unknown signature");
        }
      };

      // check if application works with given func/arg type
      const auto check = [&](mono func, mono arg) {
        const mono ret = s->fresh();
        s->unify(func , arg >>= ret);
        return ret;
      };

      // TODO find a less stupid way of trying all cases?
      try {
        // normal case
        return with_inferred([&](mono func, mono arg) {
            return check(func, arg);
          });

      } catch(error& e) {

        try {
          // open func type and retry
          return with_inferred([&](mono func, mono arg) {
              const mono inner_func = inner_type(func);
              return check(inner_func, arg);
            });
        }
        catch(error& ) {  }

        try {
          // open arg type and retry
          return with_inferred([&](mono func, mono arg) {
              const mono inner_arg = inner_type(arg);
              return check(func, inner_arg);
            });
        }
        catch(error& ) {  }

        try {
          // open both func and arg types and retry
          return with_inferred([&](mono func, mono arg) {
              const mono inner_func = inner_type(func);
              const mono inner_arg = inner_type(arg);
              return check(inner_func, inner_arg);
            });
        }
        catch(error& ) {  }

        throw e;
      }

    }

    // non-recursive let
    mono operator()(const let& self, const ref<state>& s) const {
      auto sub = scope(s);

      for(ast::bind def : self.defs) {
        sub->vars->locals.emplace(def.name, s->generalize(infer(s, def.value)));
      }

      return infer(sub, self.body);
    }

    // recursive let
    mono operator()(const ast::let& self, const ref<state>& s) const {
      auto sub = scope(s);

      const mono a = sub->fresh();
      sub->def(let::fix(), (a >>= a) >>= a);

      return operator()(let::rewrite(self), sub);
    }

    // sel
    mono operator()(const ast::sel& self, const ref<state>& s) const {
      const mono tail = s->fresh(kind::row());
      const mono head = s->fresh(kind::term());

      const mono row = ext(self.name)(head)(tail);
      return record(row) >>= head;
    }

    // record
    mono operator()(const ast::record& self, const ref<state>& s) const {
      const mono init = empty;
      const mono row = foldr(init, self.attrs, [&](ast::record::attr attr, mono tail) {
          return ext(attr.name)(infer(s, attr.value))(tail);
        });

      return record(row);
    }

    // make
    mono operator()(const ast::make& self, const ref<state>& s) const {
      // get signature type
      const poly sig = s->vars->find(self.type);

      auto sub = scope(s);

      const mono outer = s->fresh();
      const mono inner = sub->fresh();

      // instantiate signature at sub level prevents generalization of
      // contravariant side (variables only appearing in the covariant side will
      // be generalized)
      s->unify(ty(outer) >>= ty(inner), sub->instantiate(sig));

      // vanilla covariant type
      const poly reference = sub->generalize(inner);

      // build provided type
      const mono init = empty;
      const mono provided =
        record(foldr(init, self.attrs,
                     [&](const ast::record::attr attr, mono tail) {
                       return row(attr.name, infer(sub, attr.value)) |= tail;
                     }));

      // now also unify inner with provided type
      s->unify(inner, provided);

      // now generalize the provided type
      const poly gen = sub->generalize(inner);

      // generalization check: quantified variables in reference/gen should
      // substitute to the same variables
      std::set<var> quantified;
      for(const var& v : gen.forall) {
        assert(sub->substitute(v) == v);
        quantified.insert(v);
      }

      // make sure all reference quantified references substitute to quantified
      // variables
      for(const var& v : reference.forall) {
        const mono vs = sub->substitute(v);
        if(auto u = vs.get<var>()) {
          auto it = quantified.find(*u);
          if(it != quantified.end()) {
            continue;
          }
        }

        std::stringstream ss;
        logger(ss) << "failed to generalize " << gen
                   << " as " << reference;

        throw error(ss.str());
      }

      return outer;
    }

    // cond
    mono operator()(const ast::cond& self, const ref<state>& s) const {
      const mono test = infer(s, *self.test);
      s->unify(test, boolean);

      const mono conseq = infer(s, *self.conseq);
      const mono alt = infer(s, *self.alt);

      const mono result = s->fresh();
      s->unify(result, conseq);
      s->unify(result, alt);

      return result;
    }

    // def
    mono operator()(const ast::def& self, const ref<state>& s) const {
      using ::list;
      const mono value =
        infer(s, ast::let(ast::bind{self.name, *self.value} >>= list<ast::bind>(),
                          ast::var{self.name}));
      try {
        s->def(self.name, value);
        return io(unit);
      } catch(std::runtime_error& e) {
        throw error(e.what());
      }
    }

    // use
    mono operator()(const ast::use& self, const ref<state>& s) const {
      // infer value type
      const mono value = infer(s, *self.env);

      // make sure value type is a record
      const mono row = s->fresh(kind::row());
      s->unify(value, record(row));

      auto sub = scope(s);

      // fill sub scope with record contents
      iter_rows(s->substitute(row), [&](symbol attr, mono t) {
          // TODO generalization issue?
          sub->def(attr, t);
        });

      return infer(sub, *self.body);
    }

    // import
    mono operator()(const ast::import& self, const ref<state>& s) const {
      auto it = s->vars->locals.find(self.package);
      if(it != s->vars->locals.end()) {
        throw error("variable " + tool::quote(self.package.get()) + " already defined");
      }

      const package pkg = package::import(self.package);

      s->vars->locals.emplace(self.package, pkg.sig());
      return io(unit);
    }

    // lit
    mono operator()(const ast::lit<::unit>& self, const ref<state>&) const {
      return unit;
    }

    mono operator()(const ast::lit<::boolean>& self, const ref<state>&) const {
      return boolean;
    }

    mono operator()(const ast::lit<::integer>& self, const ref<state>&) const {
      return integer;
    }

    mono operator()(const ast::lit<::real>& self, const ref<state>&) const {
      return real;
    }

  };

  mono infer(const ref<state>& s, const ast::expr& self) {
    return self.visit(infer_visitor(), s);
  }

}
<commit_msg>refactor a bit<commit_after>#include "infer.hpp"
#include "ast.hpp"
#include "package.hpp"

#include <sstream>

namespace type {

  template<class Func>
  static void iter_rows(mono self, Func func) {
    assert(self.kind() == kind::row());
    self.match
      ([&](const app& self) {
        const auto e = extension::unpack(self);
        func(e.attr, e.head);
        iter_rows(e.tail, func);
      }, [](const mono& ) { });
  }

  struct let {
    const ::list<ast::bind> defs;
    const ast::expr body;

    static symbol fix() { return "__fix__"; }

    // rewrite let as non-recursive let + fix **when defining functions**
    static let rewrite(const ast::let& self) {
      using ::list;
      const list<ast::bind> defs = map(self.defs, [](ast::bind self) {
          return self.value.match<ast::bind>
          ([&](const ast::abs& abs) {
            return ast::bind{self.name,
                             ast::app{ast::var{fix()},
                                      ast::abs{self.name >>= list<ast::abs::arg>(),
                                               self.value}
                                      >>= list<ast::expr>()}};
          },
            [&](const ast::expr& expr) { return self; });
        });

      return {defs, *self.body};
    }
  };

  // rewrite app as nested unary applications
  static ast::app rewrite(const ast::app& self) {
    const ast::expr& init = *self.func;
    return foldl(init, self.args, [](ast::expr func, ast::expr arg) {
        return ast::app(func, arg >>= list<ast::expr>());
      }).cast<ast::app>();
  }

  // reconstruct actual type from reified type: ... -> (type 'a) yields 'a
  // note: t must be substituted
  static mono reconstruct(ref<state> s, mono t) {
    // std::clog << "reconstructing: " << s->generalize(t) << std::endl;

    if(auto self = t.get<app>()) {
      // std::clog << "ctor: " << s->generalize((*self)->ctor) << std::endl;

      if((*self)->ctor == ty) {
        return (*self)->arg;
      }
    }

    auto from = s->fresh();
    auto to = s->fresh();

    s->unify(from >>= to, t);
    return reconstruct(s, s->substitute(to));
  }

  // obtain constructor for a monotype
  static cst constructor(mono t) {
    if(auto self = t.get<app>()) {
      return constructor((*self)->ctor);
    }

    if(auto self = t.get<cst>()) {
      return *self;
    }

    // TODO restrictive?
    throw error("constructor must be a constant");
  }

  template<class T>
  static mono infer(const ref<T>& s, const T& self) {
    throw std::logic_error("infer unimplemented: " + tool::type_name(typeid(T)));
  }

  // var
  static mono infer(const ref<state>& s, const ast::var& self) {
    try {
      return s->instantiate(s->vars->find(self.name));
    } catch(std::out_of_range& e) {
      throw error("unbound variable " + tool::quote(self.name.get()));
    }
  }

  // abs
  static mono infer(const ref<state>& s, const ast::abs& self) {
    // function scope
    const auto sub = scope(s);

    // construct function type
    const mono result = s->fresh();

    const mono res = foldr(result, self.args, [&](ast::abs::arg arg, mono tail) {
        const mono sig = arg.match<mono>([&](symbol self) {
            // untyped arguments: trivial signature
            const mono a = sub->fresh();
            return a;
          },
          [&](ast::abs::typed self) {
            // obtain reified type from annoatation
            const mono t = infer(s, self.type);

            // extract actual type from reified
            const mono r = reconstruct(s, s->substitute(t));

            // obtain type constructor from type
              const cst c = constructor(r);

              try {
                // fetch associated signature, if any
                const auto sig = s->sigs->at(c);

                // instantiate signature
                return sub->instantiate(sig);

              } catch(std::out_of_range&) {
                throw error("unknown signature " + tool::quote(c->name.get()));
              }
          });

        const mono outer = s->fresh();
        const mono inner = sub->fresh();

        sub->unify(outer >>= inner, sig);
        sub->def(arg.name(), outer);

        // std::clog << "inner: " << sub->vars->find(arg.name()) << std::endl;

        return outer >>= tail;
      });

      // infer lambda body with augmented environment
      s->unify(result, infer(sub, *self.body));

      return res;
    }

  // app
  static mono infer(const ref<state>& s, const ast::app& self) {
    // normalize application as unary
    const ast::app rw = rewrite(self);
    assert(size(rw.args) == 1);

    // infer func/arg types for application
    const auto with_inferred = [&](auto cont) {
      const mono func = infer(s, *rw.func);
      const mono arg = infer(s, rw.args->head);

      return cont(func, arg);
    };

    // obtain inner type from a type with associated signature
    const auto inner_type = [&](mono t) {
      // obtain type constructor from argument type
      const cst c = constructor(s->substitute(t));

      try {
        auto sig = s->sigs->at(c);

        const mono inner = s->fresh();
        s->unify(t >>= inner, s->instantiate(sig));

        return inner;
      } catch(std::out_of_range&) {
        throw error("unknown signature");
      }
    };

    // check if application works with given func/arg type
    const auto check = [&](mono func, mono arg) {
      const mono ret = s->fresh();
      s->unify(func , arg >>= ret);
      return ret;
    };

    // TODO find a less stupid way of trying all cases?
    try {
      // normal case
      return with_inferred([&](mono func, mono arg) {
          return check(func, arg);
        });

    } catch(error& e) {

      try {
        // open func type and retry
        return with_inferred([&](mono func, mono arg) {
            const mono inner_func = inner_type(func);
            return check(inner_func, arg);
          });
      }
      catch(error& ) {  }

      try {
        // open arg type and retry
        return with_inferred([&](mono func, mono arg) {
            const mono inner_arg = inner_type(arg);
            return check(func, inner_arg);
          });
      }
      catch(error& ) {  }

      try {
        // open both func and arg types and retry
        return with_inferred([&](mono func, mono arg) {
            const mono inner_func = inner_type(func);
            const mono inner_arg = inner_type(arg);
            return check(inner_func, inner_arg);
          });
      }
      catch(error& ) {  }

      throw e;
    }

  }

  // non-recursive let
  static mono infer(const ref<state>& s, const let& self) {
    auto sub = scope(s);

    for(ast::bind def : self.defs) {
      sub->vars->locals.emplace(def.name, s->generalize(infer(s, def.value)));
    }

    return infer(sub, self.body);
  }

  // recursive let
  static mono infer(const ref<state>& s, const ast::let& self) {
    auto sub = scope(s);

    const mono a = sub->fresh();
    sub->def(let::fix(), (a >>= a) >>= a);

    return infer(sub, let::rewrite(self));
  }

  // sel
  static mono infer(const ref<state>& s, const ast::sel& self) {
    const mono tail = s->fresh(kind::row());
    const mono head = s->fresh(kind::term());

    const mono row = ext(self.name)(head)(tail);
    return record(row) >>= head;
  }

  // record
  static mono infer(const ref<state>& s, const ast::record& self) {
    const mono init = empty;
    const mono row = foldr(init, self.attrs, [&](ast::record::attr attr, mono tail) {
        return ext(attr.name)(infer(s, attr.value))(tail);
      });

    return record(row);
  }

  // make
  static mono infer(const ref<state>& s, const ast::make& self) {
    // get signature type
    const poly sig = s->vars->find(self.type);

    auto sub = scope(s);

    const mono outer = s->fresh();
    const mono inner = sub->fresh();

    // instantiate signature at sub level prevents generalization of
    // contravariant side (variables only appearing in the covariant side will
    // be generalized)
    s->unify(ty(outer) >>= ty(inner), sub->instantiate(sig));

    // vanilla covariant type
    const poly reference = sub->generalize(inner);

    // build provided type
    const mono init = empty;
    const mono provided =
      record(foldr(init, self.attrs,
                   [&](const ast::record::attr attr, mono tail) {
                     return row(attr.name, infer(sub, attr.value)) |= tail;
                   }));

    // now also unify inner with provided type
    s->unify(inner, provided);

    // now generalize the provided type
    const poly gen = sub->generalize(inner);

    // generalization check: quantified variables in reference/gen should
    // substitute to the same variables
    std::set<var> quantified;
    for(const var& v : gen.forall) {
      assert(sub->substitute(v) == v);
      quantified.insert(v);
    }

    // make sure all reference quantified references substitute to quantified
    // variables
    for(const var& v : reference.forall) {
      const mono vs = sub->substitute(v);
      if(auto u = vs.get<var>()) {
        auto it = quantified.find(*u);
        if(it != quantified.end()) {
          continue;
        }
      }

      std::stringstream ss;
      logger(ss) << "failed to generalize " << gen
                 << " as " << reference;

      throw error(ss.str());
    }

    return outer;
  }

  // cond
  static mono infer(const ref<state>& s, const ast::cond& self) {
    const mono test = infer(s, *self.test);
    s->unify(test, boolean);

    const mono conseq = infer(s, *self.conseq);
    const mono alt = infer(s, *self.alt);

    const mono result = s->fresh();
    s->unify(result, conseq);
    s->unify(result, alt);

    return result;
  }

  // def
  static mono infer(const ref<state>& s, const ast::def& self) {
    const mono value =
      infer(s, ast::let(ast::bind{self.name, *self.value} >>= list<ast::bind>(),
                        ast::var{self.name}));
    try {
      s->def(self.name, value);
      return io(unit);
    } catch(std::runtime_error& e) {
      throw error(e.what());
    }
  }

  // use
  static mono infer(const ref<state>& s, const ast::use& self) {
    // infer value type
    const mono value = infer(s, *self.env);

    // make sure value type is a record
    const mono row = s->fresh(kind::row());
    s->unify(value, record(row));

    auto sub = scope(s);

    // fill sub scope with record contents
    iter_rows(s->substitute(row), [&](symbol attr, mono t) {
        // TODO generalization issue?
        sub->def(attr, t);
      });

    return infer(sub, *self.body);
  }

  // import
  static mono infer(const ref<state>& s, const ast::import& self) {
    auto it = s->vars->locals.find(self.package);
    if(it != s->vars->locals.end()) {
      throw error("variable " + tool::quote(self.package.get()) + " already defined");
    }

    const package pkg = package::import(self.package);

    s->vars->locals.emplace(self.package, pkg.sig());
    return io(unit);
  }

  // lit
  static mono infer(const ref<state>&, const ast::lit<::boolean>& self) {
    return boolean;
  }

  static mono infer(const ref<state>&, const ast::lit<::integer>& self) {
    return integer;
  }

  static mono infer(const ref<state>&, const ast::lit<::real>& self) {
    return real;
  }

  struct infer_visitor {
    using type = mono;

    template<class T>
    mono operator()(const T& self, const ref<state>& s) const {
      return infer(s, self);
    }

  };

  mono infer(const ref<state>& s, const ast::expr& self) {
    return self.visit(infer_visitor(), s);
  }

}
<|endoftext|>


<commit_before>
#include <Beard/detail/gr_ceformat.hpp>
#include <Beard/tty/TerminalInfo.hpp>

#include <duct/EndianUtils.hpp>
#include <duct/IO/arithmetic.hpp>
#include <duct/IO/unicode.hpp>

#include <istream>

namespace Beard {
namespace tty {

// class TerminalInfo implementation

#define BEARD_SCOPE_CLASS tty::TerminalInfo

TerminalInfo::~TerminalInfo() noexcept = default;

TerminalInfo::TerminalInfo() noexcept
	: m_initialized(false)
	, m_names()
	, m_cap_flags()
	, m_cap_numbers()
	, m_cap_strings()
{}

TerminalInfo::TerminalInfo(TerminalInfo&&) noexcept = default;
TerminalInfo::TerminalInfo(TerminalInfo const&) = default;
TerminalInfo& TerminalInfo::operator=(TerminalInfo&&) noexcept = default;

// serialization

// TODO: <tic.h> specifies some differences:
// 1. max names field size is 512 (XSI);
// 2. there are two different "signed" values specifying
//	  different meanings for capabilities;

// terminfo format:
/*
	uint16_t magic = 0x011a

	uint16_t names_size
	uint16_t flag_count
	uint16_t number_count
	uint16_t string_offset_count
	uint16_t string_table_size

	// Names for terminal type, separated by '|'
	char names[names_size]

	// Boolean flags
	uint8_t flags[flag_count]

	// Seek ahead to align to 2-byte word (ergo: possible dead byte)

	uint16_t numbers[number_count]

	// Offsets are relative to string_table
	uint16_t string_offsets[string_offset_count]
	char string_table[string_table_size]
*/

namespace {
enum : unsigned {
	terminfo_magic = 0x011a,
	terminfo_max_names_size = 128u,

	terminfo_table_offset_empty = 0xFFFFu,
	mask_offset_signbit = 0x8000,
};

constexpr auto const
terminfo_endian = duct::Endian::LITTLE;

struct terminfo_header {
	uint16_t magic{0};
	uint16_t names_size{0};
	uint16_t flag_count{0};
	uint16_t number_count{0};
	uint16_t string_offset_count{0};
	uint16_t string_table_size{0};
};
} // anonymous namespace

#define BEARD_TERMINFO_CHECK_IO_ERROR_(m_)				\
	if (stream.fail()) {								\
		BEARD_THROW_FQN(								\
			ErrorCode::serialization_io_failed,			\
			m_											\
		);												\
	}
//

#define BEARD_SCOPE_FUNC deserialize
namespace {
BEARD_DEF_FMT_FQN(
	s_err_bad_magic,
	"bad magic encountered: expected %-#04x, got %-#04x"
);
BEARD_DEF_FMT_FQN(
	s_err_name_too_large,
	"names section too large: expected s <= %u, got s = %u"
);
BEARD_DEF_FMT_FQN(
	s_err_string_offset_invalid,
	"index %u offset %u overflows string table (size = %u)"
);
} // anonymous namespace

void
TerminalInfo::deserialize(
	std::istream& stream
) {
	m_initialized = false;
	m_names.clear();
	m_cap_flags.clear();
	m_cap_numbers.clear();
	m_cap_strings.clear();

	terminfo_header hdr{};

	// header
	duct::IO::read_arithmetic(stream, hdr.magic, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.names_size, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.flag_count, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.number_count, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.string_offset_count, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.string_table_size, terminfo_endian);

	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read header"
	);

	if (terminfo_magic != hdr.magic) {
		BEARD_THROW_FMT(
			ErrorCode::serialization_data_malformed,
			s_err_bad_magic,
			static_cast<unsigned>(terminfo_magic),
			static_cast<unsigned>(hdr.magic)
		);
	}

	if (terminfo_max_names_size < hdr.names_size) {
		BEARD_THROW_FMT(
			ErrorCode::serialization_data_malformed,
			s_err_name_too_large,
			static_cast<unsigned>(terminfo_max_names_size),
			static_cast<unsigned>(hdr.names_size)
		);
	}

	// names
	// Assuming ASCII encoding -- compatible with UTF-8, so no
	// decoding necessary.

	String names_glob{};
	duct::IO::read_string_copy(
		stream,
		names_glob,
		static_cast<std::size_t>(hdr.names_size),
		terminfo_endian
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read names field"
	);

	if (0u < names_glob.size() && '\0' == *names_glob.crbegin()) {
		names_glob.pop_back();
	}

	String::size_type pos = 0u, next = String::npos;
	for (;;) {
		next = names_glob.find('|', pos + 1u);
		if (String::npos == next) {
			m_names.emplace_back(names_glob.substr(pos, String::npos));
			break;
		} else if (next > (pos + 1u)) {
			m_names.emplace_back(names_glob.substr(pos, next - pos));
			pos = next + 1u;
		} else {
			// next <= pos (empty block)
			++pos;
			continue;
		}
	}

	// flags
	m_cap_flags.resize(static_cast<std::size_t>(hdr.flag_count));
	duct::IO::read_arithmetic_array(
		stream,
		m_cap_flags.data(),
		m_cap_flags.size(),
		terminfo_endian
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read flag section"
	);

	// align
	// names_size and flag_count will indicate unalignment if
	// their sum is uneven because their respective elements are
	// bytes.
	if ((hdr.names_size + hdr.flag_count) % 2) {
		char dead_byte;
		stream.read(&dead_byte, 1u);
		BEARD_TERMINFO_CHECK_IO_ERROR_(
			"failed to read dead byte for alignment"
		);
	}

	// numbers
	m_cap_numbers.resize(static_cast<std::size_t>(hdr.number_count));
	duct::IO::read_arithmetic_array(
		stream,
		m_cap_numbers.data(),
		m_cap_numbers.size(),
		terminfo_endian
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read number section"
	);

	// string offsets
	aux::vector<uint16_t> string_offsets(
		static_cast<std::size_t>(hdr.string_offset_count)
	);
	duct::IO::read_arithmetic_array(
		stream,
		string_offsets.data(),
		string_offsets.size(),
		terminfo_endian
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read string offsets section"
	);

	// string table
	// Again assuming ASCII.
	aux::vector<char> string_table(
		static_cast<std::size_t>(hdr.string_table_size)
	);
	duct::IO::read(
		stream,
		string_table.data(),
		string_table.size()
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read string table"
	);

	unsigned index = 0u, offset = 0u;
	auto
		it_start = string_table.cend(),
		it_cnull = it_start
	;
	for (
		auto it = string_offsets.cbegin();
		string_offsets.cend() != it;
		++it, ++index
	) {
		offset = static_cast<unsigned>(*it);

		// -1 means terminal does not support capability, and
		// "other negative values are illegal".
		// And in Unix fashion, /you will get illegal values/.
		if (
			terminfo_table_offset_empty == offset
		||  (mask_offset_signbit & offset)
		) {
			continue;
		}

		if (string_table.size() <= offset) {
			BEARD_THROW_FMT(
				ErrorCode::serialization_data_malformed,
				s_err_string_offset_invalid,
				index,
				offset,
				string_table.size()
			);
		}

		it_start = string_table.cbegin() + offset;
		it_cnull = std::find(
			it_start,
			string_table.cend(),
			'\0'
		);
		m_cap_strings.emplace(
			index,
			String{it_start, it_cnull}
		);
	}

	m_initialized = true;
}
#undef BEARD_SCOPE_FUNC

#undef BEARD_SCOPE_CLASS

} // namespace tty
} // namespace Beard
<commit_msg>Corrected references to duct::Endian members.¹<commit_after>
#include <Beard/detail/gr_ceformat.hpp>
#include <Beard/tty/TerminalInfo.hpp>

#include <duct/EndianUtils.hpp>
#include <duct/IO/arithmetic.hpp>
#include <duct/IO/unicode.hpp>

#include <istream>

namespace Beard {
namespace tty {

// class TerminalInfo implementation

#define BEARD_SCOPE_CLASS tty::TerminalInfo

TerminalInfo::~TerminalInfo() noexcept = default;

TerminalInfo::TerminalInfo() noexcept
	: m_initialized(false)
	, m_names()
	, m_cap_flags()
	, m_cap_numbers()
	, m_cap_strings()
{}

TerminalInfo::TerminalInfo(TerminalInfo&&) noexcept = default;
TerminalInfo::TerminalInfo(TerminalInfo const&) = default;
TerminalInfo& TerminalInfo::operator=(TerminalInfo&&) noexcept = default;

// serialization

// TODO: <tic.h> specifies some differences:
// 1. max names field size is 512 (XSI);
// 2. there are two different "signed" values specifying
//	  different meanings for capabilities;

// terminfo format:
/*
	uint16_t magic = 0x011a

	uint16_t names_size
	uint16_t flag_count
	uint16_t number_count
	uint16_t string_offset_count
	uint16_t string_table_size

	// Names for terminal type, separated by '|'
	char names[names_size]

	// Boolean flags
	uint8_t flags[flag_count]

	// Seek ahead to align to 2-byte word (ergo: possible dead byte)

	uint16_t numbers[number_count]

	// Offsets are relative to string_table
	uint16_t string_offsets[string_offset_count]
	char string_table[string_table_size]
*/

namespace {
enum : unsigned {
	terminfo_magic = 0x011a,
	terminfo_max_names_size = 128u,

	terminfo_table_offset_empty = 0xFFFFu,
	mask_offset_signbit = 0x8000,
};

constexpr auto const
terminfo_endian = duct::Endian::little;

struct terminfo_header {
	uint16_t magic{0};
	uint16_t names_size{0};
	uint16_t flag_count{0};
	uint16_t number_count{0};
	uint16_t string_offset_count{0};
	uint16_t string_table_size{0};
};
} // anonymous namespace

#define BEARD_TERMINFO_CHECK_IO_ERROR_(m_)				\
	if (stream.fail()) {								\
		BEARD_THROW_FQN(								\
			ErrorCode::serialization_io_failed,			\
			m_											\
		);												\
	}
//

#define BEARD_SCOPE_FUNC deserialize
namespace {
BEARD_DEF_FMT_FQN(
	s_err_bad_magic,
	"bad magic encountered: expected %-#04x, got %-#04x"
);
BEARD_DEF_FMT_FQN(
	s_err_name_too_large,
	"names section too large: expected s <= %u, got s = %u"
);
BEARD_DEF_FMT_FQN(
	s_err_string_offset_invalid,
	"index %u offset %u overflows string table (size = %u)"
);
} // anonymous namespace

void
TerminalInfo::deserialize(
	std::istream& stream
) {
	m_initialized = false;
	m_names.clear();
	m_cap_flags.clear();
	m_cap_numbers.clear();
	m_cap_strings.clear();

	terminfo_header hdr{};

	// header
	duct::IO::read_arithmetic(stream, hdr.magic, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.names_size, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.flag_count, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.number_count, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.string_offset_count, terminfo_endian);
	duct::IO::read_arithmetic(stream, hdr.string_table_size, terminfo_endian);

	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read header"
	);

	if (terminfo_magic != hdr.magic) {
		BEARD_THROW_FMT(
			ErrorCode::serialization_data_malformed,
			s_err_bad_magic,
			static_cast<unsigned>(terminfo_magic),
			static_cast<unsigned>(hdr.magic)
		);
	}

	if (terminfo_max_names_size < hdr.names_size) {
		BEARD_THROW_FMT(
			ErrorCode::serialization_data_malformed,
			s_err_name_too_large,
			static_cast<unsigned>(terminfo_max_names_size),
			static_cast<unsigned>(hdr.names_size)
		);
	}

	// names
	// Assuming ASCII encoding -- compatible with UTF-8, so no
	// decoding necessary.

	String names_glob{};
	duct::IO::read_string_copy(
		stream,
		names_glob,
		static_cast<std::size_t>(hdr.names_size),
		terminfo_endian
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read names field"
	);

	if (0u < names_glob.size() && '\0' == *names_glob.crbegin()) {
		names_glob.pop_back();
	}

	String::size_type pos = 0u, next = String::npos;
	for (;;) {
		next = names_glob.find('|', pos + 1u);
		if (String::npos == next) {
			m_names.emplace_back(names_glob.substr(pos, String::npos));
			break;
		} else if (next > (pos + 1u)) {
			m_names.emplace_back(names_glob.substr(pos, next - pos));
			pos = next + 1u;
		} else {
			// next <= pos (empty block)
			++pos;
			continue;
		}
	}

	// flags
	m_cap_flags.resize(static_cast<std::size_t>(hdr.flag_count));
	duct::IO::read_arithmetic_array(
		stream,
		m_cap_flags.data(),
		m_cap_flags.size(),
		terminfo_endian
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read flag section"
	);

	// align
	// names_size and flag_count will indicate unalignment if
	// their sum is uneven because their respective elements are
	// bytes.
	if ((hdr.names_size + hdr.flag_count) % 2) {
		char dead_byte;
		stream.read(&dead_byte, 1u);
		BEARD_TERMINFO_CHECK_IO_ERROR_(
			"failed to read dead byte for alignment"
		);
	}

	// numbers
	m_cap_numbers.resize(static_cast<std::size_t>(hdr.number_count));
	duct::IO::read_arithmetic_array(
		stream,
		m_cap_numbers.data(),
		m_cap_numbers.size(),
		terminfo_endian
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read number section"
	);

	// string offsets
	aux::vector<uint16_t> string_offsets(
		static_cast<std::size_t>(hdr.string_offset_count)
	);
	duct::IO::read_arithmetic_array(
		stream,
		string_offsets.data(),
		string_offsets.size(),
		terminfo_endian
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read string offsets section"
	);

	// string table
	// Again assuming ASCII.
	aux::vector<char> string_table(
		static_cast<std::size_t>(hdr.string_table_size)
	);
	duct::IO::read(
		stream,
		string_table.data(),
		string_table.size()
	);
	BEARD_TERMINFO_CHECK_IO_ERROR_(
		"failed to read string table"
	);

	unsigned index = 0u, offset = 0u;
	auto
		it_start = string_table.cend(),
		it_cnull = it_start
	;
	for (
		auto it = string_offsets.cbegin();
		string_offsets.cend() != it;
		++it, ++index
	) {
		offset = static_cast<unsigned>(*it);

		// -1 means terminal does not support capability, and
		// "other negative values are illegal".
		// And in Unix fashion, /you will get illegal values/.
		if (
			terminfo_table_offset_empty == offset
		||  (mask_offset_signbit & offset)
		) {
			continue;
		}

		if (string_table.size() <= offset) {
			BEARD_THROW_FMT(
				ErrorCode::serialization_data_malformed,
				s_err_string_offset_invalid,
				index,
				offset,
				string_table.size()
			);
		}

		it_start = string_table.cbegin() + offset;
		it_cnull = std::find(
			it_start,
			string_table.cend(),
			'\0'
		);
		m_cap_strings.emplace(
			index,
			String{it_start, it_cnull}
		);
	}

	m_initialized = true;
}
#undef BEARD_SCOPE_FUNC

#undef BEARD_SCOPE_CLASS

} // namespace tty
} // namespace Beard
<|endoftext|>


<commit_before><commit_msg>coverity#1079279 Uninitialized scalar field<commit_after><|endoftext|>


<commit_before><commit_msg>Send the V8 histograms to UMA<commit_after><|endoftext|>


<commit_before>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkSurfaceToImageFilter.h"
#include "mitkDataTreeNodeFactory.h"
#include "mitkPicFileWriter.h"

#include <vtkPolyData.h>

#include <fstream>

int mitkSurfaceToImageFilterTest(int argc, char* argv[])
{
  mitk::SurfaceToImageFilter::Pointer s2iFilter;
  std::cout << "Testing mitk::Surface::New(): " << std::flush;
  s2iFilter = mitk::SurfaceToImageFilter::New();
  if (s2iFilter.IsNull())
  {
    std::cout<<"[FAILED]"<<std::endl;
    return EXIT_FAILURE;
  }
  else
  {
    std::cout<<"[PASSED]"<<std::endl;
  }

  std::cout << "Loading file: " << std::flush;
  if(argc==0)
  {
    std::cout<<"no file specified [FAILED]"<<std::endl;
    return EXIT_FAILURE;
  }
  mitk::Surface::Pointer surface = NULL;
  mitk::DataTreeNodeFactory::Pointer factory = mitk::DataTreeNodeFactory::New();
  try
  {
    std::cout<<argv[1]<<std::endl;
    factory->SetFileName( argv[1] );
    factory->Update();

    if(factory->GetNumberOfOutputs()<1)
    {
      std::cout<<"file could not be loaded [FAILED]"<<std::endl;
      return EXIT_FAILURE;
    }
    mitk::DataTreeNode::Pointer node = factory->GetOutput( 0 );
    surface = dynamic_cast<mitk::Surface*>(node->GetData());
    if(surface.IsNull())
    {
      std::cout<<"file not a surface - test will not be applied [PASSED]"<<std::endl;
      std::cout<<"[TEST DONE]"<<std::endl;
      return EXIT_SUCCESS;
    }
  }
  catch ( itk::ExceptionObject & ex )
  {
    std::cout << "Exception: " << ex << "[FAILED]" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "Testing number of points of surface: " << std::flush;
  if(surface->GetVtkPolyData()->GetNumberOfPoints() == 0)
  {
    std::cout<<"number of points is 0 - test will not be applied [PASSED]"<<std::endl;
    std::cout<<"[TEST DONE]"<<std::endl;
    return EXIT_SUCCESS;
  }

  std::cout << "Testing creation of mitk::Image with same Geometry as Surface: " << std::flush;
  mitk::Image::Pointer image = mitk::Image::New();
  surface->UpdateOutputInformation(); //should not be necessary, bug #1536
  image->Initialize(typeid(unsigned char), *surface->GetGeometry());

  std::cout << "Testing mitk::SurfaceToImageFilter::MakeOutputBinaryOn(): " << std::flush;
  s2iFilter->MakeOutputBinaryOn();
  std::cout<<"[PASSED]"<<std::endl;

  std::cout << "Testing mitk::SurfaceToImageFilter::SetInput(): " << std::flush;
  s2iFilter->SetInput(surface);
  std::cout<<"[PASSED]"<<std::endl;

  std::cout << "Testing mitk::SurfaceToImageFilter::SetImage(): " << std::flush;
  s2iFilter->SetImage(image);
  std::cout<<"[PASSED]"<<std::endl;

  std::cout << "Testing mitk::SurfaceToImageFilter::Update(): " << std::flush;
  s2iFilter->Update();
  std::cout<<"[PASSED]"<<std::endl;

  //mitk::PicFileWriter::Pointer picWriter = mitk::PicFileWriter::New();
  //picWriter->SetInput(s2iFilter->GetOutput());
  //picWriter->SetFileName("SurfaceToImageFilterTestOutput.pic");
  //picWriter->Write();

  std::cout<<"[TEST DONE]"<<std::endl;
  return EXIT_SUCCESS;
}
<commit_msg>FIX (#1536): BoundingBox of Surface objects sometimes not initialized<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkSurfaceToImageFilter.h"
#include "mitkDataTreeNodeFactory.h"
#include "mitkPicFileWriter.h"

#include <vtkPolyData.h>

#include <fstream>

int mitkSurfaceToImageFilterTest(int argc, char* argv[])
{
  mitk::SurfaceToImageFilter::Pointer s2iFilter;
  std::cout << "Testing mitk::Surface::New(): " << std::flush;
  s2iFilter = mitk::SurfaceToImageFilter::New();
  if (s2iFilter.IsNull())
  {
    std::cout<<"[FAILED]"<<std::endl;
    return EXIT_FAILURE;
  }
  else
  {
    std::cout<<"[PASSED]"<<std::endl;
  }

  std::cout << "Loading file: " << std::flush;
  if(argc==0)
  {
    std::cout<<"no file specified [FAILED]"<<std::endl;
    return EXIT_FAILURE;
  }
  mitk::Surface::Pointer surface = NULL;
  mitk::DataTreeNodeFactory::Pointer factory = mitk::DataTreeNodeFactory::New();
  try
  {
    std::cout<<argv[1]<<std::endl;
    factory->SetFileName( argv[1] );
    factory->Update();

    if(factory->GetNumberOfOutputs()<1)
    {
      std::cout<<"file could not be loaded [FAILED]"<<std::endl;
      return EXIT_FAILURE;
    }
    mitk::DataTreeNode::Pointer node = factory->GetOutput( 0 );
    surface = dynamic_cast<mitk::Surface*>(node->GetData());
    if(surface.IsNull())
    {
      std::cout<<"file not a surface - test will not be applied [PASSED]"<<std::endl;
      std::cout<<"[TEST DONE]"<<std::endl;
      return EXIT_SUCCESS;
    }
  }
  catch ( itk::ExceptionObject & ex )
  {
    std::cout << "Exception: " << ex << "[FAILED]" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "Testing number of points of surface: " << std::flush;
  if(surface->GetVtkPolyData()->GetNumberOfPoints() == 0)
  {
    std::cout<<"number of points is 0 - test will not be applied [PASSED]"<<std::endl;
    std::cout<<"[TEST DONE]"<<std::endl;
    return EXIT_SUCCESS;
  }

  std::cout << "Testing creation of mitk::Image with same Geometry as Surface: " << std::flush;
  mitk::Image::Pointer image = mitk::Image::New();
  //surface->UpdateOutputInformation(); //is not necessary anymore (bug #1536), should never be neccessary
  image->Initialize(typeid(unsigned char), *surface->GetGeometry());

  std::cout << "Testing mitk::SurfaceToImageFilter::MakeOutputBinaryOn(): " << std::flush;
  s2iFilter->MakeOutputBinaryOn();
  std::cout<<"[PASSED]"<<std::endl;

  std::cout << "Testing mitk::SurfaceToImageFilter::SetInput(): " << std::flush;
  s2iFilter->SetInput(surface);
  std::cout<<"[PASSED]"<<std::endl;

  std::cout << "Testing mitk::SurfaceToImageFilter::SetImage(): " << std::flush;
  s2iFilter->SetImage(image);
  std::cout<<"[PASSED]"<<std::endl;

  std::cout << "Testing mitk::SurfaceToImageFilter::Update(): " << std::flush;
  s2iFilter->Update();
  std::cout<<"[PASSED]"<<std::endl;

  //mitk::PicFileWriter::Pointer picWriter = mitk::PicFileWriter::New();
  //picWriter->SetInput(s2iFilter->GetOutput());
  //picWriter->SetFileName("SurfaceToImageFilterTestOutput.pic");
  //picWriter->Write();

  std::cout<<"[TEST DONE]"<<std::endl;
  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/client/terminal_state.h>
#include <move_base_msgs/MoveBaseAction.h>
#include <move_basic/queued_action_server.h>
#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>

class GoalQueueSuite : public ::testing::Test {
protected:
	virtual void SetUp() {
		resetFlags();
		ros::NodeHandle actionNh("");
		qserv.reset(new actionlib::QueuedActionServer<move_base_msgs::MoveBaseAction> (actionNh, "queue_server", boost::bind(&GoalQueueSuite::executeCallback, this, _1)));
		cli.reset(new actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> ("queue_server", true)); // true -> don't need ros::spin()

		qserv->start();
	}

	virtual void TearDown() {
		// Kill the executeCallback if it is running
		// New Scope so that lock_guard destructs and unlocks
		{
			std::lock_guard<std::mutex> lk1(execute_lock);
			finish_executing = true;
			execute_cv.notify_one(); // Releases one waiting thread
		}
		qserv->shutdown();
	}

	// TODO: Making sure it gets called at the right time (flags!) - in its own thread
	void executeCallback(const move_base_msgs::MoveBaseGoalConstPtr &msg) {
		resetFlags();
		got_goal = true;
		received_goal = msg;
		while (true) {
			if (qserv->isPreemptRequested()) {
				goal_preempted = true;
			} else {
				goal_preempted = false;
			}
			if (qserv->isNewGoalAvailable()) {
				next_goal_available = true;
			} else {
				next_goal_available = false;
			}

			// Wait until signalled to end
			std::unique_lock<std::mutex> lk(execute_lock);
			execute_cv.wait(lk,
				//lambda function to wait on our bool variables
				[this](){return finish_executing || resume_executing;} // blocks only if lambda returns false
			);
			// We were requested to stop, so we stop
			if (finish_executing) {
				break;
			}
		}

		// Signal that we are done here
		std::lock_guard<std::mutex> lk(execute_done_lock);
		execute_done = true;
		execute_done_cv.notify_all(); // Releases all waiting thread
	}

	// Helper function to signal the executeCallback to end
	void finishExecuting() {
		// New Scope so that lock_guard destructs and unlocks
		{
			std::lock_guard<std::mutex> lk1(execute_lock);
			finish_executing = true;
			execute_cv.notify_one(); //unlocks lambda waiting function in ExecuteCallback
		}

		// Wait for execute callback to actually finish
		std::unique_lock<std::mutex> lk2(execute_done_lock);
		execute_done_cv.wait(lk2, [this]() {return execute_done;}); // at the end of ExecuteCallback
	}

	// Helper function to signal the executeCallback to resume
	// This is useful for seeing if the callback code sees a preempt
	void resumeExecuting() {
		std::lock_guard<std::mutex> lk(execute_lock);
		resume_executing = true;
		execute_cv.notify_one();
	}

	void resetFlags() {
		got_goal = false;
		goal_preempted = false;
		next_goal_available = false;
		received_goal = nullptr;

		// Signal flags
		finish_executing = false;
		resume_executing = false;
		execute_done = false;
	}

	// Flags for assertions
	bool got_goal;
	bool goal_preempted;
	bool next_goal_available;
	move_base_msgs::MoveBaseGoalConstPtr received_goal;

	// Signaling variables
	bool finish_executing;
	bool resume_executing;
	bool execute_done;
	std::mutex execute_lock;
	std::mutex execute_done_lock;
	std::condition_variable execute_cv;
	std::condition_variable execute_done_cv;

	std::unique_ptr<actionlib::QueuedActionServer<move_base_msgs::MoveBaseAction>> qserv;
	std::unique_ptr<actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction>> cli;
};

// IMPORTANT: Time delays and ros::spinOnce() sequence should stay the same, otherwise, strange things happen
TEST_F(GoalQueueSuite, establishDuplex) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	finishExecuting();
	ASSERT_TRUE(got_goal);
	ASSERT_FALSE(goal_preempted);
	ASSERT_FALSE(next_goal_available);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);
}

TEST_F(GoalQueueSuite, addGoalWhileExecuting) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Cancelling the first goal - PITFALL
	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	// Finish the preempted goal
	finishExecuting();

	// "Second" goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x);

	// Third goal
	goal.target_pose.pose.position.x = 13.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	resumeExecuting();
	// Make sure that the "second" goal is still executing, but a new goal is seen
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x);
	ASSERT_FALSE(goal_preempted);
	//ASSERT_TRUE(next_goal_available); // TODO: Fails when changing other code???
	// Finish the "second" goal, then the 3nd goal should start executing
	finishExecuting();
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_FALSE(goal_preempted);
	ASSERT_FALSE(next_goal_available);
	ASSERT_EQ(13.0, received_goal->target_pose.pose.position.x);
	finishExecuting(); // Finish the 3nd goal
/*
	- if another goal is received add it to the queue (DONE)
	- if the queue full, set the current goal as preempted - explicitly called! - so cancelling the current goal and start executing the next one
	- start executing the next goal in queue (the one after the preempted)
*/
}

TEST_F(GoalQueueSuite, goalPreempting) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// One goal -> Cancel request -> Stop
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(1.0).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(got_goal);
	ASSERT_TRUE(qserv->isActive());
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	finishExecuting(); // Finish the goal
	ros::spinOnce();
	ros::Duration(1.0).sleep();
	ASSERT_FALSE(qserv->isActive());

	// Two goals -> Cancel current goal -> Start executing the second
	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Cancelling the first goal - PITFALL
	resumeExecuting();
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	// Finish the preempted goal
	finishExecuting(); // Finish 1st goal

	// "Second" goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x); // call FINISH!
	finishExecuting(); // Finish 2nd goal

//	- if a cancel request is received for the current goal, set it as preempted (DONE)
//	- if there another goal, start executing it
//	- if no goal, stop (DONE)
}

TEST_F(GoalQueueSuite, goalCancelling) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// Two goals -> Cancel current goal -> Start executing the second
	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Second goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ros::spinOnce();
	// Cancelling the second goal
	cli->cancelGoal();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	//ASSERT_TRUE(goal_preempted); // TODO: Why is this failling?

	resumeExecuting();
	ASSERT_TRUE(qserv->isActive());
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);
	finishExecuting();
	//ASSERT_FALSE(qserv->isActive()); // TODO: Why is this failling?

//	- if a cancel request on the "next_goal" received, remove it from the queue and set it as cancelled
}
// Two more TEST_F missing and a pitfall

int main(int argc, char **argv) {
    ros::init(argc, argv, "goal_queueing_test");
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<commit_msg>Chronologic ExecuteCallback looping.<commit_after>#include <gtest/gtest.h>
#include <ros/ros.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/client/terminal_state.h>
#include <move_base_msgs/MoveBaseAction.h>
#include <move_basic/queued_action_server.h>
#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>

class GoalQueueSuite : public ::testing::Test {
protected:
	virtual void SetUp() {
		resetFlags();
		ros::NodeHandle actionNh("");
		qserv.reset(new actionlib::QueuedActionServer<move_base_msgs::MoveBaseAction> (actionNh, "queue_server", boost::bind(&GoalQueueSuite::executeCallback, this, _1)));
		cli.reset(new actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> ("queue_server", true)); // true -> don't need ros::spin()

		qserv->start();
	}

	virtual void TearDown() {
		// Kill the executeCallback if it is running
		// New Scope so that lock_guard destructs and unlocks
		{
			std::lock_guard<std::mutex> lk1(execute_lock);
			finish_executing = true;
			execute_cv.notify_one(); // Releases one waiting thread
		}
		qserv->shutdown();
	}

	// TODO: Making sure it gets called at the right time (flags!) - in its own thread
	void executeCallback(const move_base_msgs::MoveBaseGoalConstPtr &msg) {
		resetFlags();
		got_goal = true;
		received_goal = msg;
		while (true) {
			if (qserv->isPreemptRequested()) {
				goal_preempted = true;
			} else {
				goal_preempted = false;
			}
			if (qserv->isNewGoalAvailable()) {
				next_goal_available = true;
			} else {
				next_goal_available = false;
			}

			// Wait until signalled to end
			std::unique_lock<std::mutex> lk(execute_lock);
			execute_cv.wait(lk,
				//lambda function to wait on our bool variables
				[this](){return finish_executing || resume_executing;} // blocks only if lambda returns false
			);
			// We were requested to stop, so we stop
			if (finish_executing) {
				break;
			}
		}

		// Signal that we are done here
		std::lock_guard<std::mutex> lk(execute_done_lock);
		execute_done = true;
		execute_done_cv.notify_all(); // Releases all waiting thread
	}

	// Helper function to signal the executeCallback to end
	void finishExecuting() {
		// New Scope so that lock_guard destructs and unlocks
		{
			std::lock_guard<std::mutex> lk1(execute_lock);
			finish_executing = true;
			execute_cv.notify_one(); //unlocks lambda waiting function in ExecuteCallback
		}

		// Wait for execute callback to actually finish
		std::unique_lock<std::mutex> lk2(execute_done_lock);
		execute_done_cv.wait(lk2, [this]() {return execute_done;}); // at the end of ExecuteCallback
	}

	// Helper function to signal the executeCallback to resume
	// This is useful for seeing if the callback code sees a preempt
	void resumeExecuting() {
		std::lock_guard<std::mutex> lk(execute_lock);
		resume_executing = true;
		execute_cv.notify_one();
	}

	void resetFlags() {
		got_goal = false;
		goal_preempted = false;
		next_goal_available = false;
		received_goal = nullptr;

		// Signal flags
		finish_executing = false;
		resume_executing = false;
		execute_done = false;
	}

	// Flags for assertions
	bool got_goal;
	bool goal_preempted;
	bool next_goal_available;
	move_base_msgs::MoveBaseGoalConstPtr received_goal;

	// Signaling variables
	bool finish_executing;
	bool resume_executing;
	bool execute_done;
	std::mutex execute_lock;
	std::mutex execute_done_lock;
	std::condition_variable execute_cv;
	std::condition_variable execute_done_cv;

	std::unique_ptr<actionlib::QueuedActionServer<move_base_msgs::MoveBaseAction>> qserv;
	std::unique_ptr<actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction>> cli;
};

// IMPORTANT: Time delays and ros::spinOnce() sequence should stay the same, otherwise, strange things happen
TEST_F(GoalQueueSuite, establishDuplex) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	finishExecuting();
	ASSERT_TRUE(got_goal);
	ASSERT_FALSE(goal_preempted);
	ASSERT_FALSE(next_goal_available);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);
}

TEST_F(GoalQueueSuite, addGoalWhileExecuting) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Cancelling the first goal - PITFALL
	cli->cancelGoal();
	resumeExecuting();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	// Finish the preempted goal
	finishExecuting();

	// "Second" goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x);

	// Third goal
	goal.target_pose.pose.position.x = 13.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	resumeExecuting();
	// Make sure that the "second" goal is still executing, but a new goal is seen
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x);
	ASSERT_FALSE(goal_preempted);
	//ASSERT_TRUE(next_goal_available); // TODO: Fails when changing other code???
	// Finish the "second" goal, then the 3nd goal should start executing
	finishExecuting();
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_FALSE(goal_preempted);
	ASSERT_FALSE(next_goal_available);
	ASSERT_EQ(13.0, received_goal->target_pose.pose.position.x);
	finishExecuting(); // Finish the 3nd goal
/*
	- if another goal is received add it to the queue (DONE)
	- if the queue full, set the current goal as preempted - explicitly called! - so cancelling the current goal and start executing the next one
	- start executing the next goal in queue (the one after the preempted)
*/
}

TEST_F(GoalQueueSuite, goalPreempting) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// One goal -> Cancel request -> Stop
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(1.0).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(got_goal);
	ASSERT_TRUE(qserv->isActive());
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	cli->cancelGoal();
	resumeExecuting();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	finishExecuting(); // Finish the goal
	ros::spinOnce();
	ros::Duration(1.0).sleep();
	ASSERT_FALSE(qserv->isActive());

	// Two goals -> Cancel current goal -> Start executing the second
	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Cancelling the first goal - PITFALL
	cli->cancelGoal();
	resumeExecuting();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	ASSERT_TRUE(goal_preempted);
	// Finish the preempted goal
	finishExecuting(); // Finish 1st goal

	// "Second" goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(7.0, received_goal->target_pose.pose.position.x); // call FINISH!
	finishExecuting(); // Finish 2nd goal

//	- if a cancel request is received for the current goal, set it as preempted (DONE)
//	- if there another goal, start executing it
//	- if no goal, stop (DONE)
}

TEST_F(GoalQueueSuite, goalCancelling) {
	move_base_msgs::MoveBaseGoal goal;
	goal.target_pose.pose.position.x = 3.0;

	// Two goals -> Cancel current goal -> Start executing the second
	// First goal
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ASSERT_TRUE(qserv->isActive());
	ASSERT_TRUE(got_goal);
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);

	// Second goal
	goal.target_pose.pose.position.x = 7.0;
	cli->sendGoal(goal);
	ros::spinOnce();
	ros::Duration(0.5).sleep(); // TODO: Make this disappear
	ros::spinOnce();
	// Cancelling the second goal
	cli->cancelGoal();
	resumeExecuting();
	ros::Duration(1.0).sleep();
	ros::spinOnce();
	//ASSERT_TRUE(goal_preempted); // TODO: Why is this failling?
	ASSERT_TRUE(qserv->isActive());
	ASSERT_EQ(3.0, received_goal->target_pose.pose.position.x);
	finishExecuting();
	//ASSERT_FALSE(qserv->isActive()); // TODO: Why is this failling?

//	- if a cancel request on the "next_goal" received, remove it from the queue and set it as cancelled
}
// Two more TEST_F missing and a pitfall

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