<commit_before>#include "catch.hpp"

/*
TEST_CASE("matrix_io/read_matrix | matrix_io/write_matrix")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/read_dense_float64_bin | matrix_io/write_dense_float64_bin")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/read_dense_float64_csv | matrix_io/write_dense_float64_csv")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/read_sparse_float64_bin | matrix_io/write_sparse_float64_bin")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/read_sparse_float64_mtx | matrix_io/write_sparse_float64_mtx")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/read_sparse_binary_bin | matrix_io/write_sparse_binary_bin")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_dense_float64_bin | matrix_io/eigen::write_dense_float64_bin")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_dense_float64_csv | matrix_io/eigen::write_dense_float64_csv")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_sparse_float64_bin | matrix_io/eigen::write_sparse_float64_bin")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_sparse_float64_mtx | matrix_io/eigen::write_sparse_float64_mtx")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_sparse_binary_bin | matrix_io/eigen::write_sparse_binary_bin")
{
   REQUIRE(false);
}
*/<commit_msg>Add tests to TestsMatrixIO.cpp<commit_after>#include "catch.hpp"

#include "matrix_io.h"
#include "MatrixUtils.h"

#include <sstream>
#include <Eigen/Core>
#include <Eigen/SparseCore>

using namespace smurff;

TEST_CASE("matrix_io/read_matrix | matrix_io/write_matrix | .ddm")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/read_matrix | matrix_io/write_matrix | .csv")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/read_matrix | matrix_io/write_matrix | .sdm")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/read_matrix | matrix_io/write_matrix | .mtx")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/read_matrix | matrix_io/write_matrix | .sbm")
{
   REQUIRE(false);
}

// ===

TEST_CASE("matrix_io/read_dense_float64_bin | matrix_io/write_dense_float64_bin")
{
   std::uint64_t matrixConfigNRow = 3;
   std::uint64_t matrixConfigNCol = 3;
   std::vector<double> matrixConfigValues = { 1, 4, 7, 2, 5, 8, 3, 6, 9 };
   MatrixConfig matrixConfig( matrixConfigNRow
                            , matrixConfigNCol
                            , std::move(matrixConfigValues)
                            , NoiseConfig()
                            );

   std::stringstream matrixStream;
   matrix_io::write_dense_float64_bin(matrixStream, matrixConfig);
   MatrixConfig actualMatrixConfig = matrix_io::read_dense_float64_bin(matrixStream);
   Eigen::MatrixXd actualMatrix = dense_to_eigen(actualMatrixConfig);

   Eigen::MatrixXd expectedMatrix(3, 3);
   expectedMatrix << 1, 2, 3, 4, 5, 6, 7, 8, 9;

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}

TEST_CASE("matrix_io/read_dense_float64_csv | matrix_io/write_dense_float64_csv")
{
   std::uint64_t matrixConfigNRow = 3;
   std::uint64_t matrixConfigNCol = 3;
   std::vector<double> matrixConfigValues = { 1, 4, 7, 2, 5, 8, 3, 6, 9 };
   MatrixConfig matrixConfig( matrixConfigNRow
                            , matrixConfigNCol
                            , std::move(matrixConfigValues)
                            , NoiseConfig()
                            );

   std::stringstream matrixConfigStream;
   matrix_io::write_dense_float64_csv(matrixConfigStream, matrixConfig);
   MatrixConfig actualMatrixConfig = matrix_io::read_dense_float64_csv(matrixConfigStream);
   Eigen::MatrixXd actualMatrix = dense_to_eigen(actualMatrixConfig);

   Eigen::MatrixXd expectedMatrix(3, 3);
   expectedMatrix << 1, 2, 3, 4, 5, 6, 7, 8, 9;

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}

TEST_CASE("matrix_io/read_sparse_float64_bin | matrix_io/write_sparse_float64_bin")
{
   std::uint64_t matrixConfigNRow = 3;
   std::uint64_t matrixConfigNCol = 3;
   std::vector<std::uint32_t> matrixConfigRows = { 0, 0, 0, 2, 2, 2 };
   std::vector<std::uint32_t> matrixConfigCols = { 0, 1, 2, 0, 1, 2 };
   std::vector<double> matrixConfigValues      = { 1, 2, 3, 7, 8, 9 };
   MatrixConfig matrixConfig( matrixConfigNRow
                            , matrixConfigNCol
                            , std::move(matrixConfigRows)
                            , std::move(matrixConfigCols)
                            , std::move(matrixConfigValues)
                            , NoiseConfig()
                            );

   std::stringstream matrixConfigStream;
   matrix_io::write_sparse_float64_bin(matrixConfigStream, matrixConfig);
   MatrixConfig actualMatrixConfig = matrix_io::read_sparse_float64_bin(matrixConfigStream);
   Eigen::SparseMatrix<double> actualMatrix = sparse_to_eigen(actualMatrixConfig);

   Eigen::SparseMatrix<double> expectedMatrix(3, 3);
   std::vector<Eigen::Triplet<double> > expectedMatrixTriplets;
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 0, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 1, 2));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 2, 3));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 0, 7));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 1, 8));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 2, 9));
   expectedMatrix.setFromTriplets(expectedMatrixTriplets.begin(), expectedMatrixTriplets.end());

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}

TEST_CASE("matrix_io/read_sparse_float64_mtx | matrix_io/write_sparse_float64_mtx")
{
   std::uint64_t matrixConfigNRow = 3;
   std::uint64_t matrixConfigNCol = 3;
   std::vector<std::uint32_t> matrixConfigRows = { 0, 0, 0, 2, 2, 2 };
   std::vector<std::uint32_t> matrixConfigCols = { 0, 1, 2, 0, 1, 2 };
   std::vector<double> matrixConfigValues      = { 1, 2, 3, 7, 8, 9 };
   MatrixConfig matrixConfig( matrixConfigNRow
                            , matrixConfigNCol
                            , std::move(matrixConfigRows)
                            , std::move(matrixConfigCols)
                            , std::move(matrixConfigValues)
                            , NoiseConfig()
                            );

   std::stringstream matrixConfigStream;
   matrix_io::write_sparse_float64_mtx(matrixConfigStream, matrixConfig);
   MatrixConfig actualMatrixConfig = matrix_io::read_sparse_float64_mtx(matrixConfigStream);
   Eigen::SparseMatrix<double> actualMatrix = sparse_to_eigen(actualMatrixConfig);

   Eigen::SparseMatrix<double> expectedMatrix(3, 3);
   std::vector<Eigen::Triplet<double> > expectedMatrixTriplets;
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 0, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 1, 2));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 2, 3));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 0, 7));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 1, 8));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 2, 9));
   expectedMatrix.setFromTriplets(expectedMatrixTriplets.begin(), expectedMatrixTriplets.end());

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}

TEST_CASE("matrix_io/read_sparse_binary_bin | matrix_io/write_sparse_binary_bin")
{
   std::uint64_t matrixConfigNRow = 3;
   std::uint64_t matrixConfigNCol = 3;
   std::vector<std::uint32_t> matrixConfigRows = { 0, 0, 0, 2, 2, 2 };
   std::vector<std::uint32_t> matrixConfigCols = { 0, 1, 2, 0, 1, 2 };
   MatrixConfig matrixConfig( matrixConfigNRow
                            , matrixConfigNCol
                            , std::move(matrixConfigRows)
                            , std::move(matrixConfigCols)
                            , NoiseConfig()
                            );

   std::stringstream matrixConfigStream;
   matrix_io::write_sparse_binary_bin(matrixConfigStream, matrixConfig);
   MatrixConfig actualMatrixConfig = matrix_io::read_sparse_binary_bin(matrixConfigStream);
   Eigen::SparseMatrix<double> actualMatrix = sparse_to_eigen(actualMatrixConfig);

   Eigen::SparseMatrix<double> expectedMatrix(3, 3);
   std::vector<Eigen::Triplet<double> > expectedMatrixTriplets;
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 0, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 1, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 2, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 0, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 1, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 2, 1));
   expectedMatrix.setFromTriplets(expectedMatrixTriplets.begin(), expectedMatrixTriplets.end());

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}

// ===

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::VectorXd& V) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .ddm")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::VectorXd& V) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .csv")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::VectorXd& V) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .sdm")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::VectorXd& V) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .mtx")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::VectorXd& V) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .sbm")
{
   REQUIRE(false);
}

// ===

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::MatrixXd& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .ddm")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::MatrixXd& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .csv")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::MatrixXd& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .sdm")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::MatrixXd& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .mtx")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::MatrixXd& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::MatrixXd& X) | .sbm")
{
   REQUIRE(false);
}

// ===

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::SparseMatrix<double>& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::SparseMatrix<double>& X) | .ddm")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::SparseMatrix<double>& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::SparseMatrix<double>& X) | .csv")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::SparseMatrix<double>& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::SparseMatrix<double>& X) | .sdm")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::SparseMatrix<double>& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::SparseMatrix<double>& X) | .mtx")
{
   REQUIRE(false);
}

TEST_CASE("matrix_io/eigen::read_matrix(const std::string& filename, Eigen::SparseMatrix<double>& X) | matrix_io/eigen::write_matrix(const std::string& filename, const Eigen::SparseMatrix<double>& X) | .sbm")
{
   REQUIRE(false);
}

// ===

TEST_CASE("matrix_io/eigen::read_dense_float64_bin | matrix_io/eigen::write_dense_float64_bin")
{
   Eigen::MatrixXd expectedMatrix(3, 3);
   expectedMatrix << 1, 2, 3, 4, 5, 6, 7, 8, 9;

   std::stringstream matrixStream;
   matrix_io::eigen::write_dense_float64_bin(matrixStream, expectedMatrix);
   Eigen::MatrixXd actualMatrix(3, 3);
   matrix_io::eigen::read_dense_float64_bin(matrixStream, actualMatrix);

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}

TEST_CASE("matrix_io/eigen::read_dense_float64_csv | matrix_io/eigen::write_dense_float64_csv")
{
   Eigen::MatrixXd expectedMatrix(3, 3);
   expectedMatrix << 1, 2, 3, 4, 5, 6, 7, 8, 9;

   std::stringstream matrixStream;
   matrix_io::eigen::write_dense_float64_csv(matrixStream, expectedMatrix);
   Eigen::MatrixXd actualMatrix(3, 3);
   matrix_io::eigen::read_dense_float64_csv(matrixStream, actualMatrix);

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}

TEST_CASE("matrix_io/eigen::read_sparse_float64_bin | matrix_io/eigen::write_sparse_float64_bin")
{
   Eigen::SparseMatrix<double> expectedMatrix(3, 3);
   std::vector<Eigen::Triplet<double> > expectedMatrixTriplets;
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 0, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 1, 2));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 2, 3));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 0, 7));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 1, 8));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 2, 9));
   expectedMatrix.setFromTriplets(expectedMatrixTriplets.begin(), expectedMatrixTriplets.end());

   std::stringstream matrixStream;
   matrix_io::eigen::write_sparse_float64_bin(matrixStream, expectedMatrix);
   Eigen::SparseMatrix<double> actualMatrix(3, 3);
   matrix_io::eigen::read_sparse_float64_bin(matrixStream, actualMatrix);

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}

TEST_CASE("matrix_io/eigen::read_sparse_float64_mtx | matrix_io/eigen::write_sparse_float64_mtx")
{
   Eigen::SparseMatrix<double> expectedMatrix(3, 3);
   std::vector<Eigen::Triplet<double> > expectedMatrixTriplets;
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 0, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 1, 2));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 2, 3));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 0, 7));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 1, 8));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 2, 9));
   expectedMatrix.setFromTriplets(expectedMatrixTriplets.begin(), expectedMatrixTriplets.end());

   std::stringstream matrixStream;
   matrix_io::eigen::write_sparse_float64_mtx(matrixStream, expectedMatrix);
   Eigen::SparseMatrix<double> actualMatrix(3, 3);
   matrix_io::eigen::read_sparse_float64_mtx(matrixStream, actualMatrix);

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}

TEST_CASE("matrix_io/eigen::read_sparse_binary_bin | matrix_io/eigen::write_sparse_binary_bin")
{
   Eigen::SparseMatrix<double> expectedMatrix(3, 3);
   std::vector<Eigen::Triplet<double> > expectedMatrixTriplets;
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 0, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 1, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(0, 2, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 0, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 1, 1));
   expectedMatrixTriplets.push_back(Eigen::Triplet<double>(2, 2, 1));
   expectedMatrix.setFromTriplets(expectedMatrixTriplets.begin(), expectedMatrixTriplets.end());

   std::stringstream matrixStream;
   matrix_io::eigen::write_sparse_binary_bin(matrixStream, expectedMatrix);
   Eigen::SparseMatrix<double> actualMatrix(3, 3);
   matrix_io::eigen::read_sparse_binary_bin(matrixStream, actualMatrix);

   REQUIRE(actualMatrix.isApprox(expectedMatrix));
}<|endoftext|>


<commit_before>//
//  TZX.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 16/07/2017.
//  Copyright © 2017 Thomas Harte. All rights reserved.
//

#include "TZX.hpp"

using namespace Storage::Tape;

namespace {
const unsigned int StandardTZXClock = 3500000;
}

TZX::TZX(const char *file_name) :
	Storage::FileHolder(file_name),
	is_high_(false) {

	// Check for signature followed by a 0x1a
	char identifier[7];
	char signature[] = "ZXTape!";
	fread(identifier, 1, strlen(signature), file_);
	if(memcmp(identifier, signature, strlen(signature))) throw ErrorNotTZX;
	if(fgetc(file_) != 0x1a) throw ErrorNotTZX;

	// Get version number
	uint8_t major_version = (uint8_t)fgetc(file_);
	uint8_t minor_version = (uint8_t)fgetc(file_);

	// Reject if an incompatible version
	if(major_version != 1 || minor_version > 20)  throw ErrorNotTZX;
}

void TZX::virtual_reset() {
	clear();
	fseek(file_, 0x0a, SEEK_SET);
}

void TZX::get_next_pulses() {
	while(empty()) {
		uint8_t chunk_id = (uint8_t)fgetc(file_);
		if(feof(file_)) {
			set_is_at_end(true);
			return;
		}

		switch(chunk_id) {
			case 0x19:	get_generalised_data_block();	break;

			case 0x30: {
				// Text description. Ripe for ignoring.
				int length = fgetc(file_);
				fseek(file_, length, SEEK_CUR);
			} break;

			default:
				// In TZX each chunk has a different way of stating or implying its length,
				// so there is no route past an unimplemented chunk.
				printf("!!Unknown %02x!!", chunk_id);
				set_is_at_end(true);
			return;
		}
	}
}

void TZX::get_generalised_data_block() {
	uint32_t block_length = fgetc32le();
	uint16_t pause_after_block = fgetc16le();

	uint32_t total_pilot_symbols = fgetc32le();
	uint8_t maximum_pulses_per_pilot_symbol = (uint8_t)fgetc(file_);
	uint8_t symbols_in_pilot_table = (uint8_t)fgetc(file_);

	uint32_t total_data_symbols = fgetc32le();
	uint8_t maximum_pulses_per_data_symbol = (uint8_t)fgetc(file_);
	uint8_t symbols_in_data_table = (uint8_t)fgetc(file_);

	get_generalised_segment(total_pilot_symbols, maximum_pulses_per_pilot_symbol, symbols_in_pilot_table, false);
	get_generalised_segment(total_data_symbols, maximum_pulses_per_data_symbol, symbols_in_data_table, true);
	emplace_back(Tape::Pulse::Zero, Storage::Time((unsigned int)pause_after_block, 1000u));
}

void TZX::get_generalised_segment(uint32_t output_symbols, uint8_t max_pulses_per_symbol, uint8_t number_of_symbols, bool is_data) {
	if(!output_symbols) return;

	// Construct the symbol table.
	struct Symbol {
		uint8_t flags;
		std::vector<uint16_t> pulse_lengths;
	};
	std::vector<Symbol> symbol_table;
	for(int c = 0; c < number_of_symbols; c++) {
		Symbol symbol;
		symbol.flags = (uint8_t)fgetc(file_);
		for(int ic = 0; ic < max_pulses_per_symbol; ic++) {
			symbol.pulse_lengths.push_back(fgetc16le());
		}
		symbol_table.push_back(symbol);
	}

	// Hence produce the output.
	BitStream stream(file_, false);
	int base = 2;
	int bits = 1;
	while(base < number_of_symbols) {
		base <<= 1;
		bits++;
	}
	for(int c = 0; c < output_symbols; c++) {
		uint8_t symbol_value;
		int count;
		if(is_data) {
			symbol_value = stream.get_bits(bits);
			count = 1;
		} else {
			symbol_value = (uint8_t)fgetc(file_);
			count = fgetc16le();
		}
		if(symbol_value > number_of_symbols) {
			continue;
		}
		Symbol &symbol = symbol_table[symbol_value];

		while(count--) {
			// Mutate initial output level.
			switch(symbol.flags & 3) {
				case 0: break;
				case 1: is_high_ ^= true;	break;
				case 2: is_high_ = true;	break;
				case 3: is_high_ = false;	break;
			}

			// Output waves.
			for(auto length : symbol.pulse_lengths) {
				if(!length) break;

				is_high_ ^= true;
				emplace_back(is_high_ ? Tape::Pulse::High : Tape::Pulse::Low, Storage::Time((unsigned int)length, StandardTZXClock));
			}
		}
	}
}
<commit_msg>Added a safety seek.<commit_after>//
//  TZX.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 16/07/2017.
//  Copyright © 2017 Thomas Harte. All rights reserved.
//

#include "TZX.hpp"

using namespace Storage::Tape;

namespace {
const unsigned int StandardTZXClock = 3500000;
}

TZX::TZX(const char *file_name) :
	Storage::FileHolder(file_name),
	is_high_(false) {

	// Check for signature followed by a 0x1a
	char identifier[7];
	char signature[] = "ZXTape!";
	fread(identifier, 1, strlen(signature), file_);
	if(memcmp(identifier, signature, strlen(signature))) throw ErrorNotTZX;
	if(fgetc(file_) != 0x1a) throw ErrorNotTZX;

	// Get version number
	uint8_t major_version = (uint8_t)fgetc(file_);
	uint8_t minor_version = (uint8_t)fgetc(file_);

	// Reject if an incompatible version
	if(major_version != 1 || minor_version > 20)  throw ErrorNotTZX;
}

void TZX::virtual_reset() {
	clear();
	fseek(file_, 0x0a, SEEK_SET);
}

void TZX::get_next_pulses() {
	while(empty()) {
		uint8_t chunk_id = (uint8_t)fgetc(file_);
		if(feof(file_)) {
			set_is_at_end(true);
			return;
		}

		switch(chunk_id) {
			case 0x19:	get_generalised_data_block();	break;

			case 0x30: {
				// Text description. Ripe for ignoring.
				int length = fgetc(file_);
				fseek(file_, length, SEEK_CUR);
			} break;

			default:
				// In TZX each chunk has a different way of stating or implying its length,
				// so there is no route past an unimplemented chunk.
				printf("!!Unknown %02x!!", chunk_id);
				set_is_at_end(true);
			return;
		}
	}
}

void TZX::get_generalised_data_block() {
	uint32_t block_length = fgetc32le();
	long endpoint = ftell(file_) + (long)block_length;
	uint16_t pause_after_block = fgetc16le();

	uint32_t total_pilot_symbols = fgetc32le();
	uint8_t maximum_pulses_per_pilot_symbol = (uint8_t)fgetc(file_);
	uint8_t symbols_in_pilot_table = (uint8_t)fgetc(file_);

	uint32_t total_data_symbols = fgetc32le();
	uint8_t maximum_pulses_per_data_symbol = (uint8_t)fgetc(file_);
	uint8_t symbols_in_data_table = (uint8_t)fgetc(file_);

	get_generalised_segment(total_pilot_symbols, maximum_pulses_per_pilot_symbol, symbols_in_pilot_table, false);
	get_generalised_segment(total_data_symbols, maximum_pulses_per_data_symbol, symbols_in_data_table, true);
	emplace_back(Tape::Pulse::Zero, Storage::Time((unsigned int)pause_after_block, 1000u));

	// This should be unnecessary, but intends to preserve sanity.
	fseek(file_, endpoint, SEEK_SET);
}

void TZX::get_generalised_segment(uint32_t output_symbols, uint8_t max_pulses_per_symbol, uint8_t number_of_symbols, bool is_data) {
	if(!output_symbols) return;

	// Construct the symbol table.
	struct Symbol {
		uint8_t flags;
		std::vector<uint16_t> pulse_lengths;
	};
	std::vector<Symbol> symbol_table;
	for(int c = 0; c < number_of_symbols; c++) {
		Symbol symbol;
		symbol.flags = (uint8_t)fgetc(file_);
		for(int ic = 0; ic < max_pulses_per_symbol; ic++) {
			symbol.pulse_lengths.push_back(fgetc16le());
		}
		symbol_table.push_back(symbol);
	}

	// Hence produce the output.
	BitStream stream(file_, false);
	int base = 2;
	int bits = 1;
	while(base < number_of_symbols) {
		base <<= 1;
		bits++;
	}
	for(int c = 0; c < output_symbols; c++) {
		uint8_t symbol_value;
		int count;
		if(is_data) {
			symbol_value = stream.get_bits(bits);
			count = 1;
		} else {
			symbol_value = (uint8_t)fgetc(file_);
			count = fgetc16le();
		}
		if(symbol_value > number_of_symbols) {
			continue;
		}
		Symbol &symbol = symbol_table[symbol_value];

		while(count--) {
			// Mutate initial output level.
			switch(symbol.flags & 3) {
				case 0: break;
				case 1: is_high_ ^= true;	break;
				case 2: is_high_ = true;	break;
				case 3: is_high_ = false;	break;
			}

			// Output waves.
			for(auto length : symbol.pulse_lengths) {
				if(!length) break;

				is_high_ ^= true;
				emplace_back(is_high_ ? Tape::Pulse::High : Tape::Pulse::Low, Storage::Time((unsigned int)length, StandardTZXClock));
			}
		}
	}
}
<|endoftext|>


<commit_before>/* Simple access class for I2C EEPROM chips like Microchip 24LC
 * Copyright (c) 2015 Robin Hourahane
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "I2CEEBlockDevice.h"
#include "rtos/ThisThread.h"
using namespace mbed;

#define I2CEE_TIMEOUT 10000

I2CEEBlockDevice::I2CEEBlockDevice(
    PinName sda, PinName scl, uint8_t addr,
    bd_size_t size, bd_size_t block, int freq)
    : _i2c_addr(addr), _size(size), _block(block)
{
    _i2c = new (_i2c_buffer) I2C(sda, scl);
    _i2c->frequency(freq);
}

I2CEEBlockDevice::I2CEEBlockDevice(
    I2C *i2c_obj, uint8_t addr,
    bd_size_t size, bd_size_t block)
    : _i2c_addr(addr), _size(size), _block(block)
{
    _i2c = i2c_obj;
}
I2CEEBlockDevice::~I2CEEBlockDevice()
{
    if (_i2c == (I2C *)_i2c_buffer) {
        _i2c->~I2C();
    }
}

int I2CEEBlockDevice::init()
{
    return _sync();
}

int I2CEEBlockDevice::deinit()
{
    return 0;
}

int I2CEEBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
{
    // Check the address and size fit onto the chip.
    MBED_ASSERT(is_valid_read(addr, size));

    _i2c->start();

    if (!_i2c->write(_i2c_addr | 0) ||
            !_i2c->write((char)(addr >> 8)) ||
            !_i2c->write((char)(addr & 0xff))) {
        return BD_ERROR_DEVICE_ERROR;
    }

    _i2c->stop();

    _sync();

    if (0 != _i2c->read(_i2c_addr, static_cast<char *>(buffer), size)) {
        return BD_ERROR_DEVICE_ERROR;
    }

    return 0;
}

int I2CEEBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
{
    // Check the addr and size fit onto the chip.
    MBED_ASSERT(is_valid_program(addr, size));

    // While we have some more data to write.
    while (size > 0) {
        uint32_t off = addr % _block;
        uint32_t chunk = (off + size < _block) ? size : (_block - off);

        _i2c->start();

        if (!_i2c->write(_i2c_addr | 0) ||
                !_i2c->write((char)(addr >> 8)) ||
                !_i2c->write((char)(addr & 0xff))) {
            return BD_ERROR_DEVICE_ERROR;
        }

        for (unsigned i = 0; i < chunk; i++) {
            _i2c->write(static_cast<const char *>(buffer)[i]);
        }

        _i2c->stop();

        int err = _sync();

        if (err) {
            return err;
        }

        addr += chunk;
        size -= chunk;
        buffer = static_cast<const char *>(buffer) + chunk;
    }

    return 0;
}

int I2CEEBlockDevice::erase(bd_addr_t addr, bd_size_t size)
{
    // No erase needed
    return 0;
}

int I2CEEBlockDevice::_sync()
{
    // The chip doesn't ACK while writing to the actual EEPROM
    // so loop trying to do a zero byte write until it is ACKed
    // by the chip.
    for (int i = 0; i < I2CEE_TIMEOUT; i++) {
        if (_i2c->write(_i2c_addr | 0, 0, 0) < 1) {
            return 0;
        }

        rtos::ThisThread::sleep_for(1);
    }

    return BD_ERROR_DEVICE_ERROR;
}

bd_size_t I2CEEBlockDevice::get_read_size() const
{
    return 1;
}

bd_size_t I2CEEBlockDevice::get_program_size() const
{
    return 1;
}

bd_size_t I2CEEBlockDevice::get_erase_size() const
{
    return 1;
}

bd_size_t I2CEEBlockDevice::size() const
{
    return _size;
}

const char *I2CEEBlockDevice::get_type() const
{
    return "I2CEE";
}
<commit_msg>Add error check for _sync() in I2CEEBlockDevice::read<commit_after>/* Simple access class for I2C EEPROM chips like Microchip 24LC
 * Copyright (c) 2015 Robin Hourahane
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "I2CEEBlockDevice.h"
#include "rtos/ThisThread.h"
using namespace mbed;

#define I2CEE_TIMEOUT 10000

I2CEEBlockDevice::I2CEEBlockDevice(
    PinName sda, PinName scl, uint8_t addr,
    bd_size_t size, bd_size_t block, int freq)
    : _i2c_addr(addr), _size(size), _block(block)
{
    _i2c = new (_i2c_buffer) I2C(sda, scl);
    _i2c->frequency(freq);
}

I2CEEBlockDevice::I2CEEBlockDevice(
    I2C *i2c_obj, uint8_t addr,
    bd_size_t size, bd_size_t block)
    : _i2c_addr(addr), _size(size), _block(block)
{
    _i2c = i2c_obj;
}
I2CEEBlockDevice::~I2CEEBlockDevice()
{
    if (_i2c == (I2C *)_i2c_buffer) {
        _i2c->~I2C();
    }
}

int I2CEEBlockDevice::init()
{
    return _sync();
}

int I2CEEBlockDevice::deinit()
{
    return 0;
}

int I2CEEBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
{
    // Check the address and size fit onto the chip.
    MBED_ASSERT(is_valid_read(addr, size));

    _i2c->start();

    if (!_i2c->write(_i2c_addr | 0) ||
            !_i2c->write((char)(addr >> 8)) ||
            !_i2c->write((char)(addr & 0xff))) {
        return BD_ERROR_DEVICE_ERROR;
    }

    _i2c->stop();

    auto err = _sync();
    if (err) {
        return err;
    }

    if (0 != _i2c->read(_i2c_addr, static_cast<char *>(buffer), size)) {
        return BD_ERROR_DEVICE_ERROR;
    }

    return 0;
}

int I2CEEBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
{
    // Check the addr and size fit onto the chip.
    MBED_ASSERT(is_valid_program(addr, size));

    // While we have some more data to write.
    while (size > 0) {
        uint32_t off = addr % _block;
        uint32_t chunk = (off + size < _block) ? size : (_block - off);

        _i2c->start();

        if (!_i2c->write(_i2c_addr | 0) ||
                !_i2c->write((char)(addr >> 8)) ||
                !_i2c->write((char)(addr & 0xff))) {
            return BD_ERROR_DEVICE_ERROR;
        }

        for (unsigned i = 0; i < chunk; i++) {
            _i2c->write(static_cast<const char *>(buffer)[i]);
        }

        _i2c->stop();

        int err = _sync();

        if (err) {
            return err;
        }

        addr += chunk;
        size -= chunk;
        buffer = static_cast<const char *>(buffer) + chunk;
    }

    return 0;
}

int I2CEEBlockDevice::erase(bd_addr_t addr, bd_size_t size)
{
    // No erase needed
    return 0;
}

int I2CEEBlockDevice::_sync()
{
    // The chip doesn't ACK while writing to the actual EEPROM
    // so loop trying to do a zero byte write until it is ACKed
    // by the chip.
    for (int i = 0; i < I2CEE_TIMEOUT; i++) {
        if (_i2c->write(_i2c_addr | 0, 0, 0) < 1) {
            return 0;
        }

        rtos::ThisThread::sleep_for(1);
    }

    return BD_ERROR_DEVICE_ERROR;
}

bd_size_t I2CEEBlockDevice::get_read_size() const
{
    return 1;
}

bd_size_t I2CEEBlockDevice::get_program_size() const
{
    return 1;
}

bd_size_t I2CEEBlockDevice::get_erase_size() const
{
    return 1;
}

bd_size_t I2CEEBlockDevice::size() const
{
    return _size;
}

const char *I2CEEBlockDevice::get_type() const
{
    return "I2CEE";
}
<|endoftext|>


<commit_before>/* mbed Microcontroller Library
 * Copyright (c) 2017 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "mbed_events.h"
#include "mbed.h"
#include "rtos.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include <cstdlib>
#include <cmath>

using namespace utest::v1;

// Test delay
#ifndef TEST_EVENTS_TIMING_TIME
#define TEST_EVENTS_TIMING_TIME 20000
#endif

#ifndef TEST_EVENTS_TIMING_MEAN
#define TEST_EVENTS_TIMING_MEAN 25
#endif

#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif

// Random number generation to skew timing values
float gauss(float mu, float sigma) {
    float x = (float)rand() / ((float)RAND_MAX+1);
    float y = (float)rand() / ((float)RAND_MAX+1);
    float x2pi = x*2.0*M_PI;
    float g2rad = sqrt(-2.0 * log(1.0-y));
    float z = cos(x2pi) * g2rad;
    return mu + z*sigma;
}

float chisq(float sigma) {
    return pow(gauss(0, sqrt(sigma)), 2);
}

Timer timer;
DigitalOut led(LED1);

equeue_sema_t sema;

// Timer timing test
void timer_timing_test() {
    timer.reset();
    timer.start();
    int prev = timer.read_us();

    while (prev < TEST_EVENTS_TIMING_TIME*1000) {
        int next = timer.read_us();
        if (next < prev) {
            printf("backwards drift %d -> %d (%08x -> %08x)\r\n",
                prev, next, prev, next);
        }
        TEST_ASSERT(next >= prev);
        prev = next;
    }
}

// equeue tick timing test
void tick_timing_test() {
    unsigned start = equeue_tick();
    int prev = 0;

    while (prev < TEST_EVENTS_TIMING_TIME) {
        int next = equeue_tick() - start;
        if (next < prev) {
            printf("backwards drift %d -> %d (%08x -> %08x)\r\n",
                prev, next, prev, next);
        }
        TEST_ASSERT(next >= prev);
        prev = next;
    }
}

// equeue semaphore timing test
void semaphore_timing_test() {
    srand(0);
    timer.reset();
    timer.start();

    int err = equeue_sema_create(&sema);
    TEST_ASSERT_EQUAL(0, err);

    while (timer.read_ms() < TEST_EVENTS_TIMING_TIME) {
        int delay = chisq(TEST_EVENTS_TIMING_MEAN);

        int start = timer.read_us();
        equeue_sema_wait(&sema, delay);
        int taken = timer.read_us() - start;

        printf("delay %dms => error %dus\r\n", delay, abs(1000*delay - taken));
        TEST_ASSERT_INT_WITHIN(5000, taken, delay * 1000);

        led = !led;
    }

    equeue_sema_destroy(&sema);
}

// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases) {
    GREENTEA_SETUP((number_of_cases+1)*TEST_EVENTS_TIMING_TIME/1000, "default_auto");
    return verbose_test_setup_handler(number_of_cases);
}

const Case cases[] = {
    Case("Testing accuracy of timer", timer_timing_test),
    Case("Testing accuracy of equeue tick", tick_timing_test),
    Case("Testing accuracy of equeue semaphore", semaphore_timing_test),
};

Specification specification(test_setup, cases);

int main() {
    return !Harness::run(specification);
}

<commit_msg>tests-events-timing - print debug info only in case of failure.<commit_after>/* mbed Microcontroller Library
 * Copyright (c) 2017 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "mbed_events.h"
#include "mbed.h"
#include "rtos.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include <cstdlib>
#include <cmath>

using namespace utest::v1;

// Test delay
#ifndef TEST_EVENTS_TIMING_TIME
#define TEST_EVENTS_TIMING_TIME 20000
#endif

#ifndef TEST_EVENTS_TIMING_MEAN
#define TEST_EVENTS_TIMING_MEAN 25
#endif

#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif

// Random number generation to skew timing values
float gauss(float mu, float sigma) {
    float x = (float)rand() / ((float)RAND_MAX+1);
    float y = (float)rand() / ((float)RAND_MAX+1);
    float x2pi = x*2.0*M_PI;
    float g2rad = sqrt(-2.0 * log(1.0-y));
    float z = cos(x2pi) * g2rad;
    return mu + z*sigma;
}

float chisq(float sigma) {
    return pow(gauss(0, sqrt(sigma)), 2);
}

Timer timer;
DigitalOut led(LED1);

equeue_sema_t sema;

// Timer timing test
void timer_timing_test() {
    timer.reset();
    timer.start();
    int prev = timer.read_us();

    while (prev < TEST_EVENTS_TIMING_TIME*1000) {
        int next = timer.read_us();
        if (next < prev) {
            printf("backwards drift %d -> %d (%08x -> %08x)\r\n",
                prev, next, prev, next);
        }
        TEST_ASSERT(next >= prev);
        prev = next;
    }
}

// equeue tick timing test
void tick_timing_test() {
    unsigned start = equeue_tick();
    int prev = 0;

    while (prev < TEST_EVENTS_TIMING_TIME) {
        int next = equeue_tick() - start;
        if (next < prev) {
            printf("backwards drift %d -> %d (%08x -> %08x)\r\n",
                prev, next, prev, next);
        }
        TEST_ASSERT(next >= prev);
        prev = next;
    }
}

// equeue semaphore timing test
void semaphore_timing_test() {
    srand(0);
    timer.reset();
    timer.start();

    int err = equeue_sema_create(&sema);
    TEST_ASSERT_EQUAL(0, err);

    while (timer.read_ms() < TEST_EVENTS_TIMING_TIME) {
        int delay = chisq(TEST_EVENTS_TIMING_MEAN);

        int start = timer.read_us();
        equeue_sema_wait(&sema, delay);
        int taken = timer.read_us() - start;

        if (taken < (delay * 1000 - 5000) || taken > (delay * 1000 + 5000)) {
            printf("delay %dms => error %dus\r\n", delay, abs(1000 * delay - taken));
        }

        TEST_ASSERT_INT_WITHIN(5000, taken, delay * 1000);

        led = !led;
    }

    equeue_sema_destroy(&sema);
}

// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases) {
    GREENTEA_SETUP((number_of_cases+1)*TEST_EVENTS_TIMING_TIME/1000, "default_auto");
    return verbose_test_setup_handler(number_of_cases);
}

const Case cases[] = {
    Case("Testing accuracy of timer", timer_timing_test),
    Case("Testing accuracy of equeue tick", tick_timing_test),
    Case("Testing accuracy of equeue semaphore", semaphore_timing_test),
};

Specification specification(test_setup, cases);

int main() {
    return !Harness::run(specification);
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2018, ARM Limited, All Rights Reserved
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#if !defined(MBED_CONF_RTOS_PRESENT)
#error [NOT_SUPPORTED] Test not supported.
#else

#define WIFI 2
#if !defined(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE) || \
    (MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
#error [NOT_SUPPORTED] No network configuration found for this target.
#else

#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "nsapi_dns.h"
#include "events/EventQueue.h"
#include "dns_tests.h"

using namespace utest::v1;

namespace {
NetworkInterface *net;
}

const char dns_test_hosts[MBED_CONF_APP_DNS_TEST_HOSTS_NUM][DNS_TEST_HOST_LEN] = MBED_CONF_APP_DNS_TEST_HOSTS;
const char dns_test_hosts_second[MBED_CONF_APP_DNS_TEST_HOSTS_NUM][DNS_TEST_HOST_LEN] = MBED_CONF_APP_DNS_TEST_HOSTS_SECOND;

// Callback used for asynchronous DNS result
void hostbyname_cb(void *data, nsapi_error_t result, SocketAddress *address)
{
    dns_application_data *app_data = static_cast<dns_application_data *>(data);
    app_data->result = result;
    if (address) {
        app_data->addr = *address;
    }
    app_data->semaphore->release();
    app_data->value_set = true;
}

// General function to do asynchronous DNS host name resolution
void do_asynchronous_gethostbyname(const char hosts[][DNS_TEST_HOST_LEN], unsigned int op_count, int *exp_ok, int *exp_no_mem, int *exp_dns_failure, int *exp_timeout)
{
    // Verify that there is enough hosts in the host list
    TEST_ASSERT(op_count <= MBED_CONF_APP_DNS_TEST_HOSTS_NUM)

    // Reset counters
    (*exp_ok) = 0;
    (*exp_no_mem) = 0;
    (*exp_dns_failure) = 0;
    (*exp_timeout) = 0;

    // Create callback semaphore and data
    rtos::Semaphore semaphore;
    dns_application_data *data = new dns_application_data[op_count];

    unsigned int count = 0;
    for (unsigned int i = 0; i < op_count; i++) {
        data[i].semaphore = &semaphore;
        nsapi_error_t err = net->gethostbyname_async(hosts[i], mbed::Callback<void(nsapi_error_t, SocketAddress *)>(hostbyname_cb, (void *) &data[i]));
        TEST_ASSERT(err >= 0 || err == NSAPI_ERROR_NO_MEMORY || err == NSAPI_ERROR_BUSY);
        if (err >= 0) {
            // Callback will be called
            count++;
        } else {
            // No memory to initiate DNS query, callback will not be called
            data[i].result = err;
        }
    }

    // Wait for callback(s) to complete
    for (unsigned int i = 0; i < count; i++) {
        semaphore.acquire();
    }

    // Print result
    for (unsigned int i = 0; i < op_count; i++) {
        TEST_ASSERT(data[i].result == NSAPI_ERROR_OK || data[i].result == NSAPI_ERROR_NO_MEMORY || data[i].result == NSAPI_ERROR_BUSY || data[i].result == NSAPI_ERROR_DNS_FAILURE || data[i].result == NSAPI_ERROR_TIMEOUT);
        if (data[i].result == NSAPI_ERROR_OK) {
            (*exp_ok)++;
            printf("DNS: query \"%s\" => \"%s\"\n",
                   hosts[i], data[i].addr.get_ip_address());
        } else if (data[i].result == NSAPI_ERROR_DNS_FAILURE) {
            (*exp_dns_failure)++;
            printf("DNS: query \"%s\" => DNS failure\n", hosts[i]);
        } else if (data[i].result == NSAPI_ERROR_TIMEOUT) {
            (*exp_timeout)++;
            printf("DNS: query \"%s\" => timeout\n", hosts[i]);
        } else if (data[i].result == NSAPI_ERROR_NO_MEMORY) {
            (*exp_no_mem)++;
            printf("DNS: query \"%s\" => no memory\n", hosts[i]);
        } else if (data[i].result == NSAPI_ERROR_BUSY) {
            (*exp_no_mem)++;
            printf("DNS: query \"%s\" => busy\n", hosts[i]);
        }
    }

    delete[] data;
}

void do_gethostbyname(const char hosts[][DNS_TEST_HOST_LEN], unsigned int op_count, int *exp_ok, int *exp_no_mem, int *exp_dns_failure, int *exp_timeout)
{
    // Verify that there is enough hosts in the host list
    TEST_ASSERT(op_count <= MBED_CONF_APP_DNS_TEST_HOSTS_NUM)

    // Reset counters
    (*exp_ok) = 0;
    (*exp_no_mem) = 0;
    (*exp_dns_failure) = 0;
    (*exp_timeout) = 0;

    for (unsigned int i = 0; i < op_count; i++) {
        SocketAddress address;
        nsapi_error_t err = net->gethostbyname(hosts[i], &address);

        if (err == NSAPI_ERROR_OK) {
            (*exp_ok)++;
            printf("DNS: query \"%s\" => \"%s\"\n",
                   hosts[i], address.get_ip_address());
        } else if (err == NSAPI_ERROR_DNS_FAILURE) {
            (*exp_dns_failure)++;
            printf("DNS: query \"%s\" => DNS failure\n", hosts[i]);
        } else if (err == NSAPI_ERROR_TIMEOUT) {
            (*exp_timeout)++;
            printf("DNS: query \"%s\" => timeout\n", hosts[i]);
        } else if (err == NSAPI_ERROR_NO_MEMORY) {
            (*exp_no_mem)++;
            printf("DNS: query \"%s\" => no memory\n", hosts[i]);
        } else if (err == NSAPI_ERROR_BUSY) {
            (*exp_no_mem)++;
            printf("DNS: query \"%s\" => busy\n", hosts[i]);
        } else {
            printf("DNS: query \"%s\" => %d, unexpected answer\n", hosts[i], err);
            TEST_ASSERT(err == NSAPI_ERROR_OK || err == NSAPI_ERROR_NO_MEMORY || err == NSAPI_ERROR_BUSY || err == NSAPI_ERROR_DNS_FAILURE || err == NSAPI_ERROR_TIMEOUT);
        }
    }
}

NetworkInterface *get_interface()
{
    return net;
}

static void net_bringup()
{
    nsapi_dns_reset();
    MBED_ASSERT(MBED_CONF_APP_DNS_TEST_HOSTS_NUM >= MBED_CONF_NSAPI_DNS_CACHE_SIZE && MBED_CONF_APP_DNS_TEST_HOSTS_NUM >= MBED_CONF_APP_DNS_SIMULT_QUERIES + 1);

    net = NetworkInterface::get_default_instance();
    nsapi_error_t err = net->connect();
    TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, err);
    printf("MBED: IP address is '%s'\n", net->get_ip_address() ? net->get_ip_address() : "null");
}

static void net_bringdown()
{
    NetworkInterface::get_default_instance()->disconnect();
    printf("MBED: ifdown\n");
}

// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases)
{
    GREENTEA_SETUP(dns_global::TESTS_TIMEOUT, "default_auto");
    net_bringup();
    return verbose_test_setup_handler(number_of_cases);
}

void greentea_teardown(const size_t passed, const size_t failed, const failure_t failure)
{
    net_bringdown();
    return greentea_test_teardown_handler(passed, failed, failure);
}

Case cases[] = {
    Case("ASYNCHRONOUS_DNS", ASYNCHRONOUS_DNS),
    Case("ASYNCHRONOUS_DNS_SIMULTANEOUS", ASYNCHRONOUS_DNS_SIMULTANEOUS),
    Case("ASYNCHRONOUS_DNS_SIMULTANEOUS_CACHE", ASYNCHRONOUS_DNS_SIMULTANEOUS_CACHE),
    Case("SYNCHRONOUS_DNS_CACHE", SYNCHRONOUS_DNS_CACHE),
#ifndef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES
    Case("ASYNCHRONOUS_DNS_CACHE", ASYNCHRONOUS_DNS_CACHE),
#endif
#if !defined MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES || MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES > MBED_CONF_APP_DNS_TEST_HOSTS_NUM
    Case("ASYNCHRONOUS_DNS_NON_ASYNC_AND_ASYNC", ASYNCHRONOUS_DNS_NON_ASYNC_AND_ASYNC),
#endif
    Case("ASYNCHRONOUS_DNS_CANCEL", ASYNCHRONOUS_DNS_CANCEL),
#ifndef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES
    Case("ASYNCHRONOUS_DNS_EXTERNAL_EVENT_QUEUE", ASYNCHRONOUS_DNS_EXTERNAL_EVENT_QUEUE),
    Case("ASYNCHRONOUS_DNS_INVALID_HOST", ASYNCHRONOUS_DNS_INVALID_HOST),
    Case("ASYNCHRONOUS_DNS_TIMEOUTS", ASYNCHRONOUS_DNS_TIMEOUTS),
#endif
    Case("ASYNCHRONOUS_DNS_SIMULTANEOUS_REPEAT",  ASYNCHRONOUS_DNS_SIMULTANEOUS_REPEAT),
    Case("SYNCHRONOUS_DNS", SYNCHRONOUS_DNS),
    Case("SYNCHRONOUS_DNS_MULTIPLE", SYNCHRONOUS_DNS_MULTIPLE),
    Case("SYNCHRONOUS_DNS_INVALID", SYNCHRONOUS_DNS_INVALID),
};

Specification specification(test_setup, cases, greentea_teardown, greentea_continue_handlers);

int main()
{
    return !Harness::run(specification);
}

#endif // !defined(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE) || (MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
#endif // !defined(MBED_CONF_RTOS_PRESENT)
<commit_msg>Incorporated the review comments<commit_after>/*
 * Copyright (c) 2018, ARM Limited, All Rights Reserved
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#if !defined(MBED_CONF_RTOS_PRESENT)
#error [NOT_SUPPORTED] dns test cases requires RTOS to run.
#else

#define WIFI 2
#if !defined(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE) || \
    (MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
#error [NOT_SUPPORTED] No network configuration found for this target.
#else

#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "nsapi_dns.h"
#include "events/EventQueue.h"
#include "dns_tests.h"

using namespace utest::v1;

namespace {
NetworkInterface *net;
}

const char dns_test_hosts[MBED_CONF_APP_DNS_TEST_HOSTS_NUM][DNS_TEST_HOST_LEN] = MBED_CONF_APP_DNS_TEST_HOSTS;
const char dns_test_hosts_second[MBED_CONF_APP_DNS_TEST_HOSTS_NUM][DNS_TEST_HOST_LEN] = MBED_CONF_APP_DNS_TEST_HOSTS_SECOND;

// Callback used for asynchronous DNS result
void hostbyname_cb(void *data, nsapi_error_t result, SocketAddress *address)
{
    dns_application_data *app_data = static_cast<dns_application_data *>(data);
    app_data->result = result;
    if (address) {
        app_data->addr = *address;
    }
    app_data->semaphore->release();
    app_data->value_set = true;
}

// General function to do asynchronous DNS host name resolution
void do_asynchronous_gethostbyname(const char hosts[][DNS_TEST_HOST_LEN], unsigned int op_count, int *exp_ok, int *exp_no_mem, int *exp_dns_failure, int *exp_timeout)
{
    // Verify that there is enough hosts in the host list
    TEST_ASSERT(op_count <= MBED_CONF_APP_DNS_TEST_HOSTS_NUM)

    // Reset counters
    (*exp_ok) = 0;
    (*exp_no_mem) = 0;
    (*exp_dns_failure) = 0;
    (*exp_timeout) = 0;

    // Create callback semaphore and data
    rtos::Semaphore semaphore;
    dns_application_data *data = new dns_application_data[op_count];

    unsigned int count = 0;
    for (unsigned int i = 0; i < op_count; i++) {
        data[i].semaphore = &semaphore;
        nsapi_error_t err = net->gethostbyname_async(hosts[i], mbed::Callback<void(nsapi_error_t, SocketAddress *)>(hostbyname_cb, (void *) &data[i]));
        TEST_ASSERT(err >= 0 || err == NSAPI_ERROR_NO_MEMORY || err == NSAPI_ERROR_BUSY);
        if (err >= 0) {
            // Callback will be called
            count++;
        } else {
            // No memory to initiate DNS query, callback will not be called
            data[i].result = err;
        }
    }

    // Wait for callback(s) to complete
    for (unsigned int i = 0; i < count; i++) {
        semaphore.acquire();
    }

    // Print result
    for (unsigned int i = 0; i < op_count; i++) {
        TEST_ASSERT(data[i].result == NSAPI_ERROR_OK || data[i].result == NSAPI_ERROR_NO_MEMORY || data[i].result == NSAPI_ERROR_BUSY || data[i].result == NSAPI_ERROR_DNS_FAILURE || data[i].result == NSAPI_ERROR_TIMEOUT);
        if (data[i].result == NSAPI_ERROR_OK) {
            (*exp_ok)++;
            printf("DNS: query \"%s\" => \"%s\"\n",
                   hosts[i], data[i].addr.get_ip_address());
        } else if (data[i].result == NSAPI_ERROR_DNS_FAILURE) {
            (*exp_dns_failure)++;
            printf("DNS: query \"%s\" => DNS failure\n", hosts[i]);
        } else if (data[i].result == NSAPI_ERROR_TIMEOUT) {
            (*exp_timeout)++;
            printf("DNS: query \"%s\" => timeout\n", hosts[i]);
        } else if (data[i].result == NSAPI_ERROR_NO_MEMORY) {
            (*exp_no_mem)++;
            printf("DNS: query \"%s\" => no memory\n", hosts[i]);
        } else if (data[i].result == NSAPI_ERROR_BUSY) {
            (*exp_no_mem)++;
            printf("DNS: query \"%s\" => busy\n", hosts[i]);
        }
    }

    delete[] data;
}

void do_gethostbyname(const char hosts[][DNS_TEST_HOST_LEN], unsigned int op_count, int *exp_ok, int *exp_no_mem, int *exp_dns_failure, int *exp_timeout)
{
    // Verify that there is enough hosts in the host list
    TEST_ASSERT(op_count <= MBED_CONF_APP_DNS_TEST_HOSTS_NUM)

    // Reset counters
    (*exp_ok) = 0;
    (*exp_no_mem) = 0;
    (*exp_dns_failure) = 0;
    (*exp_timeout) = 0;

    for (unsigned int i = 0; i < op_count; i++) {
        SocketAddress address;
        nsapi_error_t err = net->gethostbyname(hosts[i], &address);

        if (err == NSAPI_ERROR_OK) {
            (*exp_ok)++;
            printf("DNS: query \"%s\" => \"%s\"\n",
                   hosts[i], address.get_ip_address());
        } else if (err == NSAPI_ERROR_DNS_FAILURE) {
            (*exp_dns_failure)++;
            printf("DNS: query \"%s\" => DNS failure\n", hosts[i]);
        } else if (err == NSAPI_ERROR_TIMEOUT) {
            (*exp_timeout)++;
            printf("DNS: query \"%s\" => timeout\n", hosts[i]);
        } else if (err == NSAPI_ERROR_NO_MEMORY) {
            (*exp_no_mem)++;
            printf("DNS: query \"%s\" => no memory\n", hosts[i]);
        } else if (err == NSAPI_ERROR_BUSY) {
            (*exp_no_mem)++;
            printf("DNS: query \"%s\" => busy\n", hosts[i]);
        } else {
            printf("DNS: query \"%s\" => %d, unexpected answer\n", hosts[i], err);
            TEST_ASSERT(err == NSAPI_ERROR_OK || err == NSAPI_ERROR_NO_MEMORY || err == NSAPI_ERROR_BUSY || err == NSAPI_ERROR_DNS_FAILURE || err == NSAPI_ERROR_TIMEOUT);
        }
    }
}

NetworkInterface *get_interface()
{
    return net;
}

static void net_bringup()
{
    nsapi_dns_reset();
    MBED_ASSERT(MBED_CONF_APP_DNS_TEST_HOSTS_NUM >= MBED_CONF_NSAPI_DNS_CACHE_SIZE && MBED_CONF_APP_DNS_TEST_HOSTS_NUM >= MBED_CONF_APP_DNS_SIMULT_QUERIES + 1);

    net = NetworkInterface::get_default_instance();
    nsapi_error_t err = net->connect();
    TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, err);
    printf("MBED: IP address is '%s'\n", net->get_ip_address() ? net->get_ip_address() : "null");
}

static void net_bringdown()
{
    NetworkInterface::get_default_instance()->disconnect();
    printf("MBED: ifdown\n");
}

// Test setup
utest::v1::status_t test_setup(const size_t number_of_cases)
{
    GREENTEA_SETUP(dns_global::TESTS_TIMEOUT, "default_auto");
    net_bringup();
    return verbose_test_setup_handler(number_of_cases);
}

void greentea_teardown(const size_t passed, const size_t failed, const failure_t failure)
{
    net_bringdown();
    return greentea_test_teardown_handler(passed, failed, failure);
}

Case cases[] = {
    Case("ASYNCHRONOUS_DNS", ASYNCHRONOUS_DNS),
    Case("ASYNCHRONOUS_DNS_SIMULTANEOUS", ASYNCHRONOUS_DNS_SIMULTANEOUS),
    Case("ASYNCHRONOUS_DNS_SIMULTANEOUS_CACHE", ASYNCHRONOUS_DNS_SIMULTANEOUS_CACHE),
    Case("SYNCHRONOUS_DNS_CACHE", SYNCHRONOUS_DNS_CACHE),
#ifndef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES
    Case("ASYNCHRONOUS_DNS_CACHE", ASYNCHRONOUS_DNS_CACHE),
#endif
#if !defined MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES || MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES > MBED_CONF_APP_DNS_TEST_HOSTS_NUM
    Case("ASYNCHRONOUS_DNS_NON_ASYNC_AND_ASYNC", ASYNCHRONOUS_DNS_NON_ASYNC_AND_ASYNC),
#endif
    Case("ASYNCHRONOUS_DNS_CANCEL", ASYNCHRONOUS_DNS_CANCEL),
#ifndef MBED_CONF_CELLULAR_OFFLOAD_DNS_QUERIES
    Case("ASYNCHRONOUS_DNS_EXTERNAL_EVENT_QUEUE", ASYNCHRONOUS_DNS_EXTERNAL_EVENT_QUEUE),
    Case("ASYNCHRONOUS_DNS_INVALID_HOST", ASYNCHRONOUS_DNS_INVALID_HOST),
    Case("ASYNCHRONOUS_DNS_TIMEOUTS", ASYNCHRONOUS_DNS_TIMEOUTS),
#endif
    Case("ASYNCHRONOUS_DNS_SIMULTANEOUS_REPEAT",  ASYNCHRONOUS_DNS_SIMULTANEOUS_REPEAT),
    Case("SYNCHRONOUS_DNS", SYNCHRONOUS_DNS),
    Case("SYNCHRONOUS_DNS_MULTIPLE", SYNCHRONOUS_DNS_MULTIPLE),
    Case("SYNCHRONOUS_DNS_INVALID", SYNCHRONOUS_DNS_INVALID),
};

Specification specification(test_setup, cases, greentea_teardown, greentea_continue_handlers);

int main()
{
    return !Harness::run(specification);
}

#endif // !defined(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE) || (MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID))
#endif // !defined(MBED_CONF_RTOS_PRESENT)
<|endoftext|>


<commit_before>#include "ClientBackend.hpp"
#include "ClientConnection.hpp"
#include "CClientTcpTransport.hpp"
#include "ClientSettings.hpp"
#include "AccountStorage.hpp"
#include "ClientConnection.hpp"
#include "Client.hpp"
#include "ClientRpcLayer.hpp"
#include "DataStorage.hpp"

#include "Operations/ClientAuthOperation.hpp"

#include <QLoggingCategory>
#include <QTimer>

// Generated low-level layer includes
#include "ClientRpcAccountLayer.hpp"
#include "ClientRpcAuthLayer.hpp"
#include "ClientRpcBotsLayer.hpp"
#include "ClientRpcChannelsLayer.hpp"
#include "ClientRpcContactsLayer.hpp"
#include "ClientRpcHelpLayer.hpp"
#include "ClientRpcLangpackLayer.hpp"
#include "ClientRpcMessagesLayer.hpp"
#include "ClientRpcPaymentsLayer.hpp"
#include "ClientRpcPhoneLayer.hpp"
#include "ClientRpcPhotosLayer.hpp"
#include "ClientRpcStickersLayer.hpp"
#include "ClientRpcUpdatesLayer.hpp"
#include "ClientRpcUploadLayer.hpp"
#include "ClientRpcUsersLayer.hpp"
// End of generated low-level layer includes

namespace Telegram {

namespace Client {

Backend::Backend(Client *parent) :
    QObject(parent),
    m_client(parent)
{
    Backend *b = this;
    BaseRpcLayerExtension::SendMethod sendMethod = [b](const QByteArray &payload) mutable {
        return sendRpcRequest(b, payload);
    };

    // Generated low-level layer initialization
    m_accountLayer = new AccountRpcLayer(this);
    m_accountLayer->setSendMethod(sendMethod);
    m_authLayer = new AuthRpcLayer(this);
    m_authLayer->setSendMethod(sendMethod);
    m_botsLayer = new BotsRpcLayer(this);
    m_botsLayer->setSendMethod(sendMethod);
    m_channelsLayer = new ChannelsRpcLayer(this);
    m_channelsLayer->setSendMethod(sendMethod);
    m_contactsLayer = new ContactsRpcLayer(this);
    m_contactsLayer->setSendMethod(sendMethod);
    m_helpLayer = new HelpRpcLayer(this);
    m_helpLayer->setSendMethod(sendMethod);
    m_langpackLayer = new LangpackRpcLayer(this);
    m_langpackLayer->setSendMethod(sendMethod);
    m_messagesLayer = new MessagesRpcLayer(this);
    m_messagesLayer->setSendMethod(sendMethod);
    m_paymentsLayer = new PaymentsRpcLayer(this);
    m_paymentsLayer->setSendMethod(sendMethod);
    m_phoneLayer = new PhoneRpcLayer(this);
    m_phoneLayer->setSendMethod(sendMethod);
    m_photosLayer = new PhotosRpcLayer(this);
    m_photosLayer->setSendMethod(sendMethod);
    m_stickersLayer = new StickersRpcLayer(this);
    m_stickersLayer->setSendMethod(sendMethod);
    m_updatesLayer = new UpdatesRpcLayer(this);
    m_updatesLayer->setSendMethod(sendMethod);
    m_uploadLayer = new UploadRpcLayer(this);
    m_uploadLayer->setSendMethod(sendMethod);
    m_usersLayer = new UsersRpcLayer(this);
    m_usersLayer->setSendMethod(sendMethod);
    // End of generated low-level layer initialization
}

PendingOperation *Backend::connectToServer()
{
    if (m_mainConnection && m_mainConnection->status() != Connection::Status::Disconnected) {
        return PendingOperation::failOperation<PendingOperation>({
                                                                     { QStringLiteral("text"), QStringLiteral("Connection is already in progress") }
                                                                 });
    }

    if (m_mainConnection) {
        // TODO!
    }

    Connection *connection = nullptr;
    if (m_accountStorage->hasMinimalDataSet()) {
        connection = createConnection(m_accountStorage->dcInfo());
        connection->setAuthKey(m_accountStorage->authKey());
    } else {
        connection = createConnection(m_settings->serverConfiguration().first());
    }
    connection->setServerRsaKey(m_settings->serverRsaKey());
    setMainConnection(connection);
    return connection->connectToDc();
}

AuthOperation *Backend::signIn()
{
    if (!m_authOperation) {
        m_authOperation = new AuthOperation(this);
    }

    if (m_signedIn) {
        m_authOperation->setDelayedFinishedWithError({
                                                         { QStringLiteral("text"), QStringLiteral("Already signed in") }
                                                     });
        return m_authOperation;
    }
    if (!m_settings || !m_settings->isValid()) {
        qWarning() << "Invalid settings";
        m_authOperation->setDelayedFinishedWithError({
                                                         { QStringLiteral("text"), QStringLiteral("Invalid settings") }
                                                     });
        return m_authOperation;
    }

    // Transport?

/*  1 ) Establish TCP connection
    2a) if there is no key in AccountStorage, use DH layer to get it
    2b) use the key from AccountStorage
    -3) try to get self phone     (postponed)
    -4) if error, report an error (postponed)
    5a) if there is no phone number in AccountStorage, emit phoneRequired()
    6b) use phone from AccountStorage
     7) API Call authSendCode()
     8) If error 401 SESSION_PASSWORD_NEEDED:
     9)     API Call accountGetPassword() -> TLAccountPassword(salt)
    10)     API Call authCheckPassword( Utils::sha256(salt + password + salt) )
    11) API Call authSignIn()

     Request phone number

     Request auth code
     Request password

     Done!
  */

//    if (!m_private->m_appInfo || !m_private->m_appInfo->isValid()) {
//        qWarning() << "CTelegramCore::connectToServer(): App information is null or is not valid.";
//        return false;
//    }

//    m_private->m_dispatcher->setAppInformation(m_private->m_appInfo);
//    return m_private->m_dispatcher->connectToServer();
    // connectToServer(),
    // checkPhoneNumber()

    m_authOperation->setBackend(this);

    if (!m_accountStorage->phoneNumber().isEmpty()) {
        m_authOperation->setPhoneNumber(m_accountStorage->phoneNumber());
    }

    if (!mainConnection()) {
        m_authOperation->runAfter(connectToServer());
        return m_authOperation;
    }
    m_authOperation->setRunMethod(&AuthOperation::requestAuthCode);
    m_authOperation->startLater();

    connect(m_authOperation, &PendingOperation::succeeded, [this]() {
        m_signedIn = true;
        emit m_client->signedInChanged(m_signedIn);
    });
    return m_authOperation;
}

Connection *Backend::createConnection(const DcOption &dcOption)
{
    Connection *connection = new Connection(this);
    connection->setDcOption(dcOption);
    connection->rpcLayer()->setAppInformation(m_appInformation);

    TcpTransport *transport = new TcpTransport(connection);
    switch (m_settings->preferedSessionType()) {
    case Settings::SessionType::Default:
        break;
    case Settings::SessionType::Abridged:
        transport->setPreferedSessionType(TcpTransport::Abridged);
        break;
    case Settings::SessionType::Obfuscated:
        transport->setPreferedSessionType(TcpTransport::Obfuscated);
        break;
    }
    connection->setTransport(transport);
    return connection;
}

Connection *Backend::mainConnection()
{
    return m_mainConnection;
}

void Backend::setMainConnection(Connection *connection)
{
    m_mainConnection = connection;
    connect(m_mainConnection, &BaseConnection::statusChanged, [this](Connection::Status status) {
        switch (status) {
        case Connection::Status::Authenticated:
        case Connection::Status::Signed:
            m_accountStorage->setAuthKey(m_mainConnection->authKey());
            m_accountStorage->setAuthId(m_mainConnection->authId());
            break;
        default:
            break;
        }
    });
}

PendingRpcOperation *Backend::sendRpcRequest(Backend *backend, const QByteArray &payload)
{
    return backend->mainConnection()->rpcLayer()->sendEncryptedPackage(payload);
}

} // Client namespace

} // Telegram namespace
<commit_msg>ClientBackend: Check the conn status on setMainConnection()<commit_after>#include "ClientBackend.hpp"
#include "ClientConnection.hpp"
#include "CClientTcpTransport.hpp"
#include "ClientSettings.hpp"
#include "AccountStorage.hpp"
#include "ClientConnection.hpp"
#include "Client.hpp"
#include "ClientRpcLayer.hpp"
#include "DataStorage.hpp"

#include "Operations/ClientAuthOperation.hpp"

#include <QLoggingCategory>
#include <QTimer>

// Generated low-level layer includes
#include "ClientRpcAccountLayer.hpp"
#include "ClientRpcAuthLayer.hpp"
#include "ClientRpcBotsLayer.hpp"
#include "ClientRpcChannelsLayer.hpp"
#include "ClientRpcContactsLayer.hpp"
#include "ClientRpcHelpLayer.hpp"
#include "ClientRpcLangpackLayer.hpp"
#include "ClientRpcMessagesLayer.hpp"
#include "ClientRpcPaymentsLayer.hpp"
#include "ClientRpcPhoneLayer.hpp"
#include "ClientRpcPhotosLayer.hpp"
#include "ClientRpcStickersLayer.hpp"
#include "ClientRpcUpdatesLayer.hpp"
#include "ClientRpcUploadLayer.hpp"
#include "ClientRpcUsersLayer.hpp"
// End of generated low-level layer includes

namespace Telegram {

namespace Client {

Backend::Backend(Client *parent) :
    QObject(parent),
    m_client(parent)
{
    Backend *b = this;
    BaseRpcLayerExtension::SendMethod sendMethod = [b](const QByteArray &payload) mutable {
        return sendRpcRequest(b, payload);
    };

    // Generated low-level layer initialization
    m_accountLayer = new AccountRpcLayer(this);
    m_accountLayer->setSendMethod(sendMethod);
    m_authLayer = new AuthRpcLayer(this);
    m_authLayer->setSendMethod(sendMethod);
    m_botsLayer = new BotsRpcLayer(this);
    m_botsLayer->setSendMethod(sendMethod);
    m_channelsLayer = new ChannelsRpcLayer(this);
    m_channelsLayer->setSendMethod(sendMethod);
    m_contactsLayer = new ContactsRpcLayer(this);
    m_contactsLayer->setSendMethod(sendMethod);
    m_helpLayer = new HelpRpcLayer(this);
    m_helpLayer->setSendMethod(sendMethod);
    m_langpackLayer = new LangpackRpcLayer(this);
    m_langpackLayer->setSendMethod(sendMethod);
    m_messagesLayer = new MessagesRpcLayer(this);
    m_messagesLayer->setSendMethod(sendMethod);
    m_paymentsLayer = new PaymentsRpcLayer(this);
    m_paymentsLayer->setSendMethod(sendMethod);
    m_phoneLayer = new PhoneRpcLayer(this);
    m_phoneLayer->setSendMethod(sendMethod);
    m_photosLayer = new PhotosRpcLayer(this);
    m_photosLayer->setSendMethod(sendMethod);
    m_stickersLayer = new StickersRpcLayer(this);
    m_stickersLayer->setSendMethod(sendMethod);
    m_updatesLayer = new UpdatesRpcLayer(this);
    m_updatesLayer->setSendMethod(sendMethod);
    m_uploadLayer = new UploadRpcLayer(this);
    m_uploadLayer->setSendMethod(sendMethod);
    m_usersLayer = new UsersRpcLayer(this);
    m_usersLayer->setSendMethod(sendMethod);
    // End of generated low-level layer initialization
}

PendingOperation *Backend::connectToServer()
{
    if (m_mainConnection && m_mainConnection->status() != Connection::Status::Disconnected) {
        return PendingOperation::failOperation<PendingOperation>({
                                                                     { QStringLiteral("text"), QStringLiteral("Connection is already in progress") }
                                                                 });
    }

    if (m_mainConnection) {
        // TODO!
    }

    Connection *connection = nullptr;
    if (m_accountStorage->hasMinimalDataSet()) {
        connection = createConnection(m_accountStorage->dcInfo());
        connection->setAuthKey(m_accountStorage->authKey());
    } else {
        connection = createConnection(m_settings->serverConfiguration().first());
    }
    connection->setServerRsaKey(m_settings->serverRsaKey());
    setMainConnection(connection);
    return connection->connectToDc();
}

AuthOperation *Backend::signIn()
{
    if (!m_authOperation) {
        m_authOperation = new AuthOperation(this);
    }

    if (m_signedIn) {
        m_authOperation->setDelayedFinishedWithError({
                                                         { QStringLiteral("text"), QStringLiteral("Already signed in") }
                                                     });
        return m_authOperation;
    }
    if (!m_settings || !m_settings->isValid()) {
        qWarning() << "Invalid settings";
        m_authOperation->setDelayedFinishedWithError({
                                                         { QStringLiteral("text"), QStringLiteral("Invalid settings") }
                                                     });
        return m_authOperation;
    }

    // Transport?

/*  1 ) Establish TCP connection
    2a) if there is no key in AccountStorage, use DH layer to get it
    2b) use the key from AccountStorage
    -3) try to get self phone     (postponed)
    -4) if error, report an error (postponed)
    5a) if there is no phone number in AccountStorage, emit phoneRequired()
    6b) use phone from AccountStorage
     7) API Call authSendCode()
     8) If error 401 SESSION_PASSWORD_NEEDED:
     9)     API Call accountGetPassword() -> TLAccountPassword(salt)
    10)     API Call authCheckPassword( Utils::sha256(salt + password + salt) )
    11) API Call authSignIn()

     Request phone number

     Request auth code
     Request password

     Done!
  */

//    if (!m_private->m_appInfo || !m_private->m_appInfo->isValid()) {
//        qWarning() << "CTelegramCore::connectToServer(): App information is null or is not valid.";
//        return false;
//    }

//    m_private->m_dispatcher->setAppInformation(m_private->m_appInfo);
//    return m_private->m_dispatcher->connectToServer();
    // connectToServer(),
    // checkPhoneNumber()

    m_authOperation->setBackend(this);

    if (!m_accountStorage->phoneNumber().isEmpty()) {
        m_authOperation->setPhoneNumber(m_accountStorage->phoneNumber());
    }

    if (!mainConnection()) {
        m_authOperation->runAfter(connectToServer());
        return m_authOperation;
    }
    m_authOperation->setRunMethod(&AuthOperation::requestAuthCode);
    m_authOperation->startLater();

    connect(m_authOperation, &PendingOperation::succeeded, [this]() {
        m_signedIn = true;
        emit m_client->signedInChanged(m_signedIn);
    });
    return m_authOperation;
}

Connection *Backend::createConnection(const DcOption &dcOption)
{
    Connection *connection = new Connection(this);
    connection->setDcOption(dcOption);
    connection->rpcLayer()->setAppInformation(m_appInformation);

    TcpTransport *transport = new TcpTransport(connection);
    switch (m_settings->preferedSessionType()) {
    case Settings::SessionType::Default:
        break;
    case Settings::SessionType::Abridged:
        transport->setPreferedSessionType(TcpTransport::Abridged);
        break;
    case Settings::SessionType::Obfuscated:
        transport->setPreferedSessionType(TcpTransport::Obfuscated);
        break;
    }
    connection->setTransport(transport);
    return connection;
}

Connection *Backend::mainConnection()
{
    return m_mainConnection;
}

void Backend::setMainConnection(Connection *connection)
{
    m_mainConnection = connection;
    auto updateStatusLambda = [this](Connection::Status status) {
        switch (status) {
        case Connection::Status::Authenticated:
        case Connection::Status::Signed:
            m_accountStorage->setAuthKey(m_mainConnection->authKey());
            m_accountStorage->setAuthId(m_mainConnection->authId());
            break;
        default:
            break;
        }
    };
    connect(m_mainConnection, &BaseConnection::statusChanged, updateStatusLambda);
    updateStatusLambda(m_mainConnection->status());
}

PendingRpcOperation *Backend::sendRpcRequest(Backend *backend, const QByteArray &payload)
{
    return backend->mainConnection()->rpcLayer()->sendEncryptedPackage(payload);
}

} // Client namespace

} // Telegram namespace
<|endoftext|>


<commit_before>#include <string.h>
#include <fstream>
#include <iostream>
#include <string>
#include <complex>
#include <math.h>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <stdio.h>
#include <stack>
#include <algorithm>
#include <list>
#include <ctime>
#include <memory.h>
#include <ctime>
#include <assert.h>
#define pi 3.14159
#define mod 1000000007
using namespace std;
long long int a[500000];
int main()
{
	int t;
	cin>>t;
	if(t%2==1)
	{
		cout<<"NO";
	}
	else
	{
		if(t == 2)
		{
			cout<<"NO";
		}
		else
		{
			cout<<"YES";
		}
	}
	return 0;
}
<commit_msg>Delete Watermelon.cpp<commit_after><|endoftext|>