<commit_before>#include "bridge.hpp"
#include "util/common.hpp"
#include "dsp/ringbuffer.hpp"

#include <unistd.h>
#if ARCH_WIN
	#include <winsock2.h>
#else
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <arpa/inet.h>
	#include <netinet/tcp.h>
	#include <fcntl.h>
#endif

#include <thread>

namespace rack {

struct BridgeClientConnection;
static BridgeClientConnection *connections[BRIDGE_NUM_PORTS] = {};
static AudioIO *audioListeners[BRIDGE_NUM_PORTS] = {};
static std::thread serverThread;
static bool serverRunning = false;
static BridgeMidiDriver *driver = NULL;

struct BridgeClientConnection {
	int client;
	bool ready = false;

	int port = -1;
	int sampleRate = 0;

	~BridgeClientConnection() {
		setPort(-1);
	}

	/** Returns true if successful */
	bool send(const void *buffer, int length) {
		if (length <= 0)
			return false;

#if ARCH_LIN
		int flags = MSG_NOSIGNAL;
#else
		int flags = 0;
#endif
		ssize_t remaining = 0;
		while (remaining < length) {
			ssize_t actual = ::send(client, (const char*) buffer, length, flags);
			if (actual <= 0) {
				ready = false;
				return false;
			}
			remaining += actual;
		}
		return true;
	}

	template <typename T>
	bool send(T x) {
		return send(&x, sizeof(x));
	}

	/** Returns true if successful */
	bool recv(void *buffer, int length) {
		if (length <= 0)
			return false;

#if ARCH_LIN
		int flags = MSG_NOSIGNAL;
#else
		int flags = 0;
#endif
		ssize_t remaining = 0;
		while (remaining < length) {
			ssize_t actual = ::recv(client, (char*) buffer + remaining, length - remaining, flags);
			if (actual <= 0) {
				ready = false;
				return false;
			}
			remaining += actual;
		}
		return true;
	}

	template <typename T>
	bool recv(T *x) {
		return recv(x, sizeof(*x));
	}

	void flush() {
		// Turn off Nagle
		int flag = 1;
		setsockopt(client, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(int));
		// Turn on Nagle
		flag = 0;
		setsockopt(client, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(int));
	}

	void run() {
		info("Bridge client connected");

		// Check hello key
		uint32_t hello = -1;
		recv<uint32_t>(&hello);
		if (hello != BRIDGE_HELLO) {
			info("Bridge client protocol mismatch %x %x", hello, BRIDGE_HELLO);
			return;
		}

		// Process commands until no longer ready
		ready = true;
		while (ready) {
			step();
		}

		info("Bridge client closed");
	}

	/** Accepts a command from the client */
	void step() {
		uint8_t command;
		if (!recv<uint8_t>(&command)) {
			return;
		}

		switch (command) {
			default:
			case NO_COMMAND: {
				warn("Bridge client: bad command %d detected, closing", command);
				ready = false;
			} break;

			case QUIT_COMMAND: {
				ready = false;
			} break;

			case PORT_SET_COMMAND: {
				uint8_t port = -1;
				recv<uint8_t>(&port);
				setPort(port);
			} break;

			case MIDI_MESSAGE_COMMAND: {
				MidiMessage message;
				if (!recv(&message, 3)) {
					return;
				}
				processMidi(message);
			} break;

			case AUDIO_SAMPLE_RATE_SET_COMMAND: {
				uint32_t sampleRate = 0;
				recv<uint32_t>(&sampleRate);
				setSampleRate(sampleRate);
			} break;

			case AUDIO_PROCESS_COMMAND: {
				uint32_t frames = 0;
				recv<uint32_t>(&frames);
				if (frames == 0 || frames > (1<<16)) {
					ready = false;
					return;
				}

				float input[BRIDGE_INPUTS * frames];
				if (!recv(&input, BRIDGE_INPUTS * frames * sizeof(float))) {
					debug("Failed to receive");
					return;
				}

				float output[BRIDGE_OUTPUTS * frames];
				memset(&output, 0, sizeof(output));
				processStream(input, output, frames);
				if (!send(&output, BRIDGE_OUTPUTS * frames * sizeof(float))) {
					debug("Failed to send");
					return;
				}
				// flush();
			} break;
		}
	}

	void setPort(int port) {
		// Unbind from existing port
		if (0 <= this->port && connections[this->port] == this) {
			connections[this->port] = NULL;
		}

		// Bind to new port
		if ((0 <= port && port < BRIDGE_NUM_PORTS) && !connections[port]) {
			this->port = port;
			connections[this->port] = this;
			refreshAudio();
		}
		else {
			this->port = -1;
		}
	}

	void processMidi(MidiMessage message) {
		if (!(0 <= port && port < BRIDGE_NUM_PORTS))
			return;
		if (!driver)
			return;
		driver->devices[port].onMessage(message);
	}

	void setSampleRate(int sampleRate) {
		this->sampleRate = sampleRate;
		refreshAudio();
	}

	void processStream(const float *input, float *output, int frames) {
		if (!(0 <= port && port < BRIDGE_NUM_PORTS))
			return;
		if (!audioListeners[port])
			return;
		audioListeners[port]->setBlockSize(frames);
		audioListeners[port]->processStream(input, output, frames);
	}

	void refreshAudio() {
		if (!(0 <= port && port < BRIDGE_NUM_PORTS))
			return;
		if (connections[port] != this)
			return;
		if (!audioListeners[port])
			return;
		audioListeners[port]->setSampleRate(sampleRate);
	}
};

static void clientRun(int client) {
	defer({
#if ARCH_WIN
		if (shutdown(client, SD_SEND)) {
			warn("Bridge client shutdown() failed");
		}
		if (closesocket(client)) {
			warn("Bridge client closesocket() failed");
		}
#else
		if (close(client)) {
			warn("Bridge client close() failed");
		}
#endif
	});

#if ARCH_MAC
	// Avoid SIGPIPE
	int flag = 1;
	if (setsockopt(client, SOL_SOCKET, SO_NOSIGPIPE, &flag, sizeof(int))) {
		warn("Bridge client setsockopt() failed");
		return;
	}
#endif

	// Disable non-blocking
#if ARCH_WIN
	unsigned long blockingMode = 0;
	if (ioctlsocket(client, FIONBIO, &blockingMode)) {
		warn("Bridge client ioctlsocket() failed");
		return;
	}
#else
	if (fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) & ~O_NONBLOCK)) {
		warn("Bridge client fcntl() failed");
		return;
	}
#endif

	BridgeClientConnection connection;
	connection.client = client;
	connection.run();
}

static void serverConnect() {
	// Initialize sockets
#if ARCH_WIN
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData)) {
		warn("Bridge server WSAStartup() failed");
		return;
	}
	defer({
		WSACleanup();
	});
#endif

	// Get address
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(BRIDGE_PORT);
#if ARCH_WIN
	addr.sin_addr.s_addr = inet_addr(BRIDGE_HOST);
#else
	inet_pton(AF_INET, BRIDGE_HOST, &addr.sin_addr);
#endif

	// Open socket
	int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (server < 0) {
		warn("Bridge server socket() failed");
		return;
	}
	defer({
		if (close(server)) {
			warn("Bridge server close() failed");
			return;
		}
		info("Bridge server closed");
	});

#if ARCH_MAC || ARCH_LIN
	int reuseAddrFlag = 1;
	setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &reuseAddrFlag, sizeof(reuseAddrFlag));
#endif

	// Bind socket to address
	if (bind(server, (struct sockaddr*) &addr, sizeof(addr))) {
		warn("Bridge server bind() failed");
		return;
	}

	// Listen for clients
	if (listen(server, 20)) {
		warn("Bridge server listen() failed");
		return;
	}
	info("Bridge server started");

	// Enable non-blocking
#if ARCH_WIN
	unsigned long blockingMode = 1;
	if (ioctlsocket(server, FIONBIO, &blockingMode)) {
		warn("Bridge server ioctlsocket() failed");
		return;
	}
#else
	int flags = fcntl(server, F_GETFL, 0);
	fcntl(server, F_SETFL, flags | O_NONBLOCK);
#endif

	// Accept clients
	while (serverRunning) {
		int client = accept(server, NULL, NULL);
		if (client < 0) {
			// Wait a bit before attempting to accept another client
			std::this_thread::sleep_for(std::chrono::duration<double>(0.1));
			continue;
		}

		// Launch client thread
		std::thread clientThread(clientRun, client);
		clientThread.detach();
	}
}

static void serverRun() {
	while (serverRunning) {
		std::this_thread::sleep_for(std::chrono::duration<double>(0.1));
		serverConnect();
	}
}

std::vector<int> BridgeMidiDriver::getInputDeviceIds() {
	std::vector<int> deviceIds;
	for (int i = 0; i < 16; i++) {
		deviceIds.push_back(i);
	}
	return deviceIds;
}

std::string BridgeMidiDriver::getInputDeviceName(int deviceId) {
	return stringf("Port %d", deviceId + 1);
}

MidiInputDevice *BridgeMidiDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) {
	if (!(0 <= deviceId && deviceId < 16))
		return NULL;

	devices[deviceId].subscribe(midiInput);
	return &devices[deviceId];
}

void BridgeMidiDriver::unsubscribeInputDevice(int deviceId, MidiInput *midiInput) {
	if (!(0 <= deviceId && deviceId < 16))
		return;

	devices[deviceId].unsubscribe(midiInput);
}

void bridgeInit() {
	serverRunning = true;
	serverThread = std::thread(serverRun);

	driver = new BridgeMidiDriver();
	midiDriverAdd(BRIDGE_DRIVER, driver);
}

void bridgeDestroy() {
	serverRunning = false;
	serverThread.join();
}

void bridgeAudioSubscribe(int port, AudioIO *audio) {
	if (!(0 <= port && port < BRIDGE_NUM_PORTS))
		return;
	// Check if an Audio is already subscribed on the port
	if (audioListeners[port])
		return;
	audioListeners[port] = audio;
	if (connections[port])
		connections[port]->refreshAudio();
}

void bridgeAudioUnsubscribe(int port, AudioIO *audio) {
	if (!(0 <= port && port < BRIDGE_NUM_PORTS))
		return;
	if (audioListeners[port] != audio)
		return;
	audioListeners[port] = NULL;
}

} // namespace rack
<commit_msg>Fix Bridge port name for ID=-1<commit_after>#include "bridge.hpp"
#include "util/common.hpp"
#include "dsp/ringbuffer.hpp"

#include <unistd.h>
#if ARCH_WIN
	#include <winsock2.h>
#else
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <arpa/inet.h>
	#include <netinet/tcp.h>
	#include <fcntl.h>
#endif

#include <thread>

namespace rack {

struct BridgeClientConnection;
static BridgeClientConnection *connections[BRIDGE_NUM_PORTS] = {};
static AudioIO *audioListeners[BRIDGE_NUM_PORTS] = {};
static std::thread serverThread;
static bool serverRunning = false;
static BridgeMidiDriver *driver = NULL;

struct BridgeClientConnection {
	int client;
	bool ready = false;

	int port = -1;
	int sampleRate = 0;

	~BridgeClientConnection() {
		setPort(-1);
	}

	/** Returns true if successful */
	bool send(const void *buffer, int length) {
		if (length <= 0)
			return false;

#if ARCH_LIN
		int flags = MSG_NOSIGNAL;
#else
		int flags = 0;
#endif
		ssize_t remaining = 0;
		while (remaining < length) {
			ssize_t actual = ::send(client, (const char*) buffer, length, flags);
			if (actual <= 0) {
				ready = false;
				return false;
			}
			remaining += actual;
		}
		return true;
	}

	template <typename T>
	bool send(T x) {
		return send(&x, sizeof(x));
	}

	/** Returns true if successful */
	bool recv(void *buffer, int length) {
		if (length <= 0)
			return false;

#if ARCH_LIN
		int flags = MSG_NOSIGNAL;
#else
		int flags = 0;
#endif
		ssize_t remaining = 0;
		while (remaining < length) {
			ssize_t actual = ::recv(client, (char*) buffer + remaining, length - remaining, flags);
			if (actual <= 0) {
				ready = false;
				return false;
			}
			remaining += actual;
		}
		return true;
	}

	template <typename T>
	bool recv(T *x) {
		return recv(x, sizeof(*x));
	}

	void flush() {
		// Turn off Nagle
		int flag = 1;
		setsockopt(client, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(int));
		// Turn on Nagle
		flag = 0;
		setsockopt(client, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof(int));
	}

	void run() {
		info("Bridge client connected");

		// Check hello key
		uint32_t hello = -1;
		recv<uint32_t>(&hello);
		if (hello != BRIDGE_HELLO) {
			info("Bridge client protocol mismatch %x %x", hello, BRIDGE_HELLO);
			return;
		}

		// Process commands until no longer ready
		ready = true;
		while (ready) {
			step();
		}

		info("Bridge client closed");
	}

	/** Accepts a command from the client */
	void step() {
		uint8_t command;
		if (!recv<uint8_t>(&command)) {
			return;
		}

		switch (command) {
			default:
			case NO_COMMAND: {
				warn("Bridge client: bad command %d detected, closing", command);
				ready = false;
			} break;

			case QUIT_COMMAND: {
				ready = false;
			} break;

			case PORT_SET_COMMAND: {
				uint8_t port = -1;
				recv<uint8_t>(&port);
				setPort(port);
			} break;

			case MIDI_MESSAGE_COMMAND: {
				MidiMessage message;
				if (!recv(&message, 3)) {
					return;
				}
				processMidi(message);
			} break;

			case AUDIO_SAMPLE_RATE_SET_COMMAND: {
				uint32_t sampleRate = 0;
				recv<uint32_t>(&sampleRate);
				setSampleRate(sampleRate);
			} break;

			case AUDIO_PROCESS_COMMAND: {
				uint32_t frames = 0;
				recv<uint32_t>(&frames);
				if (frames == 0 || frames > (1<<16)) {
					ready = false;
					return;
				}

				float input[BRIDGE_INPUTS * frames];
				if (!recv(&input, BRIDGE_INPUTS * frames * sizeof(float))) {
					debug("Failed to receive");
					return;
				}

				float output[BRIDGE_OUTPUTS * frames];
				memset(&output, 0, sizeof(output));
				processStream(input, output, frames);
				if (!send(&output, BRIDGE_OUTPUTS * frames * sizeof(float))) {
					debug("Failed to send");
					return;
				}
				// flush();
			} break;
		}
	}

	void setPort(int port) {
		// Unbind from existing port
		if (0 <= this->port && connections[this->port] == this) {
			connections[this->port] = NULL;
		}

		// Bind to new port
		if ((0 <= port && port < BRIDGE_NUM_PORTS) && !connections[port]) {
			this->port = port;
			connections[this->port] = this;
			refreshAudio();
		}
		else {
			this->port = -1;
		}
	}

	void processMidi(MidiMessage message) {
		if (!(0 <= port && port < BRIDGE_NUM_PORTS))
			return;
		if (!driver)
			return;
		driver->devices[port].onMessage(message);
	}

	void setSampleRate(int sampleRate) {
		this->sampleRate = sampleRate;
		refreshAudio();
	}

	void processStream(const float *input, float *output, int frames) {
		if (!(0 <= port && port < BRIDGE_NUM_PORTS))
			return;
		if (!audioListeners[port])
			return;
		audioListeners[port]->setBlockSize(frames);
		audioListeners[port]->processStream(input, output, frames);
	}

	void refreshAudio() {
		if (!(0 <= port && port < BRIDGE_NUM_PORTS))
			return;
		if (connections[port] != this)
			return;
		if (!audioListeners[port])
			return;
		audioListeners[port]->setSampleRate(sampleRate);
	}
};

static void clientRun(int client) {
	defer({
#if ARCH_WIN
		if (shutdown(client, SD_SEND)) {
			warn("Bridge client shutdown() failed");
		}
		if (closesocket(client)) {
			warn("Bridge client closesocket() failed");
		}
#else
		if (close(client)) {
			warn("Bridge client close() failed");
		}
#endif
	});

#if ARCH_MAC
	// Avoid SIGPIPE
	int flag = 1;
	if (setsockopt(client, SOL_SOCKET, SO_NOSIGPIPE, &flag, sizeof(int))) {
		warn("Bridge client setsockopt() failed");
		return;
	}
#endif

	// Disable non-blocking
#if ARCH_WIN
	unsigned long blockingMode = 0;
	if (ioctlsocket(client, FIONBIO, &blockingMode)) {
		warn("Bridge client ioctlsocket() failed");
		return;
	}
#else
	if (fcntl(client, F_SETFL, fcntl(client, F_GETFL, 0) & ~O_NONBLOCK)) {
		warn("Bridge client fcntl() failed");
		return;
	}
#endif

	BridgeClientConnection connection;
	connection.client = client;
	connection.run();
}

static void serverConnect() {
	// Initialize sockets
#if ARCH_WIN
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData)) {
		warn("Bridge server WSAStartup() failed");
		return;
	}
	defer({
		WSACleanup();
	});
#endif

	// Get address
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(BRIDGE_PORT);
#if ARCH_WIN
	addr.sin_addr.s_addr = inet_addr(BRIDGE_HOST);
#else
	inet_pton(AF_INET, BRIDGE_HOST, &addr.sin_addr);
#endif

	// Open socket
	int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (server < 0) {
		warn("Bridge server socket() failed");
		return;
	}
	defer({
		if (close(server)) {
			warn("Bridge server close() failed");
			return;
		}
		info("Bridge server closed");
	});

#if ARCH_MAC || ARCH_LIN
	int reuseAddrFlag = 1;
	setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &reuseAddrFlag, sizeof(reuseAddrFlag));
#endif

	// Bind socket to address
	if (bind(server, (struct sockaddr*) &addr, sizeof(addr))) {
		warn("Bridge server bind() failed");
		return;
	}

	// Listen for clients
	if (listen(server, 20)) {
		warn("Bridge server listen() failed");
		return;
	}
	info("Bridge server started");

	// Enable non-blocking
#if ARCH_WIN
	unsigned long blockingMode = 1;
	if (ioctlsocket(server, FIONBIO, &blockingMode)) {
		warn("Bridge server ioctlsocket() failed");
		return;
	}
#else
	int flags = fcntl(server, F_GETFL, 0);
	fcntl(server, F_SETFL, flags | O_NONBLOCK);
#endif

	// Accept clients
	while (serverRunning) {
		int client = accept(server, NULL, NULL);
		if (client < 0) {
			// Wait a bit before attempting to accept another client
			std::this_thread::sleep_for(std::chrono::duration<double>(0.1));
			continue;
		}

		// Launch client thread
		std::thread clientThread(clientRun, client);
		clientThread.detach();
	}
}

static void serverRun() {
	while (serverRunning) {
		std::this_thread::sleep_for(std::chrono::duration<double>(0.1));
		serverConnect();
	}
}

std::vector<int> BridgeMidiDriver::getInputDeviceIds() {
	std::vector<int> deviceIds;
	for (int i = 0; i < 16; i++) {
		deviceIds.push_back(i);
	}
	return deviceIds;
}

std::string BridgeMidiDriver::getInputDeviceName(int deviceId) {
	if (deviceId < 0)
		return "";
	return stringf("Port %d", deviceId + 1);
}

MidiInputDevice *BridgeMidiDriver::subscribeInputDevice(int deviceId, MidiInput *midiInput) {
	if (!(0 <= deviceId && deviceId < 16))
		return NULL;

	devices[deviceId].subscribe(midiInput);
	return &devices[deviceId];
}

void BridgeMidiDriver::unsubscribeInputDevice(int deviceId, MidiInput *midiInput) {
	if (!(0 <= deviceId && deviceId < 16))
		return;

	devices[deviceId].unsubscribe(midiInput);
}

void bridgeInit() {
	serverRunning = true;
	serverThread = std::thread(serverRun);

	driver = new BridgeMidiDriver();
	midiDriverAdd(BRIDGE_DRIVER, driver);
}

void bridgeDestroy() {
	serverRunning = false;
	serverThread.join();
}

void bridgeAudioSubscribe(int port, AudioIO *audio) {
	if (!(0 <= port && port < BRIDGE_NUM_PORTS))
		return;
	// Check if an Audio is already subscribed on the port
	if (audioListeners[port])
		return;
	audioListeners[port] = audio;
	if (connections[port])
		connections[port]->refreshAudio();
}

void bridgeAudioUnsubscribe(int port, AudioIO *audio) {
	if (!(0 <= port && port < BRIDGE_NUM_PORTS))
		return;
	if (audioListeners[port] != audio)
		return;
	audioListeners[port] = NULL;
}

} // namespace rack
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * 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.
 */

#ifndef otbTrainDecisionTree_hxx
#define otbTrainDecisionTree_hxx
#include "otbLearningApplicationBase.h"
#include "otbDecisionTreeMachineLearningModel.h"

namespace otb
{
namespace Wrapper
{

template <class TInputValue, class TOutputValue>
void
LearningApplicationBase<TInputValue,TOutputValue>
::InitDecisionTreeParams()
{
  AddChoice("classifier.dt", "Decision Tree classifier");
  SetParameterDescription("classifier.dt",
    "http://docs.opencv.org/modules/ml/doc/decision_trees.html");
  //MaxDepth
  AddParameter(ParameterType_Int, "classifier.dt.max", "Maximum depth of the tree");
#ifdef OTB_OPENCV_3
  SetParameterInt("classifier.dt.max",10);
#else
  SetParameterInt("classifier.dt.max",65535);
#endif
  SetParameterDescription("classifier.dt.max",
   "The training algorithm attempts to split each node while its depth is smaller "
   "than the maximum possible depth of the tree. The actual depth may be smaller "
   "if the other termination criteria are met, and/or if the tree is pruned.");

  //MinSampleCount
  AddParameter(ParameterType_Int, "classifier.dt.min", "Minimum number of samples in each node");
  SetParameterInt("classifier.dt.min",10);
  SetParameterDescription("classifier.dt.min",
    "If the number of samples in a node is smaller "
    "than this parameter, then this node will not be split.");

  //RegressionAccuracy
  AddParameter(ParameterType_Float, "classifier.dt.ra", "Termination criteria for regression tree");
  SetParameterFloat("classifier.dt.ra",0.01);
  SetParameterDescription("classifier.dt.ra",
    "If all absolute differences between an estimated value in a node "
    "and the values of the train samples in this node are smaller than this "
    "regression accuracy parameter, then the node will not be split further.");

  //UseSurrogates : don't need to be exposed !
  //SetParameterDescription("classifier.dt.sur","These splits allow working with missing data and compute variable importance correctly.");

  //MaxCategories
  AddParameter(ParameterType_Int, "classifier.dt.cat",
    "Cluster possible values of a categorical variable into K <= cat clusters to find a "
    "suboptimal split");
  SetParameterInt("classifier.dt.cat",10);
  SetParameterDescription("classifier.dt.cat",
      "Cluster possible values of a categorical variable into K <= cat clusters to find a "
      "suboptimal split.");

  //CVFolds
  AddParameter(ParameterType_Int, "classifier.dt.f", "K-fold cross-validations");
#ifdef OTB_OPENCV_3
  // disable cross validation by default (crash in opencv 3.2)
  SetParameterInt("classifier.dt.f",0);
#else
  SetParameterInt("classifier.dt.f",10);
#endif
  SetParameterDescription("classifier.dt.f",
    "If cv_folds > 1, then it prunes a tree with K-fold cross-validation where K "
    "is equal to cv_folds.");

  //Use1seRule
  AddParameter(ParameterType_Bool, "classifier.dt.r", "Set Use1seRule flag to false");
  SetParameterDescription("classifier.dt.r",
      "If true, then a pruning will be harsher. This will make a tree more compact and more "
      "resistant to the training data noise but a bit less accurate.");

  //TruncatePrunedTree
  AddParameter(ParameterType_Bool, "classifier.dt.t", "Set TruncatePrunedTree flag to false");
  SetParameterDescription("classifier.dt.t",
    "If true, then pruned branches are physically removed from the tree.");

  //Priors are not exposed.

}

template <class TInputValue, class TOutputValue>
void
LearningApplicationBase<TInputValue,TOutputValue>
::TrainDecisionTree(typename ListSampleType::Pointer trainingListSample,
                    typename TargetListSampleType::Pointer trainingLabeledListSample,
                    std::string modelPath)
{
  typedef otb::DecisionTreeMachineLearningModel<InputValueType, OutputValueType> DecisionTreeType;
  typename DecisionTreeType::Pointer classifier = DecisionTreeType::New();
  classifier->SetRegressionMode(this->m_RegressionFlag);
  classifier->SetInputListSample(trainingListSample);
  classifier->SetTargetListSample(trainingLabeledListSample);
  classifier->SetMaxDepth(GetParameterInt("classifier.dt.max"));
  classifier->SetMinSampleCount(GetParameterInt("classifier.dt.min"));
  classifier->SetRegressionAccuracy(GetParameterFloat("classifier.dt.ra"));
  classifier->SetMaxCategories(GetParameterInt("classifier.dt.cat"));
  classifier->SetCVFolds(GetParameterInt("classifier.dt.f"));
  if (GetParameterInt("classifier.dt.r"))
    {
    classifier->SetUse1seRule(false);
    }
  if (GetParameterInt("classifier.dt.t"))
    {
    classifier->SetTruncatePrunedTree(false);
    }
  classifier->Train();
  classifier->Save(modelPath);
}

} //end namespace wrapper
} //end namespace otb

#endif
<commit_msg>BUG: don't expose dt CVFold parameter for openCV 3<commit_after>/*
 * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * 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.
 */

#ifndef otbTrainDecisionTree_hxx
#define otbTrainDecisionTree_hxx
#include "otbLearningApplicationBase.h"
#include "otbDecisionTreeMachineLearningModel.h"

namespace otb
{
namespace Wrapper
{

template <class TInputValue, class TOutputValue>
void
LearningApplicationBase<TInputValue,TOutputValue>
::InitDecisionTreeParams()
{
  AddChoice("classifier.dt", "Decision Tree classifier");
  SetParameterDescription("classifier.dt",
    "http://docs.opencv.org/modules/ml/doc/decision_trees.html");
  //MaxDepth
  AddParameter(ParameterType_Int, "classifier.dt.max", "Maximum depth of the tree");
#ifdef OTB_OPENCV_3
  SetParameterInt("classifier.dt.max",10);
#else
  SetParameterInt("classifier.dt.max",65535);
#endif
  SetParameterDescription("classifier.dt.max",
   "The training algorithm attempts to split each node while its depth is smaller "
   "than the maximum possible depth of the tree. The actual depth may be smaller "
   "if the other termination criteria are met, and/or if the tree is pruned.");

  //MinSampleCount
  AddParameter(ParameterType_Int, "classifier.dt.min", "Minimum number of samples in each node");
  SetParameterInt("classifier.dt.min",10);
  SetParameterDescription("classifier.dt.min",
    "If the number of samples in a node is smaller "
    "than this parameter, then this node will not be split.");

  //RegressionAccuracy
  AddParameter(ParameterType_Float, "classifier.dt.ra", "Termination criteria for regression tree");
  SetParameterFloat("classifier.dt.ra",0.01);
  SetParameterDescription("classifier.dt.ra",
    "If all absolute differences between an estimated value in a node "
    "and the values of the train samples in this node are smaller than this "
    "regression accuracy parameter, then the node will not be split further.");

  //UseSurrogates : don't need to be exposed !
  //SetParameterDescription("classifier.dt.sur","These splits allow working with missing data and compute variable importance correctly.");

  //MaxCategories
  AddParameter(ParameterType_Int, "classifier.dt.cat",
    "Cluster possible values of a categorical variable into K <= cat clusters to find a "
    "suboptimal split");
  SetParameterInt("classifier.dt.cat",10);
  SetParameterDescription("classifier.dt.cat",
      "Cluster possible values of a categorical variable into K <= cat clusters to find a "
      "suboptimal split.");

  //CVFolds: only exposed for OPENCV 2 because it crashes in OpenCV 3
#ifndef OTB_OPENCV_3
  AddParameter(ParameterType_Int, "classifier.dt.f", "K-fold cross-validations");
  SetParameterInt("classifier.dt.f",10);
  SetParameterDescription("classifier.dt.f",
    "If cv_folds > 1, then it prunes a tree with K-fold cross-validation where K "
    "is equal to cv_folds.");
#endif

  //Use1seRule
  AddParameter(ParameterType_Bool, "classifier.dt.r", "Set Use1seRule flag to false");
  SetParameterDescription("classifier.dt.r",
      "If true, then a pruning will be harsher. This will make a tree more compact and more "
      "resistant to the training data noise but a bit less accurate.");

  //TruncatePrunedTree
  AddParameter(ParameterType_Bool, "classifier.dt.t", "Set TruncatePrunedTree flag to false");
  SetParameterDescription("classifier.dt.t",
    "If true, then pruned branches are physically removed from the tree.");

  //Priors are not exposed.

}

template <class TInputValue, class TOutputValue>
void
LearningApplicationBase<TInputValue,TOutputValue>
::TrainDecisionTree(typename ListSampleType::Pointer trainingListSample,
                    typename TargetListSampleType::Pointer trainingLabeledListSample,
                    std::string modelPath)
{
  typedef otb::DecisionTreeMachineLearningModel<InputValueType, OutputValueType> DecisionTreeType;
  typename DecisionTreeType::Pointer classifier = DecisionTreeType::New();
  classifier->SetRegressionMode(this->m_RegressionFlag);
  classifier->SetInputListSample(trainingListSample);
  classifier->SetTargetListSample(trainingLabeledListSample);
  classifier->SetMaxDepth(GetParameterInt("classifier.dt.max"));
  classifier->SetMinSampleCount(GetParameterInt("classifier.dt.min"));
  classifier->SetRegressionAccuracy(GetParameterFloat("classifier.dt.ra"));
  classifier->SetMaxCategories(GetParameterInt("classifier.dt.cat"));
  //CVFolds is only exposed for OPENCV 2 because it crashes in OpenCV 3
#ifndef OTB_OPENCV_3
  classifier->SetCVFolds(GetParameterInt("classifier.dt.f"));
#endif
  if (GetParameterInt("classifier.dt.r"))
    {
    classifier->SetUse1seRule(false);
    }
  if (GetParameterInt("classifier.dt.t"))
    {
    classifier->SetTruncatePrunedTree(false);
    }
  classifier->Train();
  classifier->Save(modelPath);
}

} //end namespace wrapper
} //end namespace otb

#endif
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.txt
 *
 *  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 "itkLevelSetDenseImageBase.h"
#include "itkImageRegionIteratorWithIndex.h"

#include "itkLevelSetTestFunction.h"

/**
 * \class ToleranceChecker
 * \brief Compare values to see if they are within tolerance.
 */
template< typename RealType >
class ToleranceChecker
{
public:
  ToleranceChecker(): m_Tolerance( 1e-8 )
  {}

  bool IsOutsideTolerance( const RealType & value, const RealType & theoreticalValue ) const
    {
    if( this->GetFractionalError( value, theoreticalValue ) > m_Tolerance )
      {
      return true;
      }
    return false;
    }

  RealType GetFractionalError( const RealType & value, const RealType & theoreticalValue ) const
    {
    RealType fractionalError = vnl_math_abs( theoreticalValue - value ) /
      ( vnl_math_abs( theoreticalValue ) + 20* vnl_math::eps );
    return fractionalError;
    }

  /** Set fractional tolerance. */
  void SetTolerance( const RealType & tolerance )
    {
    m_Tolerance = tolerance;
    }

private:
  RealType m_Tolerance;

};

int itkLevelSetDenseImageBaseTest( int , char* [] )
{
  const unsigned int Dimension = 2;

  typedef float PixelType;

  typedef itk::Image< PixelType, Dimension >        ImageType;
  typedef itk::LevelSetDenseImageBase< ImageType >  LevelSetType;

  ImageType::IndexType index;
  index[0] = 0;
  index[1] = 0;

  ImageType::SizeType size;
  size[0] = 10;
  size[1] = 20;

  ImageType::RegionType region;
  region.SetIndex( index );
  region.SetSize( size );

  PixelType zeroValue = 0.;

  ImageType::SpacingType spacing;
  spacing[0] = 0.02 / size[0];
  spacing[1] = 0.02 / size[1];

  ImageType::PointType origin;
  origin[0] = 3.99;
  origin[1] = 3.99;

  ImageType::Pointer input = ImageType::New();
  input->SetRegions( region );
  input->SetSpacing( spacing );
  input->SetOrigin(  origin );
  input->Allocate();
  input->FillBuffer( zeroValue );

  itk::ImageRegionIteratorWithIndex< ImageType > it( input,
                                              input->GetLargestPossibleRegion() );

  it.GoToBegin();

  ImageType::IndexType idx;
  ImageType::PointType pt;

  typedef itk::LevelSetTestFunction< PixelType > TestFunctionType;
  TestFunctionType::Pointer testFunction = TestFunctionType::New();

  while( !it.IsAtEnd() )
    {
    idx = it.GetIndex();
    input->TransformIndexToPhysicalPoint( idx, pt );

    it.Set( testFunction->Evaluate( pt ) );
    ++it;
    }

  LevelSetType::Pointer level_set = LevelSetType::New();
  level_set->SetImage( input );

  idx[0] = 9;
  idx[1] = 18;
  input->TransformIndexToPhysicalPoint( idx, pt );
  LevelSetType::OutputType theoreticalValue = testFunction->Evaluate( pt );
  LevelSetType::OutputType value = level_set->Evaluate( idx );

  ToleranceChecker< double > toleranceChecker;

  toleranceChecker.SetTolerance( 1e-8 );
  it.GoToBegin();
  while( !it.IsAtEnd() )
    {
    idx = it.GetIndex();
    input->TransformIndexToPhysicalPoint( idx, pt );

    theoreticalValue = testFunction->Evaluate( pt );
    value            = level_set->Evaluate( idx );
    if( toleranceChecker.IsOutsideTolerance( value, theoreticalValue ) )
      {
      std::cout << "Index:" << idx << " *EvaluateTestFail* " << value << " != "
                << theoreticalValue << std::endl;
      return EXIT_FAILURE;
      }

    ++it;
    }

  LevelSetType::GradientType gradient;
  LevelSetType::GradientType theoreticalGradient;

  toleranceChecker.SetTolerance( 0.1 );
  it.GoToBegin();
  while( !it.IsAtEnd() )
    {
    idx = it.GetIndex();
    input->TransformIndexToPhysicalPoint( idx, pt );

    theoreticalGradient = testFunction->EvaluateGradient( pt );
    gradient            = level_set->EvaluateGradient( idx );
    if( toleranceChecker.IsOutsideTolerance( gradient[0], theoreticalGradient[0] ) ||
        toleranceChecker.IsOutsideTolerance( gradient[1], theoreticalGradient[1] ) )
      {
      std::cout << "Index:" << idx << " Point: " << pt
        << " Error: [" << toleranceChecker.GetFractionalError( gradient[0], theoreticalGradient[0] )
        << ',' << toleranceChecker.GetFractionalError( gradient[1], theoreticalGradient[1] ) << "] "
        <<" *EvaluateGradientTestFail* " << gradient << " != " << theoreticalGradient << std::endl;
      return EXIT_FAILURE;
      }

    ++it;
    }

  /** \todo more thorough testing as with the gradient above for hessian,
   * laplacian, gradient norm. */
  idx[0] = 9;
  idx[1] = 18;
  input->TransformIndexToPhysicalPoint( idx, pt );
  LevelSetType::HessianType hessian = level_set->EvaluateHessian( idx );
  std::cout << "hessian = " << std::endl << hessian << std::endl;

  if ( vnl_math_abs( vnl_math_abs( hessian[0][0] ) - 499.998 ) / 499.998 > 5e-2 )
    {
    std::cout << idx << " *HessianTestFail* " << vnl_math_abs( hessian[0][0] ) << " != "
              << vnl_math_abs( hessian[1][1] ) << std::endl;
    return EXIT_FAILURE;
    }

  LevelSetType::OutputRealType laplacian = level_set->EvaluateLaplacian( idx );
  std::cout << "laplacian = " << laplacian << std::endl;

  LevelSetType::OutputRealType gradientnorm = level_set->EvaluateGradientNorm( idx );
  std::cout <<"gradient norm = " << gradientnorm << std::endl;

  if( vnl_math_abs( 1 - gradientnorm ) > 5e-2 )
    {
    std::cout << idx << " *GradientNormFail* " << gradientnorm << " != "
              << 1 << std::endl;
    return EXIT_FAILURE;
    }

  LevelSetType::OutputRealType meancurvature = level_set->EvaluateMeanCurvature( idx );
  std::cout <<"mean curvature = " << meancurvature << std::endl;

  return EXIT_SUCCESS;
}
<commit_msg>BUG: LevelSetDenseImage test recognize zero values.<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.txt
 *
 *  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 "itkLevelSetDenseImageBase.h"
#include "itkImageRegionIteratorWithIndex.h"

#include "itkLevelSetTestFunction.h"

/**
 * \class ToleranceChecker
 * \brief Compare values to see if they are within tolerance.
 */
template< typename RealType >
class ToleranceChecker
{
public:
  ToleranceChecker(): m_Tolerance( 1e-8 )
  {}

  bool IsOutsideTolerance( const RealType & value, const RealType & theoreticalValue ) const
    {
    // ignore if they are both effectively zero
    if( vnl_math_max( vnl_math_abs( value ), vnl_math_abs( theoreticalValue ) ) <  50 * vnl_math::eps )
      {
      return false;
      }
    if( this->GetFractionalError( value, theoreticalValue ) > m_Tolerance )
      {
      return true;
      }
    return false;
    }

  RealType GetFractionalError( const RealType & value, const RealType & theoreticalValue ) const
    {
    RealType fractionalError = vnl_math_abs( theoreticalValue - value ) /
      ( vnl_math_abs( theoreticalValue ) + 20* vnl_math::eps );
    return fractionalError;
    }

  /** Set fractional tolerance. */
  void SetTolerance( const RealType & tolerance )
    {
    m_Tolerance = tolerance;
    }

private:
  RealType m_Tolerance;

};

int itkLevelSetDenseImageBaseTest( int , char* [] )
{
  const unsigned int Dimension = 2;

  typedef float PixelType;

  typedef itk::Image< PixelType, Dimension >        ImageType;
  typedef itk::LevelSetDenseImageBase< ImageType >  LevelSetType;

  ImageType::IndexType index;
  index[0] = 0;
  index[1] = 0;

  ImageType::SizeType size;
  size[0] = 10;
  size[1] = 20;

  ImageType::RegionType region;
  region.SetIndex( index );
  region.SetSize( size );

  PixelType zeroValue = 0.;

  ImageType::SpacingType spacing;
  spacing[0] = 0.02 / size[0];
  spacing[1] = 0.02 / size[1];

  ImageType::PointType origin;
  origin[0] = 3.99;
  origin[1] = 3.99;

  ImageType::Pointer input = ImageType::New();
  input->SetRegions( region );
  input->SetSpacing( spacing );
  input->SetOrigin(  origin );
  input->Allocate();
  input->FillBuffer( zeroValue );

  itk::ImageRegionIteratorWithIndex< ImageType > it( input,
                                              input->GetLargestPossibleRegion() );

  it.GoToBegin();

  ImageType::IndexType idx;
  ImageType::PointType pt;

  typedef itk::LevelSetTestFunction< PixelType > TestFunctionType;
  TestFunctionType::Pointer testFunction = TestFunctionType::New();

  while( !it.IsAtEnd() )
    {
    idx = it.GetIndex();
    input->TransformIndexToPhysicalPoint( idx, pt );

    it.Set( testFunction->Evaluate( pt ) );
    ++it;
    }

  LevelSetType::Pointer level_set = LevelSetType::New();
  level_set->SetImage( input );

  idx[0] = 9;
  idx[1] = 18;
  input->TransformIndexToPhysicalPoint( idx, pt );
  LevelSetType::OutputType theoreticalValue = testFunction->Evaluate( pt );
  LevelSetType::OutputType value = level_set->Evaluate( idx );

  ToleranceChecker< double > toleranceChecker;

  toleranceChecker.SetTolerance( 1e-8 );
  it.GoToBegin();
  while( !it.IsAtEnd() )
    {
    idx = it.GetIndex();
    input->TransformIndexToPhysicalPoint( idx, pt );

    theoreticalValue = testFunction->Evaluate( pt );
    value            = level_set->Evaluate( idx );
    if( toleranceChecker.IsOutsideTolerance( value, theoreticalValue ) )
      {
      std::cout << "Index:" << idx << " *EvaluateTestFail* " << value << " != "
                << theoreticalValue << std::endl;
      return EXIT_FAILURE;
      }

    ++it;
    }

  LevelSetType::GradientType gradient;
  LevelSetType::GradientType theoreticalGradient;

  toleranceChecker.SetTolerance( 0.1 );
  it.GoToBegin();
  while( !it.IsAtEnd() )
    {
    idx = it.GetIndex();
    input->TransformIndexToPhysicalPoint( idx, pt );

    theoreticalGradient = testFunction->EvaluateGradient( pt );
    gradient            = level_set->EvaluateGradient( idx );
    if( toleranceChecker.IsOutsideTolerance( gradient[0], theoreticalGradient[0] ) ||
        toleranceChecker.IsOutsideTolerance( gradient[1], theoreticalGradient[1] ) )
      {
      std::cout << "Index:" << idx << " Point: " << pt
        << " Error: [" << toleranceChecker.GetFractionalError( gradient[0], theoreticalGradient[0] )
        << ',' << toleranceChecker.GetFractionalError( gradient[1], theoreticalGradient[1] ) << "] "
        <<" *EvaluateGradientTestFail* " << gradient << " != " << theoreticalGradient << std::endl;
      return EXIT_FAILURE;
      }

    ++it;
    }

  /** \todo more thorough testing as with the gradient above for hessian,
   * laplacian, gradient norm. */
  idx[0] = 9;
  idx[1] = 18;
  input->TransformIndexToPhysicalPoint( idx, pt );
  LevelSetType::HessianType hessian = level_set->EvaluateHessian( idx );
  std::cout << "hessian = " << std::endl << hessian << std::endl;

  if ( vnl_math_abs( vnl_math_abs( hessian[0][0] ) - 499.998 ) / 499.998 > 5e-2 )
    {
    std::cout << idx << " *HessianTestFail* " << vnl_math_abs( hessian[0][0] ) << " != "
              << vnl_math_abs( hessian[1][1] ) << std::endl;
    return EXIT_FAILURE;
    }

  LevelSetType::OutputRealType laplacian = level_set->EvaluateLaplacian( idx );
  std::cout << "laplacian = " << laplacian << std::endl;

  LevelSetType::OutputRealType gradientnorm = level_set->EvaluateGradientNorm( idx );
  std::cout <<"gradient norm = " << gradientnorm << std::endl;

  if( vnl_math_abs( 1 - gradientnorm ) > 5e-2 )
    {
    std::cout << idx << " *GradientNormFail* " << gradientnorm << " != "
              << 1 << std::endl;
    return EXIT_FAILURE;
    }

  LevelSetType::OutputRealType meancurvature = level_set->EvaluateMeanCurvature( idx );
  std::cout <<"mean curvature = " << meancurvature << std::endl;

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include <array>

#include "builtin.hh"
#include "number.hh"
#include "procedure.hh"
#include "lisp_ptr.hh"
#include "eval.hh"
#include "builtin_util.hh"
#include "printer.hh"

using namespace std;
using namespace Procedure;

namespace {

void plus_2(){
  auto args = pick_args<2>();

  VM.return_value = {};

  Number* n1 = args[0].get<Number*>();
  if(!n1){
    fprintf(stderr, "native func '+': first arg is not number! %s\n",
            stringify(args[0].tag()));
    return;
  }

  Number* n2 = args[1].get<Number*>();
  if(!n2){
    fprintf(stderr, "native func '+': second arg is not number! %s\n",
            stringify(args[1].tag()));
    return;
  }

  Number* newn = new Number(n1->get<long>() + n2->get<long>());
  VM.return_value = Lisp_ptr(newn);
}

template <Ptr_tag p>
void type_check_pred(){
  auto arg = pick_args_1();
  VM.return_value = Lisp_ptr{arg.tag() == p};
}

void type_check_pair(){
  auto arg = pick_args_1();
  VM.return_value = Lisp_ptr{(arg.tag() == Ptr_tag::cons) && !nullp(arg)};
}

void type_check_procedure(){
  auto arg = pick_args_1();
  VM.return_value = Lisp_ptr{(arg.tag() == Ptr_tag::i_procedure)
                             || (arg.tag() == Ptr_tag::n_procedure)};
}

Lisp_ptr whole_macro_or_expand(Cons* c){
  if(!c->cdr() || nullp(c->cdr())){
    return c->car();
  }

  const auto if_sym = intern(VM.symtable, "if");
  auto else_clause = whole_macro_or_expand(c->cdr().get<Cons*>());

  return Lisp_ptr(new Cons(Lisp_ptr(if_sym),
         Lisp_ptr(new Cons(c->car(),
         Lisp_ptr(new Cons(Lisp_ptr(vm_op_nop),
         Lisp_ptr(new Cons(else_clause, Cons::NIL))))))));
}

Lisp_ptr whole_macro_and_expand(Cons* c){
  if(!c->cdr() || nullp(c->cdr())){
    return c->car();
  }

  const auto if_sym = intern(VM.symtable, "if");
  auto then_clause = whole_macro_and_expand(c->cdr().get<Cons*>());

  return Lisp_ptr(new Cons(Lisp_ptr(if_sym),
         Lisp_ptr(new Cons(c->car(),
         Lisp_ptr(new Cons(then_clause,
         Lisp_ptr(new Cons(Lisp_ptr(vm_op_nop), Cons::NIL))))))));
}

Lisp_ptr whole_macro_cond_expand(Cons* head){
  if(!head) return {}; // unspecified in R5RS.

  auto clause = head->car();
  if(!clause.get<Cons*>()){
    fprintf(stderr, "macro cond: informal clause syntax! '");
    print(stderr, head->car());
    fprintf(stderr, "'\n");
    return {};
  }

  Lisp_ptr test_form;
  Lisp_ptr then_form;

  int ret = bind_cons_list(clause,
                           [&](Cons* c){
                             test_form = c->car();
                             if(nullp(c->cdr())){
                               then_form = Lisp_ptr(vm_op_nop);
                             }
                           },
                           [&](Cons* c){
                             if(auto sym = c->car().get<Symbol*>()){
                               if(sym->name() == "=>"){
                                 fprintf(stderr, "macto cond: sorry, cond's => syntax is not implemented..\n");
                                 then_form = {};
                                 return;
                               }
                             }
                             then_form = Lisp_ptr(new Cons(Lisp_ptr(intern(VM.symtable, "begin")), Lisp_ptr(c)));
                           });
  assert(ret >= 1); // should be handled by previous tests.
  (void)ret;

  if(auto sym = test_form.get<Symbol*>()){
    if(sym->name() == "else"){
      return then_form;
    }
  }

  const auto if_sym = intern(VM.symtable, "if");
  auto else_form = whole_macro_cond_expand(head->cdr().get<Cons*>());

  return Lisp_ptr(new Cons(Lisp_ptr(if_sym),
         Lisp_ptr(new Cons(test_form,
         Lisp_ptr(new Cons(then_form,
         Lisp_ptr(new Cons(else_form, Cons::NIL))))))));
}

template<typename T, typename Expander>
inline
void whole_macro_conditional(T default_value, Expander e){
  auto arg = pick_args_1();
  if(!arg) return;

  Cons* head;

  int len = bind_cons_list(arg,
                           [](Cons*){},
                           [&](Cons* c){
                             head = c;
                           });
  if(len < 2){
    VM.return_value = Lisp_ptr(default_value);
    return;
  }

  VM.return_value = e(head);
}

void whole_macro_and(){
  whole_macro_conditional(true, whole_macro_and_expand);
}

void whole_macro_or(){
  whole_macro_conditional(false, whole_macro_or_expand);
}

void whole_macro_cond(){
  whole_macro_conditional(Lisp_ptr{}, whole_macro_cond_expand);
}

bool eq_internal(Lisp_ptr a, Lisp_ptr b){
  if(a.tag() != b.tag()) return false;

  if(a.tag() == Ptr_tag::boolean){
    return a.get<bool>() == b.get<bool>();
  }else if(a.tag() == Ptr_tag::character){
     // this can be moved into eqv? in R5RS, but char is contained in Lisp_ptr.
    return a.get<char>() == b.get<char>();
  }else{
    return a.get<void*>() == b.get<void*>();
  }
}

void eq(){
  auto args = pick_args<2>();

  VM.return_value = Lisp_ptr{eq_internal(args[0], args[1])};
}

void eqv(){
  auto args = pick_args<2>();

  if(args[0].tag() == Ptr_tag::number && args[1].tag() == Ptr_tag::number){
    VM.return_value = Lisp_ptr{eqv(*args[0].get<Number*>(), *args[1].get<Number*>())};
  }else{
    VM.return_value = Lisp_ptr{eq_internal(args[0], args[1])};
  }
}

constexpr struct Entry {
  const char* name;
  const NProcedure func;

  constexpr Entry(const char* n, const NProcedure& f)
    : name(n), func(f){}
} builtin_func[] = {
  // syntaxes
  {"quote", {
      whole_function_quote,
      Calling::whole_function, {1, false}}},
  {"lambda", {
      whole_function_lambda,
      Calling::whole_function, {1, true}}},
  {"if", {
      whole_function_if,
      Calling::whole_function, {3, false}}},
  {"set!", {
      whole_function_set,
      Calling::whole_function, {2, false}}},
  {"define", {
      whole_function_define,
      Calling::whole_function, {2, true}}},
  {"quasiquote", {
      whole_function_quasiquote,
      Calling::whole_function, {1, false}}},
  {"begin", {
      whole_function_begin,
      Calling::whole_function, {1, true}}},

  {"cond", {
      whole_macro_cond,
      Calling::whole_macro, {1, true}}},
  {"case", {
      whole_function_unimplemented,
      Calling::whole_function, {0, true}}},
  {"and", {
      whole_macro_and,
      Calling::whole_macro, {0, true}}},
  {"or", {
      whole_macro_or,
      Calling::whole_macro, {0, true}}},
  {"let", {
      whole_function_let,
      Calling::whole_function, {1, true}}},
  {"let*", {
      whole_function_let_star,
      Calling::whole_function, {1, true}}},
  {"letrec", {
      whole_function_letrec,
      Calling::whole_function, {1, true}}},
  {"do", {
      whole_function_unimplemented,
      Calling::whole_function, {0, true}}},
  {"delay", {
      whole_function_unimplemented,
      Calling::whole_function, {0, true}}},

  {"unquote", {
      whole_function_pass_through,
      Calling::whole_function, {0, true}}},
  {"unquote-splicing", {
      whole_function_pass_through,
      Calling::whole_function, {0, true}}},
  {"else", {
      whole_function_error,
      Calling::whole_function, {0, false}}},
  {"=>", {
      whole_function_error,
      Calling::whole_function, {0, false}}},

  // functions
  {"+", {
      plus_2,
      Calling::function, {2, true}}},
  {"list", {
      procedure_list,
      Calling::function, {1, true}}},
  {"list*", {
      procedure_list_star,
      Calling::function, {1, true}}},
  {"vector", {
      procedure_vector,
      Calling::function, {1, true}}},
  {"boolean?", {
      type_check_pred<Ptr_tag::boolean>,
      Calling::function, {1, false}}},
  {"symbol?", {
      type_check_pred<Ptr_tag::symbol>,
      Calling::function, {1, false}}},
  {"char?", {
      type_check_pred<Ptr_tag::character>,
      Calling::function, {1, false}}},
  {"vector?", {
      type_check_pred<Ptr_tag::vector>,
      Calling::function, {1, false}}},
  {"procedure?", {
      type_check_procedure,
      Calling::function, {1, false}}},
  {"pair?", {
      type_check_pair,
      Calling::function, {1, false}}},
  {"number?", {
      type_check_pred<Ptr_tag::number>,
      Calling::function, {1, false}}},
  {"string?", {
      type_check_pred<Ptr_tag::string>,
      Calling::function, {1, false}}},
  {"port?", {
      type_check_pred<Ptr_tag::port>,
      Calling::function, {1, false}}},
  {"eqv?", {
      eqv,
      Calling::function, {2, false}}},
  {"eq?", {
      eq,
      Calling::function, {2, false}}}
};

} //namespace

void install_builtin(){
  for(auto& e : builtin_func){
    VM.set(intern(VM.symtable, e.name), Lisp_ptr{&e.func});
  }
}
<commit_msg>bitly change in eql<commit_after>#include <array>

#include "builtin.hh"
#include "number.hh"
#include "procedure.hh"
#include "lisp_ptr.hh"
#include "eval.hh"
#include "builtin_util.hh"
#include "printer.hh"

using namespace std;
using namespace Procedure;

namespace {

void plus_2(){
  auto args = pick_args<2>();

  VM.return_value = {};

  Number* n1 = args[0].get<Number*>();
  if(!n1){
    fprintf(stderr, "native func '+': first arg is not number! %s\n",
            stringify(args[0].tag()));
    return;
  }

  Number* n2 = args[1].get<Number*>();
  if(!n2){
    fprintf(stderr, "native func '+': second arg is not number! %s\n",
            stringify(args[1].tag()));
    return;
  }

  Number* newn = new Number(n1->get<long>() + n2->get<long>());
  VM.return_value = Lisp_ptr(newn);
}

template <Ptr_tag p>
void type_check_pred(){
  auto arg = pick_args_1();
  VM.return_value = Lisp_ptr{arg.tag() == p};
}

void type_check_pair(){
  auto arg = pick_args_1();
  VM.return_value = Lisp_ptr{(arg.tag() == Ptr_tag::cons) && !nullp(arg)};
}

void type_check_procedure(){
  auto arg = pick_args_1();
  VM.return_value = Lisp_ptr{(arg.tag() == Ptr_tag::i_procedure)
                             || (arg.tag() == Ptr_tag::n_procedure)};
}

Lisp_ptr whole_macro_or_expand(Cons* c){
  if(!c->cdr() || nullp(c->cdr())){
    return c->car();
  }

  const auto if_sym = intern(VM.symtable, "if");
  auto else_clause = whole_macro_or_expand(c->cdr().get<Cons*>());

  return Lisp_ptr(new Cons(Lisp_ptr(if_sym),
         Lisp_ptr(new Cons(c->car(),
         Lisp_ptr(new Cons(Lisp_ptr(vm_op_nop),
         Lisp_ptr(new Cons(else_clause, Cons::NIL))))))));
}

Lisp_ptr whole_macro_and_expand(Cons* c){
  if(!c->cdr() || nullp(c->cdr())){
    return c->car();
  }

  const auto if_sym = intern(VM.symtable, "if");
  auto then_clause = whole_macro_and_expand(c->cdr().get<Cons*>());

  return Lisp_ptr(new Cons(Lisp_ptr(if_sym),
         Lisp_ptr(new Cons(c->car(),
         Lisp_ptr(new Cons(then_clause,
         Lisp_ptr(new Cons(Lisp_ptr(vm_op_nop), Cons::NIL))))))));
}

Lisp_ptr whole_macro_cond_expand(Cons* head){
  if(!head) return {}; // unspecified in R5RS.

  auto clause = head->car();
  if(!clause.get<Cons*>()){
    fprintf(stderr, "macro cond: informal clause syntax! '");
    print(stderr, head->car());
    fprintf(stderr, "'\n");
    return {};
  }

  Lisp_ptr test_form;
  Lisp_ptr then_form;

  int ret = bind_cons_list(clause,
                           [&](Cons* c){
                             test_form = c->car();
                             if(nullp(c->cdr())){
                               then_form = Lisp_ptr(vm_op_nop);
                             }
                           },
                           [&](Cons* c){
                             if(auto sym = c->car().get<Symbol*>()){
                               if(sym->name() == "=>"){
                                 fprintf(stderr, "macto cond: sorry, cond's => syntax is not implemented..\n");
                                 then_form = {};
                                 return;
                               }
                             }
                             then_form = Lisp_ptr(new Cons(Lisp_ptr(intern(VM.symtable, "begin")), Lisp_ptr(c)));
                           });
  assert(ret >= 1); // should be handled by previous tests.
  (void)ret;

  if(auto sym = test_form.get<Symbol*>()){
    if(sym->name() == "else"){
      return then_form;
    }
  }

  const auto if_sym = intern(VM.symtable, "if");
  auto else_form = whole_macro_cond_expand(head->cdr().get<Cons*>());

  return Lisp_ptr(new Cons(Lisp_ptr(if_sym),
         Lisp_ptr(new Cons(test_form,
         Lisp_ptr(new Cons(then_form,
         Lisp_ptr(new Cons(else_form, Cons::NIL))))))));
}

template<typename T, typename Expander>
inline
void whole_macro_conditional(T default_value, Expander e){
  auto arg = pick_args_1();
  if(!arg) return;

  Cons* head;

  int len = bind_cons_list(arg,
                           [](Cons*){},
                           [&](Cons* c){
                             head = c;
                           });
  if(len < 2){
    VM.return_value = Lisp_ptr(default_value);
    return;
  }

  VM.return_value = e(head);
}

void whole_macro_and(){
  whole_macro_conditional(true, whole_macro_and_expand);
}

void whole_macro_or(){
  whole_macro_conditional(false, whole_macro_or_expand);
}

void whole_macro_cond(){
  whole_macro_conditional(Lisp_ptr{}, whole_macro_cond_expand);
}

bool eq_internal(Lisp_ptr a, Lisp_ptr b){
  if(a.tag() != b.tag()) return false;

  if(a.tag() == Ptr_tag::boolean){
    return a.get<bool>() == b.get<bool>();
  }else if(a.tag() == Ptr_tag::character){
     // this can be moved into eqv? in R5RS, but char is contained in Lisp_ptr.
    return a.get<char>() == b.get<char>();
  }else{
    return a.get<void*>() == b.get<void*>();
  }
}

bool eqv_internal(Lisp_ptr a, Lisp_ptr b){
  if(a.tag() == Ptr_tag::number && b.tag() == Ptr_tag::number){
    return eqv(*a.get<Number*>(), *b.get<Number*>());
  }else{
    return eq_internal(a, b);
  }
}

void eq(){
  auto args = pick_args<2>();
  VM.return_value = Lisp_ptr{eq_internal(args[0], args[1])};
}

void eqv(){
  auto args = pick_args<2>();
  VM.return_value = Lisp_ptr{eqv_internal(args[0], args[1])};
}

constexpr struct Entry {
  const char* name;
  const NProcedure func;

  constexpr Entry(const char* n, const NProcedure& f)
    : name(n), func(f){}
} builtin_func[] = {
  // syntaxes
  {"quote", {
      whole_function_quote,
      Calling::whole_function, {1, false}}},
  {"lambda", {
      whole_function_lambda,
      Calling::whole_function, {1, true}}},
  {"if", {
      whole_function_if,
      Calling::whole_function, {3, false}}},
  {"set!", {
      whole_function_set,
      Calling::whole_function, {2, false}}},
  {"define", {
      whole_function_define,
      Calling::whole_function, {2, true}}},
  {"quasiquote", {
      whole_function_quasiquote,
      Calling::whole_function, {1, false}}},
  {"begin", {
      whole_function_begin,
      Calling::whole_function, {1, true}}},

  {"cond", {
      whole_macro_cond,
      Calling::whole_macro, {1, true}}},
  {"case", {
      whole_function_unimplemented,
      Calling::whole_function, {0, true}}},
  {"and", {
      whole_macro_and,
      Calling::whole_macro, {0, true}}},
  {"or", {
      whole_macro_or,
      Calling::whole_macro, {0, true}}},
  {"let", {
      whole_function_let,
      Calling::whole_function, {1, true}}},
  {"let*", {
      whole_function_let_star,
      Calling::whole_function, {1, true}}},
  {"letrec", {
      whole_function_letrec,
      Calling::whole_function, {1, true}}},
  {"do", {
      whole_function_unimplemented,
      Calling::whole_function, {0, true}}},
  {"delay", {
      whole_function_unimplemented,
      Calling::whole_function, {0, true}}},

  {"unquote", {
      whole_function_pass_through,
      Calling::whole_function, {0, true}}},
  {"unquote-splicing", {
      whole_function_pass_through,
      Calling::whole_function, {0, true}}},
  {"else", {
      whole_function_error,
      Calling::whole_function, {0, false}}},
  {"=>", {
      whole_function_error,
      Calling::whole_function, {0, false}}},

  // functions
  {"+", {
      plus_2,
      Calling::function, {2, true}}},
  {"list", {
      procedure_list,
      Calling::function, {1, true}}},
  {"list*", {
      procedure_list_star,
      Calling::function, {1, true}}},
  {"vector", {
      procedure_vector,
      Calling::function, {1, true}}},
  {"boolean?", {
      type_check_pred<Ptr_tag::boolean>,
      Calling::function, {1, false}}},
  {"symbol?", {
      type_check_pred<Ptr_tag::symbol>,
      Calling::function, {1, false}}},
  {"char?", {
      type_check_pred<Ptr_tag::character>,
      Calling::function, {1, false}}},
  {"vector?", {
      type_check_pred<Ptr_tag::vector>,
      Calling::function, {1, false}}},
  {"procedure?", {
      type_check_procedure,
      Calling::function, {1, false}}},
  {"pair?", {
      type_check_pair,
      Calling::function, {1, false}}},
  {"number?", {
      type_check_pred<Ptr_tag::number>,
      Calling::function, {1, false}}},
  {"string?", {
      type_check_pred<Ptr_tag::string>,
      Calling::function, {1, false}}},
  {"port?", {
      type_check_pred<Ptr_tag::port>,
      Calling::function, {1, false}}},
  {"eqv?", {
      eqv,
      Calling::function, {2, false}}},
  {"eq?", {
      eq,
      Calling::function, {2, false}}}
};

} //namespace

void install_builtin(){
  for(auto& e : builtin_func){
    VM.set(intern(VM.symtable, e.name), Lisp_ptr{&e.func});
  }
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <cmath>
#include <vector>
#include <QFile>
#include <QRegularExpression>
#include "ToolBase.h"
#include "Exceptions.h"
#include "Helper.h"
#include "VcfFile.h"
#include "NeedlemanWunsch.hpp"

using namespace std;

class ConcreteTool: public ToolBase
{
	Q_OBJECT

public:
	ConcreteTool(int& argc, char *argv[])
		: ToolBase(argc, argv)
	{
	}

	virtual void setup()
	{
		setDescription("Breaks complex variants into primitives preserving INFO/SAMPLE fields.");
		setExtendedDescription(QStringList() << "Multi-allelic variants are ignored, since we assume they have already been split, e.g. with VcfBreakMulti."
											 << "Complex variants that are decomposed, are flagged with a BBC (before break-complex) info entry.");
		addInfile("in", "Input VCF file. If unset, reads from STDIN.", true, true);
		addOutfile("out", "Output VCF list. If unset, writes to STDOUT.", true, true);
		addOutfile("stats", "Ouptuts statistics. If unset, writes to STDERR.", true, true);
		addFlag("keep_mnps", "Write out MNPs unchanged.");
		addFlag("no_tag", "Skip annotation of decomposed variants with BBC in INFO field.");

		changeLog(2018, 11, 30, "Initial implementation.");
	}

	/**
	 * @brief main
	 * This program breaks complex variants into several lines. It is inspired by vt's decompose_suballelic and vcflib's vcfallelicprimitives.
	 * However this program will only deal with complex variants. It will not consinder multi-allelic variants.
	 * We use the classification procedure as described in the VT wiki here https://genome.sph.umich.edu/wiki/Variant_classification
	 * Local aligment of variants is done using Needleman-Wunsch as implemented in edlib. See https://doi.org/10.1093/bioinformatics/btw753
	 */
	virtual void main()
	{
		QString in = getInfile("in");
		QString out = getOutfile("out");
		QString stats = getOutfile("stats");
		bool keep_mnps = getFlag("keep_mnps");
		bool no_tag = getFlag("no_tag");
		if(in!="" && in==out)
		{
			THROW(ArgumentException, "Input and output files must be different when streaming!");
		}
		if(stats!="" && stats==out)
		{
			THROW(ArgumentException, "Error and output files must be different when streaming!");
		}

		QSharedPointer<QFile> in_p = Helper::openFileForReading(in, true);
		QSharedPointer<QFile> out_p = Helper::openFileForWriting(out, true);

		// Statistics
		bool inserted_info = false;
		unsigned int number_of_additional_snps = 0;
		unsigned int number_of_biallelic_block_substitutions = 0;
		unsigned int number_of_new_variants = 0;
		unsigned int number_of_variants = 0;

		using Aligment = tuple<QByteArray, QByteArray, int>; // Describes an aligment of REF, ALT, OFFSET

		while(!in_p->atEnd())
		{
			QByteArray line = in_p->readLine();
			// Skip empty lines and headers
			if (line.trimmed().isEmpty() || line.startsWith("#"))
			{
				out_p->write(line);
				if (!inserted_info) // Insert right after version line
				{
					if (!no_tag) out_p->write("##INFO=<ID=BBC,Number=1,Type=String,Description=\"Original chr:pos:ref|alt encoding\">\n");
					inserted_info = true;
				}
				continue;
			}

			// Not a header line so increment the variant
			++number_of_variants;

			QByteArray ref = VcfFile::getPartByColumn(line, VcfFile::REF).trimmed();
			QByteArray alt = VcfFile::getPartByColumn(line, VcfFile::ALT).trimmed();
			int pos = VcfFile::getPartByColumn(line, VcfFile::POS).trimmed().toInt();

			// Skip alternating allele pairs
			if (alt.contains(","))
			{
				out_p->write(line);
				continue;
			}

			VariantType variant_type = VcfFile::classifyVariant(ref, alt);

			if (variant_type != SNP) // Align with Needleman-Wunsch
			{
				auto nw = NeedlemanWunsch<QByteArray>(ref, alt);
				nw.compute_matrix();
				nw.trace_back();
				auto aligment = nw.get_aligments();
				auto reference = get<ALIGMENT_REFERENCE>(aligment), query = get<ALIGMENT_QUERY>(aligment);

				// Iterate through the reference and compare it with the query
				using AligmentPair = pair<QByteArray, QByteArray>;
				vector<Aligment> aligments;

				if (ref.length() == 1 || alt.length() == 1) // SNP
				{
					aligments.push_back(Aligment(query.replace("-", ""), reference.replace("-", ""), 0));
					++number_of_additional_snps;
				}
				else // MNP or CLUMPED
				{
					if (keep_mnps) // Keep more complex variants
					{
						out_p->write(line);
					}
					else // Break up clumped variants
					{
						while (query.startsWith("-")) // Kill leading gaps
						{
							query = query.remove(0, 1);
						}

						for (auto i = 0; i < query.size(); ++i)
						{
							if (i + 1 < query.size() && query.at(i + 1) == '-') // transition from REF,- to ALT
							{
								int gap_start = static_cast<int> (i + 1);
								int gap_end = gap_start;
								while ((i + 1) < query.size() && query.at(i + 1) == '-')
								{
									++i;
									gap_end++;
								}
								aligments.push_back(Aligment(query.mid(gap_start - 1, 1), reference.mid(gap_start - 1, gap_end).replace("-", ""), gap_start));
								++number_of_biallelic_block_substitutions; // new biallelic block substitutios
							}
							else if (i + 1 < reference.size() && reference.at(i + 1) == '-') // do the same for the reference
							{
								int gap_start = static_cast<int> (i +1);
								int gap_end = gap_start;
								while ((i + 1) < reference.size() && reference.at(i + 1) == '-')
								{
									++i;
									++gap_end;
								}
								aligments.push_back(Aligment(query.mid(gap_start - 1, 2), reference.mid(gap_start - 1, gap_end).replace("-", ""), gap_start));
								++number_of_biallelic_block_substitutions; // new biallelic block substitutios
							}
							else if (reference.at(i) == '-') {
								int gap_start = static_cast<int> (i);
								int gap_end = gap_start;
								while ((i + 1) < reference.size() && reference.at(i) == '-')
								{
									++i;
									++gap_end;
								}
								aligments.push_back(Aligment(query.mid(gap_start, gap_end - gap_start), reference.mid(gap_end, 1), gap_start));
								++number_of_biallelic_block_substitutions;
							}
							else if (query.at(i) != reference.at(i)) // transition from REF -> ALT
							{
								aligments.push_back(Aligment(query.mid(i, 1), reference.mid(i, 1), i));
								++i;
								++number_of_additional_snps;
							}
						}
					}
				}

				for (size_t i = 0; i < aligments.size(); ++i) // write out the new sequences
				{
					if (i > 0) ++number_of_new_variants;
					auto parts = line.split('\t');
					// Append INFO entry in format BBC=chr:pos:ref:alt
					if (!no_tag) parts[VcfFile::INFO] = parts[VcfFile::INFO].trimmed() + ";BBC=" + parts[VcfFile::CHROM] + ":" + parts[VcfFile::POS] + ":" + parts[VcfFile::REF] + "|" + parts[VcfFile::ALT];

					// Modify alt and ref with new aligments
					parts[VcfFile::REF] = get<0>(aligments[i]);
					parts[VcfFile::ALT] = get<1>(aligments[i]);
					parts[VcfFile::POS] = QByteArray::number(pos + get<2>(aligments[i]));

					// Write to output
					out_p->write(parts.join("\t"));

				}
			}
			else // We don't look at SNP's
			{
				out_p->write(line);
			}
		}

		// After processing print statistics to error stream
		QSharedPointer<QFile> stats_p = QSharedPointer<QFile>(new QFile(stats));
		stats_p->open(stderr, QFile::WriteOnly | QIODevice::Text);
		double new_variants_in_percent = (number_of_variants>0) ? 100.0 * number_of_new_variants / number_of_variants : 0.0;
		stats_p->write(QByteArray("Processed ") + QByteArray::number(number_of_variants) + " variant(s) of which " + QByteArray::number(number_of_new_variants) + " (" + QByteArray::number(new_variants_in_percent, 'f', 2) + "%) were decomposed.\n");
		stats_p->write(QByteArray::number(number_of_additional_snps - number_of_new_variants) + " of these are additional SNPs and " + QByteArray::number(number_of_biallelic_block_substitutions - number_of_variants) + " of these are biallelic substitutions.\n");
	}
};

#include "main.moc"

int main(int argc, char *argv[])
{
	ConcreteTool tool(argc, argv);
	return tool.execute();
}
<commit_msg>[vcfbreakcomplex] Remove unneeded declaration<commit_after>#include <algorithm>
#include <cmath>
#include <vector>
#include <QFile>
#include <QRegularExpression>
#include "ToolBase.h"
#include "Exceptions.h"
#include "Helper.h"
#include "VcfFile.h"
#include "NeedlemanWunsch.hpp"

using namespace std;

class ConcreteTool: public ToolBase
{
	Q_OBJECT

public:
	ConcreteTool(int& argc, char *argv[])
		: ToolBase(argc, argv)
	{
	}

	virtual void setup()
	{
		setDescription("Breaks complex variants into primitives preserving INFO/SAMPLE fields.");
		setExtendedDescription(QStringList() << "Multi-allelic variants are ignored, since we assume they have already been split, e.g. with VcfBreakMulti."
											 << "Complex variants that are decomposed, are flagged with a BBC (before break-complex) info entry.");
		addInfile("in", "Input VCF file. If unset, reads from STDIN.", true, true);
		addOutfile("out", "Output VCF list. If unset, writes to STDOUT.", true, true);
		addOutfile("stats", "Ouptuts statistics. If unset, writes to STDERR.", true, true);
		addFlag("keep_mnps", "Write out MNPs unchanged.");
		addFlag("no_tag", "Skip annotation of decomposed variants with BBC in INFO field.");

		changeLog(2018, 11, 30, "Initial implementation.");
	}

	/**
	 * @brief main
	 * This program breaks complex variants into several lines. It is inspired by vt's decompose_suballelic and vcflib's vcfallelicprimitives.
	 * However this program will only deal with complex variants. It will not consinder multi-allelic variants.
	 * We use the classification procedure as described in the VT wiki here https://genome.sph.umich.edu/wiki/Variant_classification
	 * Local aligment of variants is done using Needleman-Wunsch as implemented in edlib. See https://doi.org/10.1093/bioinformatics/btw753
	 */
	virtual void main()
	{
		QString in = getInfile("in");
		QString out = getOutfile("out");
		QString stats = getOutfile("stats");
		bool keep_mnps = getFlag("keep_mnps");
		bool no_tag = getFlag("no_tag");
		if(in!="" && in==out)
		{
			THROW(ArgumentException, "Input and output files must be different when streaming!");
		}
		if(stats!="" && stats==out)
		{
			THROW(ArgumentException, "Error and output files must be different when streaming!");
		}

		QSharedPointer<QFile> in_p = Helper::openFileForReading(in, true);
		QSharedPointer<QFile> out_p = Helper::openFileForWriting(out, true);

		// Statistics
		bool inserted_info = false;
		unsigned int number_of_additional_snps = 0;
		unsigned int number_of_biallelic_block_substitutions = 0;
		unsigned int number_of_new_variants = 0;
		unsigned int number_of_variants = 0;

		using Aligment = tuple<QByteArray, QByteArray, int>; // Describes an aligment of REF, ALT, OFFSET

		while(!in_p->atEnd())
		{
			QByteArray line = in_p->readLine();
			// Skip empty lines and headers
			if (line.trimmed().isEmpty() || line.startsWith("#"))
			{
				out_p->write(line);
				if (!inserted_info) // Insert right after version line
				{
					if (!no_tag) out_p->write("##INFO=<ID=BBC,Number=1,Type=String,Description=\"Original chr:pos:ref|alt encoding\">\n");
					inserted_info = true;
				}
				continue;
			}

			// Not a header line so increment the variant
			++number_of_variants;

			QByteArray ref = VcfFile::getPartByColumn(line, VcfFile::REF).trimmed();
			QByteArray alt = VcfFile::getPartByColumn(line, VcfFile::ALT).trimmed();
			int pos = VcfFile::getPartByColumn(line, VcfFile::POS).trimmed().toInt();

			// Skip alternating allele pairs
			if (alt.contains(","))
			{
				out_p->write(line);
				continue;
			}

			VariantType variant_type = VcfFile::classifyVariant(ref, alt);

			if (variant_type != SNP) // Align with Needleman-Wunsch
			{
				auto nw = NeedlemanWunsch<QByteArray>(ref, alt);
				nw.compute_matrix();
				nw.trace_back();
				auto aligment = nw.get_aligments();
				auto reference = get<ALIGMENT_REFERENCE>(aligment), query = get<ALIGMENT_QUERY>(aligment);

				// Iterate through the reference and compare it with the query
				vector<Aligment> aligments;

				if (ref.length() == 1 || alt.length() == 1) // SNP
				{
					aligments.push_back(Aligment(query.replace("-", ""), reference.replace("-", ""), 0));
					++number_of_additional_snps;
				}
				else // MNP or CLUMPED
				{
					if (keep_mnps) // Keep more complex variants
					{
						out_p->write(line);
					}
					else // Break up clumped variants
					{
						while (query.startsWith("-")) // Kill leading gaps
						{
							query = query.remove(0, 1);
						}

						for (auto i = 0; i < query.size(); ++i)
						{
							if (i + 1 < query.size() && query.at(i + 1) == '-') // transition from REF,- to ALT
							{
								int gap_start = static_cast<int> (i + 1);
								int gap_end = gap_start;
								while ((i + 1) < query.size() && query.at(i + 1) == '-')
								{
									++i;
									gap_end++;
								}
								aligments.push_back(Aligment(query.mid(gap_start - 1, 1), reference.mid(gap_start - 1, gap_end).replace("-", ""), gap_start));
								++number_of_biallelic_block_substitutions; // new biallelic block substitutios
							}
							else if (i + 1 < reference.size() && reference.at(i + 1) == '-') // do the same for the reference
							{
								int gap_start = static_cast<int> (i +1);
								int gap_end = gap_start;
								while ((i + 1) < reference.size() && reference.at(i + 1) == '-')
								{
									++i;
									++gap_end;
								}
								aligments.push_back(Aligment(query.mid(gap_start - 1, 2), reference.mid(gap_start - 1, gap_end).replace("-", ""), gap_start));
								++number_of_biallelic_block_substitutions; // new biallelic block substitutios
							}
							else if (reference.at(i) == '-') {
								int gap_start = static_cast<int> (i);
								int gap_end = gap_start;
								while ((i + 1) < reference.size() && reference.at(i) == '-')
								{
									++i;
									++gap_end;
								}
								aligments.push_back(Aligment(query.mid(gap_start, gap_end - gap_start), reference.mid(gap_end, 1), gap_start));
								++number_of_biallelic_block_substitutions;
							}
							else if (query.at(i) != reference.at(i)) // transition from REF -> ALT
							{
								aligments.push_back(Aligment(query.mid(i, 1), reference.mid(i, 1), i));
								++i;
								++number_of_additional_snps;
							}
						}
					}
				}

				for (size_t i = 0; i < aligments.size(); ++i) // write out the new sequences
				{
					if (i > 0) ++number_of_new_variants;
					auto parts = line.split('\t');
					// Append INFO entry in format BBC=chr:pos:ref:alt
					if (!no_tag) parts[VcfFile::INFO] = parts[VcfFile::INFO].trimmed() + ";BBC=" + parts[VcfFile::CHROM] + ":" + parts[VcfFile::POS] + ":" + parts[VcfFile::REF] + "|" + parts[VcfFile::ALT];

					// Modify alt and ref with new aligments
					parts[VcfFile::REF] = get<0>(aligments[i]);
					parts[VcfFile::ALT] = get<1>(aligments[i]);
					parts[VcfFile::POS] = QByteArray::number(pos + get<2>(aligments[i]));

					// Write to output
					out_p->write(parts.join("\t"));

				}
			}
			else // We don't look at SNP's
			{
				out_p->write(line);
			}
		}

		// After processing print statistics to error stream
		QSharedPointer<QFile> stats_p = QSharedPointer<QFile>(new QFile(stats));
		stats_p->open(stderr, QFile::WriteOnly | QIODevice::Text);
		double new_variants_in_percent = (number_of_variants>0) ? 100.0 * number_of_new_variants / number_of_variants : 0.0;
		stats_p->write(QByteArray("Processed ") + QByteArray::number(number_of_variants) + " variant(s) of which " + QByteArray::number(number_of_new_variants) + " (" + QByteArray::number(new_variants_in_percent, 'f', 2) + "%) were decomposed.\n");
		stats_p->write(QByteArray::number(number_of_additional_snps - number_of_new_variants) + " of these are additional SNPs and " + QByteArray::number(number_of_biallelic_block_substitutions - number_of_variants) + " of these are biallelic substitutions.\n");
	}
};

#include "main.moc"

int main(int argc, char *argv[])
{
	ConcreteTool tool(argc, argv);
	return tool.execute();
}
<|endoftext|>


<commit_before>#include "musiclrccontainerforinline.h"
#include "musiclrcmanagerforinline.h"
#include "musiclrcartphotoupload.h"
#include "musiclrcfloatwidget.h"

#include <QVBoxLayout>
#include <QSettings>
#include <QPainter>
#include <QDesktopServices>
#include <QClipboard>
#include <QApplication>

MusicLrcContainerForInline::MusicLrcContainerForInline(QWidget *parent) :
    MusicLrcContainer(parent)
{
    m_vBoxLayout = new QVBoxLayout(this);
    m_vBoxLayout->setMargin(0);

    setLayout(m_vBoxLayout);
    m_containerType = "INLINE";
    for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
    {
       MusicLRCManager *w = new MusicLRCManagerForInline(this);
       m_vBoxLayout->addWidget(w);
       m_musicLrcContainer.append(w);
    }

    changeCurrentLrcColorOrigin();
    m_currentLrcIndex = 0;
    m_mouseMovedAt = QPoint(-1,-1);
    m_mousePressedAt = QPoint(-1,-1);
    m_mouseLeftPressed = false;
    m_showArtBackground = true;
    m_showInlineLrc = true;
    for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
        m_musicLrcContainer[i]->setText(".........");
    m_musicLrcContainer[CURRENT_LRC_PAINT]->setText(tr("noCurrentSongPlay"));

    m_lrcFloatWidget = new MusicLrcFloatWidget(this);
}

MusicLrcContainerForInline::~MusicLrcContainerForInline()
{
    clearAllMusicLRCManager();
    delete m_vBoxLayout;
    delete m_lrcFloatWidget;
}

bool MusicLrcContainerForInline::transLrcFileToTime(const QString& lrcFileName)
{
    static_cast<MusicLRCManagerForInline*>(m_musicLrcContainer[CURRENT_LRC_PAINT])->setUpdateLrc(false);
    m_lrcContainer.clear();///Clear the original map
    m_currentShowLrcContainer.clear();///Clear the original lrc
    QFile file(m_currentLrcFileName = lrcFileName); ///Open the lyrics file

    for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
        m_musicLrcContainer[i]->setText(".........");
    if(!file.open(QIODevice::ReadOnly))
    {
        m_musicLrcContainer[CURRENT_LRC_PAINT]->setText(tr("unFoundLrc"));
        return false;
    }
    else
        m_musicLrcContainer[CURRENT_LRC_PAINT]->setText(tr("noCurrentSongPlay"));

    QString getAllText = QString(file.readAll());
    file.close();
    //The lyrics by line into the lyrics list
    QStringList lines = getAllText.split("\n");
    //This is the time the label format[00:05.54]
    //Regular expressionsd {2} Said matching 2 numbers
    QRegExp reg("\\[\\d{2}:\\d{2}\\.\\d{2}\\]");
    foreach(QString oneLine, lines)
    {
      QString temp = oneLine;
      temp.replace(reg,"");
      /*Replace the regular expression matching in place with the empty
        string, then we get the lyrics text,And then get all the time the
        label in the current row, and separately with lyrics text into QMap
        IndexIn () to return to the first matching position, if the return
        is -1, said no match,Under normal circumstances, POS should be
        followed is corresponding to the lyrics file
      */
      int pos = reg.indexIn(oneLine, 0);
      while(pos != -1)
      { //That match
        QString cap = reg.cap(0);
        //Return zeroth expression matching the content
        //The time tag into the time value, in milliseconds
        QRegExp regexp;
        regexp.setPattern("\\d{2}(?=:)");
        regexp.indexIn(cap);
        int minute = regexp.cap(0).toInt();
        regexp.setPattern("\\d{2}(?=\\.)");
        regexp.indexIn(cap);
        int second = regexp.cap(0).toInt();
        regexp.setPattern("\\d{2}(?=\\])");
        regexp.indexIn(cap);
        int millisecond = regexp.cap(0).toInt();
        qint64 totalTime = minute * 60000 + second * 1000 + millisecond * 10;
        //Insert into lrcContainer
        m_lrcContainer.insert(totalTime, temp);
        pos += reg.matchedLength();
        pos = reg.indexIn(oneLine, pos);//Matching all
      }
    }
    //If the lrcContainer is empty
    if (m_lrcContainer.isEmpty())
    {
        m_musicLrcContainer[CURRENT_LRC_PAINT]->setText(tr("lrcFileError"));
        return false;
    }

    m_currentShowLrcContainer.clear();
    m_currentLrcIndex = 0;

    for(int i=0; i<MIN_LRCCONTAIN_COUNT/2; ++i)
        m_currentShowLrcContainer<<".........";
    if(m_lrcContainer.find(0) == m_lrcContainer.end())
       m_lrcContainer.insert(0,".........");

    MIntStringMapIt it(m_lrcContainer);
    while(it.hasNext())
    {
        it.next();
        m_currentShowLrcContainer.append(it.value());
    }
    for(int i=0; i<MIN_LRCCONTAIN_COUNT/2; ++i)
        m_currentShowLrcContainer<<" ";

    return true;
}

QString MusicLrcContainerForInline::text() const
{
    return m_musicLrcContainer[CURRENT_LRC_PAINT]->text();
}

void MusicLrcContainerForInline::setMaskLinearGradientColor(QColor color)
{
    m_musicLrcContainer[CURRENT_LRC_PAINT]->setMaskLinearGradientColor(color);
}

void MusicLrcContainerForInline::startTimerClock()
{
    m_musicLrcContainer[CURRENT_LRC_PAINT]->startTimerClock();
}

void MusicLrcContainerForInline::stopLrcMask()
{
    m_musicLrcContainer[CURRENT_LRC_PAINT]->stopLrcMask();
}

void MusicLrcContainerForInline::setSongSpeedAndSlow(qint64 time)
{
    foreach(qint64 value, m_lrcContainer.keys())
    {
        if(time < value)
        {
            time = value;
            break;
        }
    }
    for(int i=0; i<m_currentShowLrcContainer.count(); ++i)
    {
        if(m_currentShowLrcContainer[i] == m_lrcContainer.value(time))
        {
            m_currentLrcIndex = i - 3;
            break;
        }
    }
}

void MusicLrcContainerForInline::updateCurrentLrc(qint64 time)
{
    if(!m_lrcContainer.isEmpty() &&
        m_currentLrcIndex + MIN_LRCCONTAIN_COUNT <= m_currentShowLrcContainer.count())
    {
        for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
        {
            m_musicLrcContainer[i]->setText(m_currentShowLrcContainer[m_currentLrcIndex + i]);
        }
        ++m_currentLrcIndex;
        static_cast<MusicLRCManagerForInline*>(m_musicLrcContainer[CURRENT_LRC_PAINT])->setUpdateLrc(true);
        m_musicLrcContainer[CURRENT_LRC_PAINT]->startLrcMask(time);

        for(int i=1; i<= MIN_LRCCONTAIN_COUNT; ++i)
        {
            MusicLRCManagerForInline *w = static_cast<MusicLRCManagerForInline*>(m_musicLrcContainer[i-1]);
            if(i == 1 || i == 9)
            {
                w->setFontSize(3);
                w->setTransparent(220);
            }
            if(i == 2 || i == 8)
            {
                w->setFontSize(2);
                w->setTransparent(105);
            }
            if(i == 3 || i == 4 || i == 6 || i == 7)
            {
                w->setFontSize(1);
                w->setTransparent(45);
            }
        }
    }
}

void MusicLrcContainerForInline::setLrcSize(LrcSizeTable size)
{
    if(size < 13 || size > 17)
    {
        qDebug()<<"set lrc size error!";
        return;
    }
    for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
    {
        m_musicLrcContainer[i]->setLrcFontSize(size);
    }
    QSettings().setValue("LRCSIZECHOICED",size);
}

int MusicLrcContainerForInline::getLrcSize()
{
    return QSettings().value("LRCSIZECHOICED").toInt();
}

void MusicLrcContainerForInline::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QFont font;
    painter.setFont(font);
    painter.setPen(QColor(Qt::white));
    painter.drawLine(0, m_mouseMovedAt.y(), width(), m_mouseMovedAt.y());
    painter.end();
}

void MusicLrcContainerForInline::mouseMoveEvent(QMouseEvent *event)
{
    if(m_mouseLeftPressed)
    {
        m_mouseMovedAt = event->pos();
        update();
    }
}

void MusicLrcContainerForInline::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        m_mouseLeftPressed = true;
        setCursor(Qt::CrossCursor);
        m_mouseMovedAt = m_mousePressedAt = event->pos();
        update();
    }
}

void MusicLrcContainerForInline::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        setCursor(Qt::ArrowCursor);
        m_mouseLeftPressed = false;
        changeLrcPostion("mouse");
        m_mouseMovedAt = m_mousePressedAt = QPoint(-1,-1);
        update();
    }
}

void MusicLrcContainerForInline::wheelEvent(QWheelEvent *event)
{
    MusicLrcContainer::wheelEvent(event);
    changeLrcPostion(QString::number(event->delta()));
}

void MusicLrcContainerForInline::changeLrcPostion(const QString &type)
{
    int level = m_currentLrcIndex;

    if(type == "mouse")
        m_currentLrcIndex += (m_mousePressedAt.y() - m_mouseMovedAt.y()) / 35;
    else
        type.toInt() < 0 ? --m_currentLrcIndex : ++m_currentLrcIndex;

    if(m_currentLrcIndex < 0) m_currentLrcIndex = 0;
    if(m_currentLrcIndex + MIN_LRCCONTAIN_COUNT < m_currentShowLrcContainer.count())
    {
        MIntStringMapIt it(m_lrcContainer);
        for(int i=0; i<m_currentLrcIndex + 1; ++i)
            if(it.hasNext()) it.next();
        emit updateCurrentTime(it.key());
    }
    else
        m_currentLrcIndex = level;
}

void MusicLrcContainerForInline::contextMenuEvent(QContextMenuEvent *)
{
    QMenu menu(this);
    QMenu changColorMenu(tr("changColorMenu"),this);
    QMenu changeLrcSize(tr("changeLrcSize"),this);
    changColorMenu.setStyleSheet(MusicObject::MusicSystemTrayMenu);
    changeLrcSize.setStyleSheet(MusicObject::MusicSystemTrayMenu);
    menu.setStyleSheet(MusicObject::MusicSystemTrayMenu);
    menu.addAction(tr("searchLrcs"), this, SLOT(searchMusicLrcs()));
    menu.addAction(tr("updateLrc"), this, SIGNAL(theCurrentLrcUpdated()));
    menu.addSeparator();
    menu.addMenu(&changColorMenu);
    menu.addMenu(&changeLrcSize);
    menu.addSeparator();

    changeLrcSize.addAction(tr("smaller"),this,SLOT(changeLrcSizeSmaller()));
    changeLrcSize.addAction(tr("small"),this,SLOT(changeLrcSizeSmall()));
    changeLrcSize.addAction(tr("middle"),this,SLOT(changeLrcSizeMiddle()));
    changeLrcSize.addAction(tr("big"),this,SLOT(changeLrcSizeBig()));
    changeLrcSize.addAction(tr("bigger"),this,SLOT(changeLrcSizeBigger()));
    changeLrcSize.addSeparator();
    changeLrcSize.addAction(tr("custom"),this,SLOT(currentLrcCustom()));
    createColorMenu(changColorMenu);

    QAction *artBgAc = menu.addAction(tr("artbgoff"), this, SLOT(theArtBgChanged()));
    m_showArtBackground ? artBgAc->setText(tr("artbgoff")) : artBgAc->setText(tr("artbgon")) ;
    QAction *showLrc = menu.addAction(tr("lrcoff"), this, SLOT(theShowLrcChanged()));
    m_showInlineLrc ? showLrc->setText(tr("lrcoff")) : showLrc->setText(tr("lrcon"));
    menu.addAction(tr("artbgupload"), this, SLOT(theArtBgUploaded()));
    menu.addSeparator();
    bool fileCheck = !m_currentLrcFileName.isEmpty() && QFile::exists(m_currentLrcFileName);
    QAction *copyToClipAc = menu.addAction(tr("copyToClip"),this,SLOT(lrcCopyClipboard()));
    copyToClipAc->setEnabled( fileCheck );
    QAction *showLrcFileAc = menu.addAction(tr("showLrcFile"),this,SLOT(lrcOpenFileDir()));
    showLrcFileAc->setEnabled( fileCheck );

    menu.addSeparator();
    menu.addAction(tr("customSetting"),this,SLOT(currentLrcCustom()));

    menu.exec(QCursor::pos());
}

void MusicLrcContainerForInline::theArtBgChanged()
{
    m_showArtBackground = !m_showArtBackground;
    emit theArtBgHasChanged();
}

void MusicLrcContainerForInline::theArtBgUploaded()
{
    MusicLrcArtPhotoUpload artDialog(this);
    artDialog.exec();
    m_showArtBackground = true;
    emit theArtBgHasChanged();
}

void MusicLrcContainerForInline::theShowLrcChanged()
{
    m_showInlineLrc = !m_showInlineLrc;
    foreach(MusicLRCManager *w, m_musicLrcContainer)
    {
        w->setVisible( m_showInlineLrc );
    }
}

void MusicLrcContainerForInline::lrcOpenFileDir()
{
    QDesktopServices::openUrl(QUrl(QFileInfo(m_currentLrcFileName).absolutePath(), QUrl::TolerantMode));
}

void MusicLrcContainerForInline::lrcCopyClipboard()
{
    QClipboard *clipBoard = QApplication::clipboard();
    QString clipString;
    foreach(QString s, m_lrcContainer.values())
    {
        clipString.append(s);
    }
    clipBoard->setText(clipString);
}
<commit_msg>fix drag timer slider bar error[323212]<commit_after>#include "musiclrccontainerforinline.h"
#include "musiclrcmanagerforinline.h"
#include "musiclrcartphotoupload.h"
#include "musiclrcfloatwidget.h"

#include <QVBoxLayout>
#include <QSettings>
#include <QPainter>
#include <QDesktopServices>
#include <QClipboard>
#include <QApplication>

MusicLrcContainerForInline::MusicLrcContainerForInline(QWidget *parent) :
    MusicLrcContainer(parent)
{
    m_vBoxLayout = new QVBoxLayout(this);
    m_vBoxLayout->setMargin(0);

    setLayout(m_vBoxLayout);
    m_containerType = "INLINE";
    for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
    {
       MusicLRCManager *w = new MusicLRCManagerForInline(this);
       m_vBoxLayout->addWidget(w);
       m_musicLrcContainer.append(w);
    }

    changeCurrentLrcColorOrigin();
    m_currentLrcIndex = 0;
    m_mouseMovedAt = QPoint(-1,-1);
    m_mousePressedAt = QPoint(-1,-1);
    m_mouseLeftPressed = false;
    m_showArtBackground = true;
    m_showInlineLrc = true;
    for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
        m_musicLrcContainer[i]->setText(".........");
    m_musicLrcContainer[CURRENT_LRC_PAINT]->setText(tr("noCurrentSongPlay"));

    m_lrcFloatWidget = new MusicLrcFloatWidget(this);
}

MusicLrcContainerForInline::~MusicLrcContainerForInline()
{
    clearAllMusicLRCManager();
    delete m_vBoxLayout;
    delete m_lrcFloatWidget;
}

bool MusicLrcContainerForInline::transLrcFileToTime(const QString& lrcFileName)
{
    static_cast<MusicLRCManagerForInline*>(m_musicLrcContainer[CURRENT_LRC_PAINT])->setUpdateLrc(false);
    m_lrcContainer.clear();///Clear the original map
    m_currentShowLrcContainer.clear();///Clear the original lrc
    QFile file(m_currentLrcFileName = lrcFileName); ///Open the lyrics file

    for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
        m_musicLrcContainer[i]->setText(".........");
    if(!file.open(QIODevice::ReadOnly))
    {
        m_musicLrcContainer[CURRENT_LRC_PAINT]->setText(tr("unFoundLrc"));
        return false;
    }
    else
        m_musicLrcContainer[CURRENT_LRC_PAINT]->setText(tr("noCurrentSongPlay"));

    QString getAllText = QString(file.readAll());
    file.close();
    //The lyrics by line into the lyrics list
    QStringList lines = getAllText.split("\n");
    //This is the time the label format[00:05.54]
    //Regular expressionsd {2} Said matching 2 numbers
    QRegExp reg("\\[\\d{2}:\\d{2}\\.\\d{2}\\]");
    foreach(QString oneLine, lines)
    {
      QString temp = oneLine;
      temp.replace(reg,"");
      /*Replace the regular expression matching in place with the empty
        string, then we get the lyrics text,And then get all the time the
        label in the current row, and separately with lyrics text into QMap
        IndexIn () to return to the first matching position, if the return
        is -1, said no match,Under normal circumstances, POS should be
        followed is corresponding to the lyrics file
      */
      int pos = reg.indexIn(oneLine, 0);
      while(pos != -1)
      { //That match
        QString cap = reg.cap(0);
        //Return zeroth expression matching the content
        //The time tag into the time value, in milliseconds
        QRegExp regexp;
        regexp.setPattern("\\d{2}(?=:)");
        regexp.indexIn(cap);
        int minute = regexp.cap(0).toInt();
        regexp.setPattern("\\d{2}(?=\\.)");
        regexp.indexIn(cap);
        int second = regexp.cap(0).toInt();
        regexp.setPattern("\\d{2}(?=\\])");
        regexp.indexIn(cap);
        int millisecond = regexp.cap(0).toInt();
        qint64 totalTime = minute * 60000 + second * 1000 + millisecond * 10;
        //Insert into lrcContainer
        m_lrcContainer.insert(totalTime, temp);
        pos += reg.matchedLength();
        pos = reg.indexIn(oneLine, pos);//Matching all
      }
    }
    //If the lrcContainer is empty
    if (m_lrcContainer.isEmpty())
    {
        m_musicLrcContainer[CURRENT_LRC_PAINT]->setText(tr("lrcFileError"));
        return false;
    }

    m_currentShowLrcContainer.clear();
    m_currentLrcIndex = 0;

    for(int i=0; i<MIN_LRCCONTAIN_COUNT/2; ++i)
        m_currentShowLrcContainer<<".........";
    if(m_lrcContainer.find(0) == m_lrcContainer.end())
       m_lrcContainer.insert(0,".........");

    MIntStringMapIt it(m_lrcContainer);
    while(it.hasNext())
    {
        it.next();
        m_currentShowLrcContainer.append(it.value());
    }
    for(int i=0; i<MIN_LRCCONTAIN_COUNT/2; ++i)
        m_currentShowLrcContainer<<" ";

    return true;
}

QString MusicLrcContainerForInline::text() const
{
    return m_musicLrcContainer[CURRENT_LRC_PAINT]->text();
}

void MusicLrcContainerForInline::setMaskLinearGradientColor(QColor color)
{
    m_musicLrcContainer[CURRENT_LRC_PAINT]->setMaskLinearGradientColor(color);
}

void MusicLrcContainerForInline::startTimerClock()
{
    m_musicLrcContainer[CURRENT_LRC_PAINT]->startTimerClock();
}

void MusicLrcContainerForInline::stopLrcMask()
{
    m_musicLrcContainer[CURRENT_LRC_PAINT]->stopLrcMask();
}

void MusicLrcContainerForInline::setSongSpeedAndSlow(qint64 time)
{
    foreach(qint64 value, m_lrcContainer.keys())
    {
        if(time < value)
        {
            time = value;
            break;
        }
    }
    for(int i=0; i<m_currentShowLrcContainer.count(); ++i)
    {
        if(m_currentShowLrcContainer[i] == m_lrcContainer.value(time))
        {
            m_currentLrcIndex = i - CURRENT_LRC_PAINT - 1;
            break;
        }
    }
}

void MusicLrcContainerForInline::updateCurrentLrc(qint64 time)
{
    if(!m_lrcContainer.isEmpty() &&
        m_currentLrcIndex + MIN_LRCCONTAIN_COUNT <= m_currentShowLrcContainer.count())
    {
        for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
        {
            m_musicLrcContainer[i]->setText(m_currentShowLrcContainer[m_currentLrcIndex + i]);
        }
        ++m_currentLrcIndex;
        static_cast<MusicLRCManagerForInline*>(m_musicLrcContainer[CURRENT_LRC_PAINT])->setUpdateLrc(true);
        m_musicLrcContainer[CURRENT_LRC_PAINT]->startLrcMask(time);

        for(int i=1; i<= MIN_LRCCONTAIN_COUNT; ++i)
        {
            MusicLRCManagerForInline *w = static_cast<MusicLRCManagerForInline*>(m_musicLrcContainer[i-1]);
            if(i == 1 || i == 9)
            {
                w->setFontSize(3);
                w->setTransparent(220);
            }
            if(i == 2 || i == 8)
            {
                w->setFontSize(2);
                w->setTransparent(105);
            }
            if(i == 3 || i == 4 || i == 6 || i == 7)
            {
                w->setFontSize(1);
                w->setTransparent(45);
            }
        }
    }
}

void MusicLrcContainerForInline::setLrcSize(LrcSizeTable size)
{
    if(size < 13 || size > 17)
    {
        qDebug()<<"set lrc size error!";
        return;
    }
    for(int i=0; i<MIN_LRCCONTAIN_COUNT; ++i)
    {
        m_musicLrcContainer[i]->setLrcFontSize(size);
    }
    QSettings().setValue("LRCSIZECHOICED",size);
}

int MusicLrcContainerForInline::getLrcSize()
{
    return QSettings().value("LRCSIZECHOICED").toInt();
}

void MusicLrcContainerForInline::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QFont font;
    painter.setFont(font);
    painter.setPen(QColor(Qt::white));
    painter.drawLine(0, m_mouseMovedAt.y(), width(), m_mouseMovedAt.y());
    painter.end();
}

void MusicLrcContainerForInline::mouseMoveEvent(QMouseEvent *event)
{
    if(m_mouseLeftPressed)
    {
        m_mouseMovedAt = event->pos();
        update();
    }
}

void MusicLrcContainerForInline::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        m_mouseLeftPressed = true;
        setCursor(Qt::CrossCursor);
        m_mouseMovedAt = m_mousePressedAt = event->pos();
        update();
    }
}

void MusicLrcContainerForInline::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        setCursor(Qt::ArrowCursor);
        m_mouseLeftPressed = false;
        changeLrcPostion("mouse");
        m_mouseMovedAt = m_mousePressedAt = QPoint(-1,-1);
        update();
    }
}

void MusicLrcContainerForInline::wheelEvent(QWheelEvent *event)
{
    MusicLrcContainer::wheelEvent(event);
    changeLrcPostion(QString::number(event->delta()));
}

void MusicLrcContainerForInline::changeLrcPostion(const QString &type)
{
    int level = m_currentLrcIndex;

    if(type == "mouse")
        m_currentLrcIndex += (m_mousePressedAt.y() - m_mouseMovedAt.y()) / 35;
    else
        type.toInt() < 0 ? --m_currentLrcIndex : ++m_currentLrcIndex;

    if(m_currentLrcIndex < 0) m_currentLrcIndex = 0;
    if(m_currentLrcIndex + MIN_LRCCONTAIN_COUNT < m_currentShowLrcContainer.count())
    {
        MIntStringMapIt it(m_lrcContainer);
        for(int i=0; i<m_currentLrcIndex + 1; ++i)
            if(it.hasNext()) it.next();
        emit updateCurrentTime(it.key());
    }
    else
        m_currentLrcIndex = level;
}

void MusicLrcContainerForInline::contextMenuEvent(QContextMenuEvent *)
{
    QMenu menu(this);
    QMenu changColorMenu(tr("changColorMenu"),this);
    QMenu changeLrcSize(tr("changeLrcSize"),this);
    changColorMenu.setStyleSheet(MusicObject::MusicSystemTrayMenu);
    changeLrcSize.setStyleSheet(MusicObject::MusicSystemTrayMenu);
    menu.setStyleSheet(MusicObject::MusicSystemTrayMenu);
    menu.addAction(tr("searchLrcs"), this, SLOT(searchMusicLrcs()));
    menu.addAction(tr("updateLrc"), this, SIGNAL(theCurrentLrcUpdated()));
    menu.addSeparator();
    menu.addMenu(&changColorMenu);
    menu.addMenu(&changeLrcSize);
    menu.addSeparator();

    changeLrcSize.addAction(tr("smaller"),this,SLOT(changeLrcSizeSmaller()));
    changeLrcSize.addAction(tr("small"),this,SLOT(changeLrcSizeSmall()));
    changeLrcSize.addAction(tr("middle"),this,SLOT(changeLrcSizeMiddle()));
    changeLrcSize.addAction(tr("big"),this,SLOT(changeLrcSizeBig()));
    changeLrcSize.addAction(tr("bigger"),this,SLOT(changeLrcSizeBigger()));
    changeLrcSize.addSeparator();
    changeLrcSize.addAction(tr("custom"),this,SLOT(currentLrcCustom()));
    createColorMenu(changColorMenu);

    QAction *artBgAc = menu.addAction(tr("artbgoff"), this, SLOT(theArtBgChanged()));
    m_showArtBackground ? artBgAc->setText(tr("artbgoff")) : artBgAc->setText(tr("artbgon")) ;
    QAction *showLrc = menu.addAction(tr("lrcoff"), this, SLOT(theShowLrcChanged()));
    m_showInlineLrc ? showLrc->setText(tr("lrcoff")) : showLrc->setText(tr("lrcon"));
    menu.addAction(tr("artbgupload"), this, SLOT(theArtBgUploaded()));
    menu.addSeparator();
    bool fileCheck = !m_currentLrcFileName.isEmpty() && QFile::exists(m_currentLrcFileName);
    QAction *copyToClipAc = menu.addAction(tr("copyToClip"),this,SLOT(lrcCopyClipboard()));
    copyToClipAc->setEnabled( fileCheck );
    QAction *showLrcFileAc = menu.addAction(tr("showLrcFile"),this,SLOT(lrcOpenFileDir()));
    showLrcFileAc->setEnabled( fileCheck );

    menu.addSeparator();
    menu.addAction(tr("customSetting"),this,SLOT(currentLrcCustom()));

    menu.exec(QCursor::pos());
}

void MusicLrcContainerForInline::theArtBgChanged()
{
    m_showArtBackground = !m_showArtBackground;
    emit theArtBgHasChanged();
}

void MusicLrcContainerForInline::theArtBgUploaded()
{
    MusicLrcArtPhotoUpload artDialog(this);
    artDialog.exec();
    m_showArtBackground = true;
    emit theArtBgHasChanged();
}

void MusicLrcContainerForInline::theShowLrcChanged()
{
    m_showInlineLrc = !m_showInlineLrc;
    foreach(MusicLRCManager *w, m_musicLrcContainer)
    {
        w->setVisible( m_showInlineLrc );
    }
}

void MusicLrcContainerForInline::lrcOpenFileDir()
{
    QDesktopServices::openUrl(QUrl(QFileInfo(m_currentLrcFileName).absolutePath(), QUrl::TolerantMode));
}

void MusicLrcContainerForInline::lrcCopyClipboard()
{
    QClipboard *clipBoard = QApplication::clipboard();
    QString clipString;
    foreach(QString s, m_lrcContainer.values())
    {
        clipString.append(s);
    }
    clipBoard->setText(clipString);
}
<|endoftext|>