<commit_before>/*
  This file is part of c-ethash.

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

  c-ethash is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ethash_cu_miner.cpp
* @author Tim Hughes <tim@twistedfury.com>
* @date 2015
*/

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <queue>
#include <random>
#include <atomic>
#include <sstream>
#include <vector>
#include <chrono>
#include <thread>
#include <libethash/util.h>
#include <libethash/ethash.h>
#include <libethash/internal.h>
#include <cuda_runtime.h>
#include "ethash_cu_miner.h"
#include "ethash_cu_miner_kernel_globals.h"

#define ETHASH_BYTES 32

// workaround lame platforms
#if !CL_VERSION_1_2
#define CL_MAP_WRITE_INVALIDATE_REGION CL_MAP_WRITE
#define CL_MEM_HOST_READ_ONLY 0
#endif

#undef min
#undef max

using namespace std;

unsigned const ethash_cu_miner::c_defaultBlockSize = 128;
unsigned const ethash_cu_miner::c_defaultGridSize = 2048; // * CL_DEFAULT_LOCAL_WORK_SIZE
unsigned const ethash_cu_miner::c_defaultNumStreams = 2;

#if defined(_WIN32)
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
static std::atomic_flag s_logSpin = ATOMIC_FLAG_INIT;
#define ETHCU_LOG(_contents) \
	do \
			{ \
		std::stringstream ss; \
		ss << _contents; \
						while (s_logSpin.test_and_set(std::memory_order_acquire)) {} \
		OutputDebugStringA(ss.str().c_str()); \
		cerr << ss.str() << endl << flush; \
		s_logSpin.clear(std::memory_order_release); \
			} while (false)
#else
#define ETHCL_LOG(_contents) cout << "[OPENCL]:" << _contents << endl
#endif

#define CUDA_SAFE_CALL(call)                                          \
do {                                                                  \
	cudaError_t err = call;                                           \
	if (cudaSuccess != err) {                                         \
		fprintf(stderr, "Cuda error in func '%s' at line %i : %s.\n", \
		         __FUNCTION__, __LINE__, cudaGetErrorString(err) );   \
		exit(EXIT_FAILURE);                                           \
		}                                                                 \
} while (0)

ethash_cu_miner::search_hook::~search_hook() {}

ethash_cu_miner::ethash_cu_miner()
{
}

std::string ethash_cu_miner::platform_info(unsigned _deviceId)
{
	int runtime_version;
	int device_count;

	device_count = getNumDevices();

	if (device_count == 0)
		return std::string();

	CUDA_SAFE_CALL(cudaRuntimeGetVersion(&runtime_version));

	// use selected default device
	int device_num = std::min<int>((int)_deviceId, device_count - 1);
	cudaDeviceProp device_props;

	CUDA_SAFE_CALL(cudaGetDeviceProperties(&device_props, device_num));

	char platform[5];
	int version_major = runtime_version / 1000;
	int version_minor = (runtime_version - (version_major * 1000)) / 10;
	sprintf(platform, "%d.%d", version_major, version_minor);

	char compute[5];
	sprintf(compute, "%d.%d", device_props.major, device_props.minor);

	return "{ \"platform\": \"CUDA " + std::string(platform) + "\", \"device\": \"" + device_props.name + "\", \"version\": \"Compute " + std::string(compute) + "\" }";
}

unsigned ethash_cu_miner::getNumDevices()
{
	int device_count;
	CUDA_SAFE_CALL(cudaGetDeviceCount(&device_count));
	return device_count;
}

bool ethash_cu_miner::configureGPU(
	unsigned _blockSize,
	unsigned _gridSize,
	unsigned _numStreams,
	unsigned _extraGPUMemory,
	bool	 _highcpu,
	uint64_t _currentBlock
	)
{
	s_blockSize = _blockSize;
	s_gridSize = _gridSize;
	s_extraRequiredGPUMem = _extraGPUMemory;
	s_numStreams = _numStreams;
	s_highCPU = _highcpu;

	// by default let's only consider the DAG of the first epoch
	uint64_t dagSize = ethash_get_datasize(_currentBlock);
	uint64_t requiredSize = dagSize + _extraGPUMemory;
	for (unsigned int i = 0; i < getNumDevices(); i++)
	{
		cudaDeviceProp props;
		CUDA_SAFE_CALL(cudaGetDeviceProperties(&props, i));
		if (props.totalGlobalMem >= requiredSize)
		{
			ETHCU_LOG(
				"Found suitable CUDA device [" << props.name
				<< "] with " << props.totalGlobalMem << " bytes of GPU memory"
				);
			return true;
		}

		ETHCU_LOG(
			"CUDA device " << props.name
			<< " has insufficient GPU memory." << props.totalGlobalMem <<
			" bytes of memory found < " << requiredSize << " bytes of memory required"
			);
	}
	return false;
}

unsigned ethash_cu_miner::s_extraRequiredGPUMem;
unsigned ethash_cu_miner::s_blockSize = ethash_cu_miner::c_defaultBlockSize;
unsigned ethash_cu_miner::s_gridSize = ethash_cu_miner::c_defaultGridSize;
unsigned ethash_cu_miner::s_numStreams = ethash_cu_miner::c_defaultNumStreams;
bool ethash_cu_miner::s_highCPU = false;

void ethash_cu_miner::listDevices()
{
	string outString = "\nListing CUDA devices.\nFORMAT: [deviceID] deviceName\n";
	unsigned int i = 0;
	for (unsigned int i = 0; i < getNumDevices(); i++)
	{
		cudaDeviceProp props;
		CUDA_SAFE_CALL(cudaGetDeviceProperties(&props, i));

		outString += "[" + to_string(i) + "] " + props.name + "\n";
		outString += "\tCompute version: " + to_string(props.major) + "." + to_string(props.minor) + "\n";
		outString += "\tcudaDeviceProp::totalGlobalMem: " + to_string(props.totalGlobalMem) + "\n";
	}
	ETHCU_LOG(outString);
}

void ethash_cu_miner::finish()
{
	for (unsigned i = 0; i != s_numStreams; i++) {
		cudaStreamDestroy(m_streams[i]);
		m_streams[i] = 0;
	}
	cudaDeviceReset();
}

bool ethash_cu_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned _deviceId)
{
	int device_count = getNumDevices();

	if (device_count == 0)
		return false;

	// use selected device
	int device_num = std::min<int>((int)_deviceId, device_count - 1);

	cudaDeviceProp device_props;
	if (cudaGetDeviceProperties(&device_props, device_num) == cudaErrorInvalidDevice)
	{
		cout << cudaGetErrorString(cudaErrorInvalidDevice) << endl;
		return false;
	}

	cout << "Using device: " << device_props.name << "(" << device_props.major << "." << device_props.minor << ")" << endl;

	cudaError_t r = cudaSetDevice(device_num);
	if (r != cudaSuccess)
	{
		cout << cudaGetErrorString(r) << endl;
		return false;
	}
	cudaDeviceReset();
	cudaSetDeviceFlags(cudaDeviceScheduleBlockingSync);
	cudaDeviceSetCacheConfig(cudaFuncCachePreferL1);

	m_hash_buf	 = new void *[s_numStreams];
	m_search_buf = new uint32_t *[s_numStreams];
	m_streams = new cudaStream_t[s_numStreams];

	// patch source code
	cudaError result;

	uint32_t dagSize128 = (unsigned)(_dagSize / ETHASH_MIX_BYTES);
	unsigned max_outputs = c_max_search_results;

	result = set_constants(&dagSize128, &max_outputs);

	// create buffer for dag
	result = cudaMalloc(&m_dag_ptr, _dagSize);

	// create buffer for header256
	result = cudaMalloc(&m_header, 32);

	// copy dag to CPU.
    result = cudaMemcpy(m_dag_ptr, _dag, _dagSize, cudaMemcpyHostToDevice);

	// create mining buffers
	for (unsigned i = 0; i != s_numStreams; ++i)
	{
		result = cudaMallocHost(&m_hash_buf[i], 32 * c_hash_batch_size);
		result = cudaMallocHost(&m_search_buf[i], (c_max_search_results + 1) * sizeof(uint32_t));
		result = cudaStreamCreate(&m_streams[i]);
	}
	if (result != cudaSuccess)
	{
		cout << cudaGetErrorString(result) << endl;
		return false;
	}
	return true;
}

/**
 * Prevent High CPU usage while waiting for an async task
 */
static unsigned waitStream(cudaStream_t stream)
{
	unsigned wait_ms = 0;
	while (cudaStreamQuery(stream) == cudaErrorNotReady) {
		this_thread::sleep_for(chrono::milliseconds(10));
		wait_ms += 10;
	}
	return wait_ms;
}

void ethash_cu_miner::search(uint8_t const* header, uint64_t target, search_hook& hook)
{
	struct pending_batch
	{
		uint64_t start_nonce;
		unsigned buf;
	};
	std::queue<pending_batch> pending;

	static uint32_t const c_zero = 0;

	// update header constant buffer
	cudaMemcpy(m_header, header, 32, cudaMemcpyHostToDevice);
	for (unsigned i = 0; i != s_numStreams; ++i)
	{
		cudaMemcpy(m_search_buf[i], &c_zero, 4, cudaMemcpyHostToDevice);
	}
	cudaError err = cudaGetLastError();
	if (cudaSuccess != err)
	{
		throw std::runtime_error(cudaGetErrorString(err));
	}

	unsigned buf = 0;
	std::random_device engine;
	uint64_t start_nonce = std::uniform_int_distribution<uint64_t>()(engine);
	for (;; start_nonce += s_gridSize)
	{
		run_ethash_search(s_gridSize, s_blockSize, m_streams[buf], m_search_buf[buf], m_header, m_dag_ptr, start_nonce, target);

		pending.push({ start_nonce, buf });
		buf = (buf + 1) % s_numStreams;

		// read results
		if (pending.size() == s_numStreams)
		{
			pending_batch const& batch = pending.front();

			uint32_t results[1 + c_max_search_results];

			if (!s_highCPU)
				waitStream(m_streams[buf]); // 28ms
			cudaMemcpyAsync(results, m_search_buf[batch.buf], (1 + c_max_search_results) * sizeof(uint32_t), cudaMemcpyHostToHost, m_streams[batch.buf]);

			unsigned num_found = std::min<unsigned>(results[0], c_max_search_results);
			uint64_t nonces[c_max_search_results];
			for (unsigned i = 0; i != num_found; ++i)
			{
				nonces[i] = batch.start_nonce + results[i + 1];
				//cout << results[i + 1] << ", ";
			}
			//if (num_found > 0)
			//	cout << endl;

			bool exit = num_found && hook.found(nonces, num_found);
			exit |= hook.searched(batch.start_nonce, s_gridSize * s_blockSize); // always report searched before exit
			if (exit)
				break;

			start_nonce += s_gridSize * s_blockSize;
			// reset search buffer if we're still going
			if (num_found)
				cudaMemcpyAsync(m_search_buf[batch.buf], &c_zero, 4, cudaMemcpyHostToDevice, m_streams[batch.buf]);

			cudaError err = cudaGetLastError();
			if (cudaSuccess != err)
			{
				throw std::runtime_error(cudaGetErrorString(err));
			}
			pending.pop();
		}
	}
}

<commit_msg>linux build fixes<commit_after>/*
  This file is part of c-ethash.

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

  c-ethash is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file ethash_cu_miner.cpp
* @author Tim Hughes <tim@twistedfury.com>
* @date 2015
*/

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <queue>
#include <random>
#include <atomic>
#include <sstream>
#include <vector>
#include <chrono>
#include <thread>
#include <libethash/util.h>
#include <libethash/ethash.h>
#include <libethash/internal.h>
#include <cuda_runtime.h>
#include "ethash_cu_miner.h"
#include "ethash_cu_miner_kernel_globals.h"

#define ETHASH_BYTES 32

// workaround lame platforms
#if !CL_VERSION_1_2
#define CL_MAP_WRITE_INVALIDATE_REGION CL_MAP_WRITE
#define CL_MEM_HOST_READ_ONLY 0
#endif

#undef min
#undef max

using namespace std;

unsigned const ethash_cu_miner::c_defaultBlockSize = 128;
unsigned const ethash_cu_miner::c_defaultGridSize = 2048; // * CL_DEFAULT_LOCAL_WORK_SIZE
unsigned const ethash_cu_miner::c_defaultNumStreams = 2;

#if defined(_WIN32)
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
static std::atomic_flag s_logSpin = ATOMIC_FLAG_INIT;
#define ETHCU_LOG(_contents) \
	do \
			{ \
		std::stringstream ss; \
		ss << _contents; \
						while (s_logSpin.test_and_set(std::memory_order_acquire)) {} \
		OutputDebugStringA(ss.str().c_str()); \
		cerr << ss.str() << endl << flush; \
		s_logSpin.clear(std::memory_order_release); \
			} while (false)
#else
#define ETHCU_LOG(_contents) cout << "[CUDA]:" << _contents << endl
#endif

#define CUDA_SAFE_CALL(call)                                          \
do {                                                                  \
	cudaError_t err = call;                                           \
	if (cudaSuccess != err) {                                         \
		fprintf(stderr, "Cuda error in func '%s' at line %i : %s.\n", \
		         __FUNCTION__, __LINE__, cudaGetErrorString(err) );   \
		exit(EXIT_FAILURE);                                           \
		}                                                                 \
} while (0)

ethash_cu_miner::search_hook::~search_hook() {}

ethash_cu_miner::ethash_cu_miner()
{
}

std::string ethash_cu_miner::platform_info(unsigned _deviceId)
{
	int runtime_version;
	int device_count;

	device_count = getNumDevices();

	if (device_count == 0)
		return std::string();

	CUDA_SAFE_CALL(cudaRuntimeGetVersion(&runtime_version));

	// use selected default device
	int device_num = std::min<int>((int)_deviceId, device_count - 1);
	cudaDeviceProp device_props;

	CUDA_SAFE_CALL(cudaGetDeviceProperties(&device_props, device_num));

	char platform[5];
	int version_major = runtime_version / 1000;
	int version_minor = (runtime_version - (version_major * 1000)) / 10;
	sprintf(platform, "%d.%d", version_major, version_minor);

	char compute[5];
	sprintf(compute, "%d.%d", device_props.major, device_props.minor);

	return "{ \"platform\": \"CUDA " + std::string(platform) + "\", \"device\": \"" + std::string(device_props.name) + "\", \"version\": \"Compute " + std::string(compute) + "\" }";

}

unsigned ethash_cu_miner::getNumDevices()
{
	int device_count;
	CUDA_SAFE_CALL(cudaGetDeviceCount(&device_count));
	return device_count;
}

bool ethash_cu_miner::configureGPU(
	unsigned _blockSize,
	unsigned _gridSize,
	unsigned _numStreams,
	unsigned _extraGPUMemory,
	bool	 _highcpu,
	uint64_t _currentBlock
	)
{
	s_blockSize = _blockSize;
	s_gridSize = _gridSize;
	s_extraRequiredGPUMem = _extraGPUMemory;
	s_numStreams = _numStreams;
	s_highCPU = _highcpu;

	// by default let's only consider the DAG of the first epoch
	uint64_t dagSize = ethash_get_datasize(_currentBlock);
	uint64_t requiredSize = dagSize + _extraGPUMemory;
	for (unsigned int i = 0; i < getNumDevices(); i++)
	{
		cudaDeviceProp props;
		CUDA_SAFE_CALL(cudaGetDeviceProperties(&props, i));
		if (props.totalGlobalMem >= requiredSize)
		{
			ETHCU_LOG(
				"Found suitable CUDA device [" << string(props.name)
				<< "] with " << props.totalGlobalMem << " bytes of GPU memory"
				);
			return true;
		}

		ETHCU_LOG(
			"CUDA device " << string(props.name)
			<< " has insufficient GPU memory." << to_string(props.totalGlobalMem) <<
			" bytes of memory found < " << to_string(requiredSize) << " bytes of memory required"
			);
	}
	return false;
}

unsigned ethash_cu_miner::s_extraRequiredGPUMem;
unsigned ethash_cu_miner::s_blockSize = ethash_cu_miner::c_defaultBlockSize;
unsigned ethash_cu_miner::s_gridSize = ethash_cu_miner::c_defaultGridSize;
unsigned ethash_cu_miner::s_numStreams = ethash_cu_miner::c_defaultNumStreams;
bool ethash_cu_miner::s_highCPU = false;

void ethash_cu_miner::listDevices()
{
	string outString = "\nListing CUDA devices.\nFORMAT: [deviceID] deviceName\n";
	unsigned int i = 0;
	for (unsigned int i = 0; i < getNumDevices(); i++)
	{
		cudaDeviceProp props;
		CUDA_SAFE_CALL(cudaGetDeviceProperties(&props, i));

		outString += "[" + to_string(i) + "] " + string(props.name) + "\n";
		outString += "\tCompute version: " + to_string(props.major) + "." + to_string(props.minor) + "\n";
		outString += "\tcudaDeviceProp::totalGlobalMem: " + to_string(props.totalGlobalMem) + "\n";
	}
	ETHCU_LOG(outString);
}

void ethash_cu_miner::finish()
{
	for (unsigned i = 0; i != s_numStreams; i++) {
		cudaStreamDestroy(m_streams[i]);
		m_streams[i] = 0;
	}
	cudaDeviceReset();
}

bool ethash_cu_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned _deviceId)
{
	int device_count = getNumDevices();

	if (device_count == 0)
		return false;

	// use selected device
	int device_num = std::min<int>((int)_deviceId, device_count - 1);

	cudaDeviceProp device_props;
	if (cudaGetDeviceProperties(&device_props, device_num) == cudaErrorInvalidDevice)
	{
		cout << cudaGetErrorString(cudaErrorInvalidDevice) << endl;
		return false;
	}

	cout << "Using device: " << device_props.name << "(" << device_props.major << "." << device_props.minor << ")" << endl;

	cudaError_t r = cudaSetDevice(device_num);
	if (r != cudaSuccess)
	{
		cout << cudaGetErrorString(r) << endl;
		return false;
	}
	cudaDeviceReset();
	cudaSetDeviceFlags(cudaDeviceScheduleBlockingSync);
	cudaDeviceSetCacheConfig(cudaFuncCachePreferL1);

	m_hash_buf	 = new void *[s_numStreams];
	m_search_buf = new uint32_t *[s_numStreams];
	m_streams = new cudaStream_t[s_numStreams];

	// patch source code
	cudaError result;

	uint32_t dagSize128 = (unsigned)(_dagSize / ETHASH_MIX_BYTES);
	unsigned max_outputs = c_max_search_results;

	result = set_constants(&dagSize128, &max_outputs);

	// create buffer for dag
	result = cudaMalloc(&m_dag_ptr, _dagSize);

	// create buffer for header256
	result = cudaMalloc(&m_header, 32);

	// copy dag to CPU.
    result = cudaMemcpy(m_dag_ptr, _dag, _dagSize, cudaMemcpyHostToDevice);

	// create mining buffers
	for (unsigned i = 0; i != s_numStreams; ++i)
	{
		result = cudaMallocHost(&m_hash_buf[i], 32 * c_hash_batch_size);
		result = cudaMallocHost(&m_search_buf[i], (c_max_search_results + 1) * sizeof(uint32_t));
		result = cudaStreamCreate(&m_streams[i]);
	}
	if (result != cudaSuccess)
	{
		cout << cudaGetErrorString(result) << endl;
		return false;
	}
	return true;
}

/**
 * Prevent High CPU usage while waiting for an async task
 */
static unsigned waitStream(cudaStream_t stream)
{
	unsigned wait_ms = 0;
	while (cudaStreamQuery(stream) == cudaErrorNotReady) {
		this_thread::sleep_for(chrono::milliseconds(10));
		wait_ms += 10;
	}
	return wait_ms;
}

void ethash_cu_miner::search(uint8_t const* header, uint64_t target, search_hook& hook)
{
	struct pending_batch
	{
		uint64_t start_nonce;
		unsigned buf;
	};
	std::queue<pending_batch> pending;

	static uint32_t const c_zero = 0;

	// update header constant buffer
	cudaMemcpy(m_header, header, 32, cudaMemcpyHostToDevice);
	for (unsigned i = 0; i != s_numStreams; ++i)
	{
		cudaMemcpy(m_search_buf[i], &c_zero, 4, cudaMemcpyHostToDevice);
	}
	cudaError err = cudaGetLastError();
	if (cudaSuccess != err)
	{
		throw std::runtime_error(cudaGetErrorString(err));
	}

	unsigned buf = 0;
	std::random_device engine;
	uint64_t start_nonce = std::uniform_int_distribution<uint64_t>()(engine);
	for (;; start_nonce += s_gridSize)
	{
		run_ethash_search(s_gridSize, s_blockSize, m_streams[buf], m_search_buf[buf], m_header, m_dag_ptr, start_nonce, target);

		pending.push({ start_nonce, buf });
		buf = (buf + 1) % s_numStreams;

		// read results
		if (pending.size() == s_numStreams)
		{
			pending_batch const& batch = pending.front();

			uint32_t results[1 + c_max_search_results];

			if (!s_highCPU)
				waitStream(m_streams[buf]); // 28ms
			cudaMemcpyAsync(results, m_search_buf[batch.buf], (1 + c_max_search_results) * sizeof(uint32_t), cudaMemcpyHostToHost, m_streams[batch.buf]);

			unsigned num_found = std::min<unsigned>(results[0], c_max_search_results);
			uint64_t nonces[c_max_search_results];
			for (unsigned i = 0; i != num_found; ++i)
			{
				nonces[i] = batch.start_nonce + results[i + 1];
				//cout << results[i + 1] << ", ";
			}
			//if (num_found > 0)
			//	cout << endl;

			bool exit = num_found && hook.found(nonces, num_found);
			exit |= hook.searched(batch.start_nonce, s_gridSize * s_blockSize); // always report searched before exit
			if (exit)
				break;

			start_nonce += s_gridSize * s_blockSize;
			// reset search buffer if we're still going
			if (num_found)
				cudaMemcpyAsync(m_search_buf[batch.buf], &c_zero, 4, cudaMemcpyHostToDevice, m_streams[batch.buf]);

			cudaError err = cudaGetLastError();
			if (cudaSuccess != err)
			{
				throw std::runtime_error(cudaGetErrorString(err));
			}
			pending.pop();
		}
	}
}

<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright (C) 2015 ScyllaDB
 *
 * Modified by ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cql3/column_condition.hh"
#include "statements/request_validations.hh"
#include "unimplemented.hh"
#include "lists.hh"
#include "maps.hh"
#include <boost/range/algorithm_ext/push_back.hpp>

namespace {

void validate_operation_on_durations(const abstract_type& type, const cql3::operator_type& op) {
    using cql3::statements::request_validations::check_false;

    if (op.is_slice() && type.references_duration()) {
        check_false(type.is_collection(), "Slice conditions are not supported on collections containing durations");
        check_false(type.is_tuple(), "Slice conditions are not supported on tuples containing durations");
        check_false(type.is_user_type(), "Slice conditions are not supported on UDTs containing durations");

        // We're a duration.
        throw exceptions::invalid_request_exception(format("Slice conditions are not supported on durations"));
    }
}

}

namespace cql3 {

bool
column_condition::uses_function(const sstring& ks_name, const sstring& function_name) {
    if (bool(_collection_element) && _collection_element->uses_function(ks_name, function_name)) {
        return true;
    }
    if (bool(_value) && _value->uses_function(ks_name, function_name)) {
        return true;
    }
    if (!_in_values.empty()) {
        for (auto&& value : _in_values) {
            if (bool(value) && value->uses_function(ks_name, function_name)) {
                return true;
            }
        }
    }
    return false;
}

void column_condition::collect_marker_specificaton(::shared_ptr<variable_specifications> bound_names) {
    if (_collection_element) {
        _collection_element->collect_marker_specification(bound_names);
    }
    if (!_in_values.empty()) {
        for (auto&& value : _in_values) {
            value->collect_marker_specification(bound_names);
        }
    }
    _value->collect_marker_specification(bound_names);
}

::shared_ptr<column_condition>
column_condition::raw::prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
    if (receiver.type->is_counter()) {
        throw exceptions::invalid_request_exception("Conditions on counters are not supported");
    }
    shared_ptr<term> collection_element_term;
    shared_ptr<column_specification> value_spec = receiver.column_specification;

    if (_collection_element) {
        if (!receiver.type->is_collection()) {
            throw exceptions::invalid_request_exception(format("Invalid element access syntax for non-collection column {}",
                        receiver.name_as_text()));
        }
        // Pass  a correct type specification to the collection_element->prepare(), so that it can
        // later be used to validate the parameter type is compatible with receiver type.
        shared_ptr<column_specification> element_spec;
        auto ctype = static_cast<const collection_type_impl*>(receiver.type.get());
        if (ctype->get_kind() == abstract_type::kind::list) {
            element_spec = lists::index_spec_of(receiver.column_specification);
            value_spec = lists::value_spec_of(receiver.column_specification);
        } else if (ctype->get_kind() == abstract_type::kind::map) {
            element_spec = maps::key_spec_of(*receiver.column_specification);
            value_spec = maps::value_spec_of(*receiver.column_specification);
        } else if (ctype->get_kind() == abstract_type::kind::set) {
            throw exceptions::invalid_request_exception(format("Invalid element access syntax for set column {}",
                        receiver.name_as_text()));
        } else {
            assert(false);
        }
        collection_element_term = _collection_element->prepare(db, keyspace, element_spec);
    }

    if (_op.is_compare()) {
        validate_operation_on_durations(*receiver.type, _op);
        return column_condition::condition(receiver, collection_element_term, _value->prepare(db, keyspace, value_spec), _op);
    }
    assert(_op == operator_type::IN);

    if (_in_marker) {
        assert(_in_values.empty());
        shared_ptr<term> multi_item_term = _in_marker->prepare(db, keyspace, value_spec);
        return column_condition::in_condition(receiver, collection_element_term, multi_item_term, {});
    }
    // Both _in_values and in _in_marker can be missing in case of empty IN list: "a IN ()"
    std::vector<::shared_ptr<term>> terms;
    terms.reserve(_in_values.size());
    for (auto&& value : _in_values) {
        terms.push_back(value->prepare(db, keyspace, value_spec));
    }
    return column_condition::in_condition(receiver, collection_element_term, {}, std::move(terms));
}

} // end of namespace cql3
<commit_msg>lwt: check for unsupported collection type in condition element access<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright (C) 2015 ScyllaDB
 *
 * Modified by ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cql3/column_condition.hh"
#include "statements/request_validations.hh"
#include "unimplemented.hh"
#include "lists.hh"
#include "maps.hh"
#include <boost/range/algorithm_ext/push_back.hpp>

namespace {

void validate_operation_on_durations(const abstract_type& type, const cql3::operator_type& op) {
    using cql3::statements::request_validations::check_false;

    if (op.is_slice() && type.references_duration()) {
        check_false(type.is_collection(), "Slice conditions are not supported on collections containing durations");
        check_false(type.is_tuple(), "Slice conditions are not supported on tuples containing durations");
        check_false(type.is_user_type(), "Slice conditions are not supported on UDTs containing durations");

        // We're a duration.
        throw exceptions::invalid_request_exception(format("Slice conditions are not supported on durations"));
    }
}

}

namespace cql3 {

bool
column_condition::uses_function(const sstring& ks_name, const sstring& function_name) {
    if (bool(_collection_element) && _collection_element->uses_function(ks_name, function_name)) {
        return true;
    }
    if (bool(_value) && _value->uses_function(ks_name, function_name)) {
        return true;
    }
    if (!_in_values.empty()) {
        for (auto&& value : _in_values) {
            if (bool(value) && value->uses_function(ks_name, function_name)) {
                return true;
            }
        }
    }
    return false;
}

void column_condition::collect_marker_specificaton(::shared_ptr<variable_specifications> bound_names) {
    if (_collection_element) {
        _collection_element->collect_marker_specification(bound_names);
    }
    if (!_in_values.empty()) {
        for (auto&& value : _in_values) {
            value->collect_marker_specification(bound_names);
        }
    }
    _value->collect_marker_specification(bound_names);
}

::shared_ptr<column_condition>
column_condition::raw::prepare(database& db, const sstring& keyspace, const column_definition& receiver) {
    if (receiver.type->is_counter()) {
        throw exceptions::invalid_request_exception("Conditions on counters are not supported");
    }
    shared_ptr<term> collection_element_term;
    shared_ptr<column_specification> value_spec = receiver.column_specification;

    if (_collection_element) {
        if (!receiver.type->is_collection()) {
            throw exceptions::invalid_request_exception(format("Invalid element access syntax for non-collection column {}",
                        receiver.name_as_text()));
        }
        // Pass  a correct type specification to the collection_element->prepare(), so that it can
        // later be used to validate the parameter type is compatible with receiver type.
        shared_ptr<column_specification> element_spec;
        auto ctype = static_cast<const collection_type_impl*>(receiver.type.get());
        if (ctype->get_kind() == abstract_type::kind::list) {
            element_spec = lists::index_spec_of(receiver.column_specification);
            value_spec = lists::value_spec_of(receiver.column_specification);
        } else if (ctype->get_kind() == abstract_type::kind::map) {
            element_spec = maps::key_spec_of(*receiver.column_specification);
            value_spec = maps::value_spec_of(*receiver.column_specification);
        } else if (ctype->get_kind() == abstract_type::kind::set) {
            throw exceptions::invalid_request_exception(format("Invalid element access syntax for set column {}",
                        receiver.name_as_text()));
        } else {
            throw exceptions::invalid_request_exception(
                    format("Unsupported collection type {} in a condition with element access", ctype->cql3_type_name()));
        }
        collection_element_term = _collection_element->prepare(db, keyspace, element_spec);
    }

    if (_op.is_compare()) {
        validate_operation_on_durations(*receiver.type, _op);
        return column_condition::condition(receiver, collection_element_term, _value->prepare(db, keyspace, value_spec), _op);
    }
    assert(_op == operator_type::IN);

    if (_in_marker) {
        assert(_in_values.empty());
        shared_ptr<term> multi_item_term = _in_marker->prepare(db, keyspace, value_spec);
        return column_condition::in_condition(receiver, collection_element_term, multi_item_term, {});
    }
    // Both _in_values and in _in_marker can be missing in case of empty IN list: "a IN ()"
    std::vector<::shared_ptr<term>> terms;
    terms.reserve(_in_values.size());
    for (auto&& value : _in_values) {
        terms.push_back(value->prepare(db, keyspace, value_spec));
    }
    return column_condition::in_condition(receiver, collection_element_term, {}, std::move(terms));
}

} // end of namespace cql3
<|endoftext|>


<commit_before>#include "firestore/src/ios/query_ios.h"

#include <utility>

#include "app/src/assert.h"
#include "firestore/src/include/firebase/firestore.h"
#include "firestore/src/ios/converter_ios.h"
#include "firestore/src/ios/document_snapshot_ios.h"
#include "firestore/src/ios/hard_assert_ios.h"
#include "firestore/src/ios/listener_ios.h"
#include "firestore/src/ios/promise_ios.h"
#include "firestore/src/ios/set_options_ios.h"
#include "firestore/src/ios/source_ios.h"
#include "firestore/src/ios/util_ios.h"
#include "Firestore/core/src/firebase/firestore/api/listener_registration.h"
#include "Firestore/core/src/firebase/firestore/core/filter.h"
#include "Firestore/core/src/firebase/firestore/core/listen_options.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
#include "Firestore/core/src/firebase/firestore/util/exception.h"

namespace firebase {
namespace firestore {

using model::DocumentKey;
using model::ResourcePath;
using util::ThrowInvalidArgument;

QueryInternal::QueryInternal(api::Query&& query)
    : query_{std::move(query)},
      promise_factory_{PromiseFactory<AsyncApis>::Create(this)},
      user_data_converter_{&firestore_internal()->database_id()} {}

Firestore* QueryInternal::firestore() { return GetFirestore(&query_); }

FirestoreInternal* QueryInternal::firestore_internal() {
  return GetFirestoreInternal(&query_);
}

const FirestoreInternal* QueryInternal::firestore_internal() const {
  return GetFirestoreInternal(&query_);
}

Query QueryInternal::OrderBy(const FieldPath& field_path,
                             Query::Direction direction) {
  api::Query decorated = query_.OrderBy(
      GetInternal(field_path), direction == Query::Direction::kDescending);
  return MakePublic(std::move(decorated));
}

Query QueryInternal::Limit(int32_t limit) {
  return MakePublic(query_.Limit(limit));
}

Future<QuerySnapshot> QueryInternal::Get(Source source) {
  auto promise = promise_factory_.CreatePromise<QuerySnapshot>(AsyncApis::kGet);
  auto listener = ListenerWithPromise<api::QuerySnapshot>(promise);
  query_.GetDocuments(ToCoreApi(source), std::move(listener));
  return promise.future();
}

Future<QuerySnapshot> QueryInternal::GetLastResult() {
  return promise_factory_.LastResult<QuerySnapshot>(AsyncApis::kGet);
}

Query QueryInternal::Where(const FieldPath& field_path, Operator op,
                           const FieldValue& value) const {
  const model::FieldPath& path = GetInternal(field_path);
  model::FieldValue parsed = user_data_converter_.ParseQueryValue(value);
  auto describer = [&value] { return Describe(value.type()); };

  api::Query decorated = query_.Filter(path, op, std::move(parsed), describer);
  return MakePublic(std::move(decorated));
}

Query QueryInternal::WithBound(BoundPosition bound_pos,
                               const DocumentSnapshot& snapshot) const {
  core::Bound bound = ToBound(bound_pos, snapshot);
  return MakePublic(CreateQueryWithBound(bound_pos, std::move(bound)));
}

Query QueryInternal::WithBound(BoundPosition bound_pos,
                               const std::vector<FieldValue>& values) const {
  core::Bound bound = ToBound(bound_pos, values);
  return MakePublic(CreateQueryWithBound(bound_pos, std::move(bound)));
}

ListenerRegistration QueryInternal::AddSnapshotListener(
    MetadataChanges metadata_changes, EventListener<QuerySnapshot>* listener) {
  auto options = core::ListenOptions::FromIncludeMetadataChanges(
      metadata_changes == MetadataChanges::kInclude);
  auto result = query_.AddSnapshotListener(
      options, ListenerWithEventListener<api::QuerySnapshot>(listener));
  return MakePublic(std::move(result), firestore_internal());
}

ListenerRegistration QueryInternal::AddSnapshotListener(
    MetadataChanges metadata_changes,
    std::function<void(const QuerySnapshot&, Error)> callback) {
  auto options = core::ListenOptions::FromIncludeMetadataChanges(
      metadata_changes == MetadataChanges::kInclude);
  auto result = query_.AddSnapshotListener(
      options, ListenerWithCallback<api::QuerySnapshot>(std::move(callback)));
  return MakePublic(std::move(result), firestore_internal());
}

bool operator==(const QueryInternal& lhs, const QueryInternal& rhs) {
  return lhs.query_ == rhs.query_;
}

core::Bound QueryInternal::ToBound(
    BoundPosition bound_pos, const DocumentSnapshot& public_snapshot) const {
  if (!public_snapshot.exists()) {
    ThrowInvalidArgument(
        "Invalid query. You are trying to start or end a query using a "
        "document that doesn't exist.");
  }

  const api::DocumentSnapshot& api_snapshot = GetCoreApi(public_snapshot);
  const model::DocumentKey& key =
      api_snapshot.internal_document().value().key();
  const model::DatabaseId& database_id = firestore_internal()->database_id();
  const core::Query& internal_query = query_.query();
  std::vector<model::FieldValue> components;

  // Because people expect to continue/end a query at the exact document
  // provided, we need to use the implicit sort order rather than the explicit
  // sort order, because it's guaranteed to contain the document key. That way
  // the position becomes unambiguous and the query continues/ends exactly at
  // the provided document. Without the key (by using the explicit sort orders),
  // multiple documents could match the position, yielding duplicate results.

  for (const core::OrderBy& order_by : internal_query.order_bys()) {
    const model::FieldPath& field_path = order_by.field();

    if (field_path.IsKeyFieldPath()) {
      components.push_back(model::FieldValue::FromReference(database_id, key));
      continue;
    }

    absl::optional<model::FieldValue> value = api_snapshot.GetValue(field_path);
    if (!value) {
      // TODO(b/147444199): use string formatting.
      // ThrowInvalidArgument(
      //     "Invalid query. You are trying to start or end a query using a "
      //     "document for which the field '%s' (used as the order by) does not
      //     " "exist.", field_path.CanonicalString());
      auto message = std::string(
                         "Invalid query. You are trying to start or end a "
                         "query using a document for which the field '") +
                     field_path.CanonicalString() +
                     "' (used as the order by) does not exist.";
      ThrowInvalidArgument(message.c_str());
    }

    if (value->type() == model::FieldValue::Type::ServerTimestamp) {
      // TODO(b/147444199): use string formatting.
      // ThrowInvalidArgument(
      //     "Invalid query. You are trying to start or end a query using a "
      //     "document for which the field '%s' is an uncommitted server "
      //     "timestamp. (Since the value of this field is unknown, you cannot "
      //     "start/end a query with it.)",
      //     field_path.CanonicalString());
      auto message =
          std::string(
              "Invalid query. You are trying to start or end a query using a "
              "document for which the field '") +
          field_path.CanonicalString() +
          "' is an uncommitted server timestamp. (Since the value of this"
          "field is unknown, you cannot start/end a query with it.)";
      ThrowInvalidArgument(message.c_str());
    }

    components.push_back(std::move(value).value());
  }

  return core::Bound{std::move(components), IsBefore(bound_pos)};
}

core::Bound QueryInternal::ToBound(
    BoundPosition bound_pos,
    const std::vector<FieldValue>& field_values) const {
  const core::Query& internal_query = query_.query();
  // Use explicit order bys  because it has to match the query the user made.
  const core::OrderByList& explicit_order_bys =
      internal_query.explicit_order_bys();

  if (field_values.size() > explicit_order_bys.size()) {
    ThrowInvalidArgument(
        "Invalid query. You are trying to start or end a query using more "
        "values than were specified in the order by.");
  }

  std::vector<model::FieldValue> components;

  for (int i = 0; i != field_values.size(); ++i) {
    model::FieldValue field_value =
        user_data_converter_.ParseQueryValue(field_values[i]);
    const core::OrderBy& order_by = explicit_order_bys[i];
    if (order_by.field().IsKeyFieldPath()) {
      components.push_back(ConvertDocumentId(field_value, internal_query));
    } else {
      components.push_back(std::move(field_value));
    }
  }

  return core::Bound{std::move(components), IsBefore(bound_pos)};
}

model::FieldValue QueryInternal::ConvertDocumentId(
    const model::FieldValue& from, const core::Query& internal_query) const {
  if (from.type() != model::FieldValue::Type::String) {
    ThrowInvalidArgument(
        "Invalid query. Expected a string for the document ID.");
  }
  const std::string& document_id = from.string_value();

  if (!internal_query.IsCollectionGroupQuery() &&
      document_id.find('/') != std::string::npos) {
    // TODO(b/147444199): use string formatting.
    // ThrowInvalidArgument(
    //     "Invalid query. When querying a collection and ordering by document "
    //     "ID, you must pass a plain document ID, but '%s' contains a slash.",
    //     document_id);
    auto message = std::string(
                       "Invalid query. When querying a collection and ordering "
                       "by document "
                       "ID, you must pass a plain document ID, but '") +
                   document_id + "' contains a slash.";
    ThrowInvalidArgument(message.c_str());
  }

  ResourcePath path =
      internal_query.path().Append(ResourcePath::FromString(document_id));
  if (!DocumentKey::IsDocumentKey(path)) {
    // TODO(b/147444199): use string formatting.
    // ThrowInvalidArgument(
    //     "Invalid query. When querying a collection group and ordering by "
    //     "document ID, you must pass a value that results in a valid document
    //     " "path, but '%s' is not because it contains an odd number of
    //     segments.", path.CanonicalString());
    auto message =
        std::string(
            "Invalid query. When querying a collection group and ordering by "
            "document ID, you must pass a value that results in a valid "
            "document path, but '") +
        path.CanonicalString() +
        "' is not because it contains an odd number of segments.";
    ThrowInvalidArgument(message.c_str());
  }

  const model::DatabaseId& database_id = firestore_internal()->database_id();
  return model::FieldValue::FromReference(database_id,
                                          DocumentKey{std::move(path)});
}

api::Query QueryInternal::CreateQueryWithBound(BoundPosition bound_pos,
                                               core::Bound&& bound) const {
  switch (bound_pos) {
    case BoundPosition::kStartAt:
    case BoundPosition::kStartAfter:
      return query_.StartAt(std::move(bound));

    case BoundPosition::kEndBefore:
    case BoundPosition::kEndAt:
      return query_.EndAt(std::move(bound));
  }

  UNREACHABLE();
}

bool QueryInternal::IsBefore(BoundPosition bound_pos) {
  switch (bound_pos) {
    case BoundPosition::kStartAt:
    case BoundPosition::kEndBefore:
      return true;

    case BoundPosition::kStartAfter:
    case BoundPosition::kEndAt:
      return false;
  }

  UNREACHABLE();
}

}  // namespace firestore
}  // namespace firebase
<commit_msg>Import Firestore release 1.10.0 from GitHub (Firebase 6.16.0, M63).<commit_after>#include "firestore/src/ios/query_ios.h"

#include <utility>

#include "app/src/assert.h"
#include "firestore/src/include/firebase/firestore.h"
#include "firestore/src/ios/converter_ios.h"
#include "firestore/src/ios/document_snapshot_ios.h"
#include "firestore/src/ios/hard_assert_ios.h"
#include "firestore/src/ios/listener_ios.h"
#include "firestore/src/ios/promise_ios.h"
#include "firestore/src/ios/set_options_ios.h"
#include "firestore/src/ios/source_ios.h"
#include "firestore/src/ios/util_ios.h"
#include "Firestore/core/src/firebase/firestore/api/listener_registration.h"
#include "Firestore/core/src/firebase/firestore/core/filter.h"
#include "Firestore/core/src/firebase/firestore/core/listen_options.h"
#include "Firestore/core/src/firebase/firestore/model/document_key.h"
#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "Firestore/core/src/firebase/firestore/model/resource_path.h"
#include "Firestore/core/src/firebase/firestore/util/exception.h"

namespace firebase {
namespace firestore {

using model::DocumentKey;
using model::ResourcePath;
using util::ThrowInvalidArgument;

QueryInternal::QueryInternal(api::Query&& query)
    : query_{std::move(query)},
      promise_factory_{PromiseFactory<AsyncApis>::Create(this)},
      user_data_converter_{&firestore_internal()->database_id()} {}

Firestore* QueryInternal::firestore() { return GetFirestore(&query_); }

FirestoreInternal* QueryInternal::firestore_internal() {
  return GetFirestoreInternal(&query_);
}

const FirestoreInternal* QueryInternal::firestore_internal() const {
  return GetFirestoreInternal(&query_);
}

Query QueryInternal::OrderBy(const FieldPath& field_path,
                             Query::Direction direction) {
  api::Query decorated = query_.OrderBy(
      GetInternal(field_path), direction == Query::Direction::kDescending);
  return MakePublic(std::move(decorated));
}

Query QueryInternal::Limit(int32_t limit) {
  return MakePublic(query_.LimitToFirst(limit));
}

Future<QuerySnapshot> QueryInternal::Get(Source source) {
  auto promise = promise_factory_.CreatePromise<QuerySnapshot>(AsyncApis::kGet);
  auto listener = ListenerWithPromise<api::QuerySnapshot>(promise);
  query_.GetDocuments(ToCoreApi(source), std::move(listener));
  return promise.future();
}

Future<QuerySnapshot> QueryInternal::GetLastResult() {
  return promise_factory_.LastResult<QuerySnapshot>(AsyncApis::kGet);
}

Query QueryInternal::Where(const FieldPath& field_path, Operator op,
                           const FieldValue& value) const {
  const model::FieldPath& path = GetInternal(field_path);
  model::FieldValue parsed = user_data_converter_.ParseQueryValue(value);
  auto describer = [&value] { return Describe(value.type()); };

  api::Query decorated = query_.Filter(path, op, std::move(parsed), describer);
  return MakePublic(std::move(decorated));
}

Query QueryInternal::WithBound(BoundPosition bound_pos,
                               const DocumentSnapshot& snapshot) const {
  core::Bound bound = ToBound(bound_pos, snapshot);
  return MakePublic(CreateQueryWithBound(bound_pos, std::move(bound)));
}

Query QueryInternal::WithBound(BoundPosition bound_pos,
                               const std::vector<FieldValue>& values) const {
  core::Bound bound = ToBound(bound_pos, values);
  return MakePublic(CreateQueryWithBound(bound_pos, std::move(bound)));
}

ListenerRegistration QueryInternal::AddSnapshotListener(
    MetadataChanges metadata_changes, EventListener<QuerySnapshot>* listener) {
  auto options = core::ListenOptions::FromIncludeMetadataChanges(
      metadata_changes == MetadataChanges::kInclude);
  auto result = query_.AddSnapshotListener(
      options, ListenerWithEventListener<api::QuerySnapshot>(listener));
  return MakePublic(std::move(result), firestore_internal());
}

ListenerRegistration QueryInternal::AddSnapshotListener(
    MetadataChanges metadata_changes,
    std::function<void(const QuerySnapshot&, Error)> callback) {
  auto options = core::ListenOptions::FromIncludeMetadataChanges(
      metadata_changes == MetadataChanges::kInclude);
  auto result = query_.AddSnapshotListener(
      options, ListenerWithCallback<api::QuerySnapshot>(std::move(callback)));
  return MakePublic(std::move(result), firestore_internal());
}

bool operator==(const QueryInternal& lhs, const QueryInternal& rhs) {
  return lhs.query_ == rhs.query_;
}

core::Bound QueryInternal::ToBound(
    BoundPosition bound_pos, const DocumentSnapshot& public_snapshot) const {
  if (!public_snapshot.exists()) {
    ThrowInvalidArgument(
        "Invalid query. You are trying to start or end a query using a "
        "document that doesn't exist.");
  }

  const api::DocumentSnapshot& api_snapshot = GetCoreApi(public_snapshot);
  const model::DocumentKey& key =
      api_snapshot.internal_document().value().key();
  const model::DatabaseId& database_id = firestore_internal()->database_id();
  const core::Query& internal_query = query_.query();
  std::vector<model::FieldValue> components;

  // Because people expect to continue/end a query at the exact document
  // provided, we need to use the implicit sort order rather than the explicit
  // sort order, because it's guaranteed to contain the document key. That way
  // the position becomes unambiguous and the query continues/ends exactly at
  // the provided document. Without the key (by using the explicit sort orders),
  // multiple documents could match the position, yielding duplicate results.

  for (const core::OrderBy& order_by : internal_query.order_bys()) {
    const model::FieldPath& field_path = order_by.field();

    if (field_path.IsKeyFieldPath()) {
      components.push_back(model::FieldValue::FromReference(database_id, key));
      continue;
    }

    absl::optional<model::FieldValue> value = api_snapshot.GetValue(field_path);
    if (!value) {
      // TODO(b/147444199): use string formatting.
      // ThrowInvalidArgument(
      //     "Invalid query. You are trying to start or end a query using a "
      //     "document for which the field '%s' (used as the order by) does not
      //     " "exist.", field_path.CanonicalString());
      auto message = std::string(
                         "Invalid query. You are trying to start or end a "
                         "query using a document for which the field '") +
                     field_path.CanonicalString() +
                     "' (used as the order by) does not exist.";
      ThrowInvalidArgument(message.c_str());
    }

    if (value->type() == model::FieldValue::Type::ServerTimestamp) {
      // TODO(b/147444199): use string formatting.
      // ThrowInvalidArgument(
      //     "Invalid query. You are trying to start or end a query using a "
      //     "document for which the field '%s' is an uncommitted server "
      //     "timestamp. (Since the value of this field is unknown, you cannot "
      //     "start/end a query with it.)",
      //     field_path.CanonicalString());
      auto message =
          std::string(
              "Invalid query. You are trying to start or end a query using a "
              "document for which the field '") +
          field_path.CanonicalString() +
          "' is an uncommitted server timestamp. (Since the value of this"
          "field is unknown, you cannot start/end a query with it.)";
      ThrowInvalidArgument(message.c_str());
    }

    components.push_back(std::move(value).value());
  }

  return core::Bound{std::move(components), IsBefore(bound_pos)};
}

core::Bound QueryInternal::ToBound(
    BoundPosition bound_pos,
    const std::vector<FieldValue>& field_values) const {
  const core::Query& internal_query = query_.query();
  // Use explicit order bys  because it has to match the query the user made.
  const core::OrderByList& explicit_order_bys =
      internal_query.explicit_order_bys();

  if (field_values.size() > explicit_order_bys.size()) {
    ThrowInvalidArgument(
        "Invalid query. You are trying to start or end a query using more "
        "values than were specified in the order by.");
  }

  std::vector<model::FieldValue> components;

  for (int i = 0; i != field_values.size(); ++i) {
    model::FieldValue field_value =
        user_data_converter_.ParseQueryValue(field_values[i]);
    const core::OrderBy& order_by = explicit_order_bys[i];
    if (order_by.field().IsKeyFieldPath()) {
      components.push_back(ConvertDocumentId(field_value, internal_query));
    } else {
      components.push_back(std::move(field_value));
    }
  }

  return core::Bound{std::move(components), IsBefore(bound_pos)};
}

model::FieldValue QueryInternal::ConvertDocumentId(
    const model::FieldValue& from, const core::Query& internal_query) const {
  if (from.type() != model::FieldValue::Type::String) {
    ThrowInvalidArgument(
        "Invalid query. Expected a string for the document ID.");
  }
  const std::string& document_id = from.string_value();

  if (!internal_query.IsCollectionGroupQuery() &&
      document_id.find('/') != std::string::npos) {
    // TODO(b/147444199): use string formatting.
    // ThrowInvalidArgument(
    //     "Invalid query. When querying a collection and ordering by document "
    //     "ID, you must pass a plain document ID, but '%s' contains a slash.",
    //     document_id);
    auto message = std::string(
                       "Invalid query. When querying a collection and ordering "
                       "by document "
                       "ID, you must pass a plain document ID, but '") +
                   document_id + "' contains a slash.";
    ThrowInvalidArgument(message.c_str());
  }

  ResourcePath path =
      internal_query.path().Append(ResourcePath::FromString(document_id));
  if (!DocumentKey::IsDocumentKey(path)) {
    // TODO(b/147444199): use string formatting.
    // ThrowInvalidArgument(
    //     "Invalid query. When querying a collection group and ordering by "
    //     "document ID, you must pass a value that results in a valid document
    //     " "path, but '%s' is not because it contains an odd number of
    //     segments.", path.CanonicalString());
    auto message =
        std::string(
            "Invalid query. When querying a collection group and ordering by "
            "document ID, you must pass a value that results in a valid "
            "document path, but '") +
        path.CanonicalString() +
        "' is not because it contains an odd number of segments.";
    ThrowInvalidArgument(message.c_str());
  }

  const model::DatabaseId& database_id = firestore_internal()->database_id();
  return model::FieldValue::FromReference(database_id,
                                          DocumentKey{std::move(path)});
}

api::Query QueryInternal::CreateQueryWithBound(BoundPosition bound_pos,
                                               core::Bound&& bound) const {
  switch (bound_pos) {
    case BoundPosition::kStartAt:
    case BoundPosition::kStartAfter:
      return query_.StartAt(std::move(bound));

    case BoundPosition::kEndBefore:
    case BoundPosition::kEndAt:
      return query_.EndAt(std::move(bound));
  }

  UNREACHABLE();
}

bool QueryInternal::IsBefore(BoundPosition bound_pos) {
  switch (bound_pos) {
    case BoundPosition::kStartAt:
    case BoundPosition::kEndBefore:
      return true;

    case BoundPosition::kStartAfter:
    case BoundPosition::kEndAt:
      return false;
  }

  UNREACHABLE();
}

}  // namespace firestore
}  // namespace firebase
<|endoftext|>


<commit_before>/*
    kopetestdaction.cpp  -  Kopete Standard Actionds

    Copyright (c) 2001-2002 by Ryan Cumming          <ryan@kde.org>
    Copyright (c) 2002-2003 by Martijn Klingens      <klingens@kde.org>

    Kopete    (c) 2001-2003 by the Kopete developers <kopete-devel@kde.org>

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

#include "kopetestdaction.h"

#include <qapplication.h>

#include <kdebug.h>
#include <kdeversion.h>
#include <kguiitem.h>
#include <klocale.h>
#include <ksettings/dialog.h>
#include <kstandardaction.h>
#include <KStandardGuiItem>
#include <kwin.h>
#include <kcmultidialog.h>
#include <kicon.h>

#include "kopetecontactlist.h"
#include "kopetegroup.h"
#include "kopeteuiglobal.h"
#include <kactioncollection.h>

KSettings::Dialog *KopetePreferencesAction::s_settingsDialog = 0L;

KopetePreferencesAction::KopetePreferencesAction( KActionCollection *parent, const char *name )
: KAction( KIcon(KStandardGuiItem::configure().iconName()), KStandardGuiItem::configure().text(), parent )
{
	connect( this, SIGNAL( triggered(bool) ), this, SLOT( slotShowPreferences() ) );
        parent->addAction( name, this );
}

KopetePreferencesAction::~KopetePreferencesAction()
{
}

void KopetePreferencesAction::slotShowPreferences()
{
	// FIXME: Use static deleter - Martijn
	if ( !s_settingsDialog )
		s_settingsDialog = new KSettings::Dialog( KSettings::Dialog::Static, Kopete::UI::Global::mainWidget() );
	s_settingsDialog->show();

	s_settingsDialog->dialog()->raise();
#ifdef Q_OS_UNIX
	KWin::activateWindow( s_settingsDialog->dialog()->winId() );
#endif
}

KAction * KopeteStdAction::preferences( KActionCollection *parent, const char *name )
{
	return new KopetePreferencesAction( parent, name );
}

KAction * KopeteStdAction::createAction(const QString &text, const KIcon &icon, const QObject *receiver, const char *slot, KActionCollection* parent, const char *name)
{
	KAction *newAction = new KAction(icon, text, parent);
	if(receiver && slot)
	{
		QObject::connect(newAction, SIGNAL(triggered(bool)), receiver, slot);
	}
        parent->addAction(name, newAction);

	return newAction;
}

KAction * KopeteStdAction::chat( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Start &Chat..." ), KIcon("mail_generic"), recvr, slot, parent, name );
}

KAction * KopeteStdAction::sendMessage( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "&Send Single Message..." ), KIcon( "mail_generic" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::contactInfo( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "User &Info" ), KIcon( "messagebox_info" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::sendFile( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Send &File..." ), KIcon( "attach" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::viewHistory( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "View &History..." ), KIcon( "history" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::addGroup( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "&Create Group..." ), KIcon( "folder" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::changeMetaContact( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Cha&nge Meta Contact..." ), KIcon( "move" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::deleteContact( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	KAction *deleteAction = createAction( i18n( "&Delete Contact" ), KIcon( "delete_user" ), recvr, slot, parent, name );
	deleteAction->setShortcut( KShortcut(Qt::Key_Delete) );

	return deleteAction;
}

KAction * KopeteStdAction::changeAlias( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Change A&lias..." ), KIcon( "signature" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::blockContact( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "&Block Contact" ), KIcon( "player_pause" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::unblockContact( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Un&block Contact" ), KIcon( "player_play" ), recvr, slot, parent, name );
}

#include "kopetestdaction.moc"

// vim: set noet ts=4 sts=4 sw=4:

<commit_msg>Fix crash when right-clicking a contact in Contact List.<commit_after>/*
    kopetestdaction.cpp  -  Kopete Standard Actionds

    Copyright (c) 2001-2002 by Ryan Cumming          <ryan@kde.org>
    Copyright (c) 2002-2003 by Martijn Klingens      <klingens@kde.org>

    Kopete    (c) 2001-2003 by the Kopete developers <kopete-devel@kde.org>

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

#include "kopetestdaction.h"

#include <qapplication.h>

#include <kdebug.h>
#include <kdeversion.h>
#include <kguiitem.h>
#include <klocale.h>
#include <ksettings/dialog.h>
#include <kstandardaction.h>
#include <KStandardGuiItem>
#include <kwin.h>
#include <kcmultidialog.h>
#include <kicon.h>

#include "kopetecontactlist.h"
#include "kopetegroup.h"
#include "kopeteuiglobal.h"
#include <kactioncollection.h>

KSettings::Dialog *KopetePreferencesAction::s_settingsDialog = 0L;

KopetePreferencesAction::KopetePreferencesAction( KActionCollection *parent, const char *name )
: KAction( KIcon(KStandardGuiItem::configure().iconName()), KStandardGuiItem::configure().text(), parent )
{
	connect( this, SIGNAL( triggered(bool) ), this, SLOT( slotShowPreferences() ) );
        parent->addAction( name, this );
}

KopetePreferencesAction::~KopetePreferencesAction()
{
}

void KopetePreferencesAction::slotShowPreferences()
{
	// FIXME: Use static deleter - Martijn
	if ( !s_settingsDialog )
		s_settingsDialog = new KSettings::Dialog( KSettings::Dialog::Static, Kopete::UI::Global::mainWidget() );
	s_settingsDialog->show();

	s_settingsDialog->dialog()->raise();
#ifdef Q_OS_UNIX
	KWin::activateWindow( s_settingsDialog->dialog()->winId() );
#endif
}

KAction * KopeteStdAction::preferences( KActionCollection *parent, const char *name )
{
	return new KopetePreferencesAction( parent, name );
}

KAction * KopeteStdAction::createAction(const QString &text, const KIcon &icon, const QObject *receiver, const char *slot, KActionCollection* parent, const char *name)
{
	KAction *newAction = new KAction(icon, text, parent);
	if(receiver && slot)
	{
		QObject::connect(newAction, SIGNAL(triggered(bool)), receiver, slot);
	}

	if( parent )
		parent->addAction(name, newAction);

	return newAction;
}

KAction * KopeteStdAction::chat( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Start &Chat..." ), KIcon("mail_generic"), recvr, slot, parent, name );
}

KAction * KopeteStdAction::sendMessage( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "&Send Single Message..." ), KIcon( "mail_generic" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::contactInfo( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "User &Info" ), KIcon( "messagebox_info" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::sendFile( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Send &File..." ), KIcon( "attach" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::viewHistory( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "View &History..." ), KIcon( "history" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::addGroup( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "&Create Group..." ), KIcon( "folder" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::changeMetaContact( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Cha&nge Meta Contact..." ), KIcon( "move" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::deleteContact( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	KAction *deleteAction = createAction( i18n( "&Delete Contact" ), KIcon( "delete_user" ), recvr, slot, parent, name );
	deleteAction->setShortcut( KShortcut(Qt::Key_Delete) );

	return deleteAction;
}

KAction * KopeteStdAction::changeAlias( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Change A&lias..." ), KIcon( "signature" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::blockContact( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "&Block Contact" ), KIcon( "player_pause" ), recvr, slot, parent, name );
}

KAction * KopeteStdAction::unblockContact( const QObject *recvr, const char *slot, KActionCollection* parent, const char *name )
{
	return createAction( i18n( "Un&block Contact" ), KIcon( "player_play" ), recvr, slot, parent, name );
}

#include "kopetestdaction.moc"

// vim: set noet ts=4 sts=4 sw=4:

<|endoftext|>


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

#include "platform/mbed_assert.h"
#include "gtest/gtest.h"
#include <stdio.h>
#include <stdbool.h>

bool mbed_assert_throw_errors = false;

extern "C" void mbed_assert_internal(const char *expr, const char *file, int line)
{
    fprintf(stderr, "mbed assertation failed: %s, file: %s, line %d \n", expr, file, line);
    if (mbed_assert_throw_errors) {
        throw 1;
    }

    /* Ensure we fail the unit test if the Mbed assertion fails. Without this,
     * we might not notice the assertion failure as it wouldn't be bubbled up
     * to googletest. Note that this is after the above throw, as some tests
     * check that an exception is thrown (i.e. negative tests). */
    FAIL();
}
<commit_msg>Allow unit tests to be compiled with -fno-exception<commit_after>/*
 * Copyright (c) , Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "platform/mbed_assert.h"
#include "gtest/gtest.h"
#include <stdio.h>
#include <stdbool.h>

bool mbed_assert_throw_errors = false;

extern "C" void mbed_assert_internal(const char *expr, const char *file, int line)
{
    fprintf(stderr, "mbed assertation failed: %s, file: %s, line %d \n", expr, file, line);
    if (mbed_assert_throw_errors) {
#ifdef __EXCEPTIONS
        throw 1;
#else
        FAIL();
#endif
    }

    /* Ensure we fail the unit test if the Mbed assertion fails. Without this,
     * we might not notice the assertion failure as it wouldn't be bubbled up
     * to googletest. Note that this is after the above throw, as some tests
     * check that an exception is thrown (i.e. negative tests). */
    FAIL();
}
<|endoftext|>


<commit_before>#include "stdsneezy.h"
#include "database.h"

void updateStockHistory()
{
  TDatabase db(DB_SNEEZY);

  // stockhistory table needs to be seeded with at least one entry for
  // each stock, for this query to work
  db.query("insert into stockhistory select sh.n+1, s.ticker, s.price from stockinfo s left outer join (select ticker, max(n) as n from stockhistory group by ticker) sh on (s.ticker=sh.ticker)");
}

void stockSplit(sstring ticker, float pricechange)
{
  TDatabase db(DB_SNEEZY);

  db.query("update stockinfo set shares=shares*2, price=(price/2)+%f where ticker='%s'", pricechange, ticker.c_str());

  db.query("update stockhistory set price=price/2 where ticker='%s'",
	   ticker.c_str());

  //  db.query("insert into stockhistory select max(n), ticker, max(price) from stockhistory where ticker='%s' group by ticker", ticker.c_str());
  //  db.query("insert into stockhistory select max(n), ticker, min(price) from stockhistory where ticker='%s' group by ticker", ticker.c_str());
}

void stockReverseSplit(sstring ticker, float pricechange)
{
  TDatabase db(DB_SNEEZY);

  db.query("update stockinfo set shares=shares/2, price=(price*2)+%f where ticker='%s'", pricechange, ticker.c_str());

  db.query("update stockhistory set price=price*2 where ticker='%s'",
	   ticker.c_str());

  //  db.query("insert into stockhistory select max(n), ticker, max(price) from stockhistory where ticker='%s' group by ticker", ticker.c_str());
  //  db.query("insert into stockhistory select max(n), ticker, min(price) from stockhistory where ticker='%s' group by ticker", ticker.c_str());
}

void updateStocks()
{
  TDatabase db(DB_SNEEZY), stocks(DB_SNEEZY);

  stocks.query("select ticker, shares, price, (((random() * volatility * 2) - volatility) / 100) as pricechange from stockinfo");

  while(stocks.fetchRow()){
    float pricechange=convertTo<float>(stocks["pricechange"]);
    float price=convertTo<float>(stocks["price"]);
    sstring ticker=stocks["ticker"];
    int shares=convertTo<int>(stocks["shares"]);

    if(shares<=0)
      continue;

    if((price+pricechange) < 10.0){
      stockReverseSplit(ticker, pricechange);
    } else if(price+pricechange > 100.0){
      stockSplit(ticker, pricechange);
    } else {
      db.query("update stockinfo set price=price+%f where ticker='%s'",
	       pricechange, ticker.c_str());
    }

  }

}

int stockBoard(TBeing *ch, cmdTypeT cmd, const char *arg, TObj *o1, TObj *o2)
{
  int found=0;
  TThing *o;
  TObj *to;
  TDatabase db(DB_SNEEZY);

  if(cmd != CMD_LOOK)
    return FALSE;

  for (o = ch->roomp->getStuff(); o; o = o->nextThing) {
    to = dynamic_cast<TObj *>(o);
    if (to && to->spec == SPEC_STOCK_BOARD &&
	isname(arg, to->name)){
      found=1;
      break;
    }
  }

  if(!found)
    return FALSE;

  ch->sendTo("You examine the board:\n\r");
  ch->sendTo("------------------------------------------------------------\n\r");
  ch->sendTo("Ticker   Bid    Ask  Change  Market Cap                                \n\r");
  ch->sendTo("------------------------------------------------------------\n\r");

  db.query("select distinct si.ticker, si.price, sh.price as dayprice, si.shares from stockinfo si, stockhistory sh, (select ticker, max(n) as n from stockhistory group by ticker) sn where sh.n=sn.n and si.ticker=sh.ticker");

  float price, pricediff;
  int shares;
  while(db.fetchRow()){
    price=convertTo<float>(db["price"]);
    shares=convertTo<int>(db["shares"]);
    pricediff=price-convertTo<float>(db["dayprice"]);

    ch->sendTo(COLOR_BASIC, fmt("%-6s  <Y>%.2f  %.2f<1>  %s%+.2f<1>   %s\n\r") %
	       db["ticker"] % (price*0.97) % (price * 1.03) %
	       (pricediff>0?"<G>":"<R>") % pricediff %
	       talenDisplay((int)(price*(float)shares)));

  }

  ch->sendTo("------------------------------------------------------------\n\r");
  ch->sendTo("Price charts can be seen at: http://sneezy.saw.net/peel/stocks/\n\r");
  ch->sendTo("------------------------------------------------------------\n\r");

  return TRUE;
}

<commit_msg>added some randomness to when a stock splits<commit_after>#include "stdsneezy.h"
#include "database.h"

void updateStockHistory()
{
  TDatabase db(DB_SNEEZY);

  // stockhistory table needs to be seeded with at least one entry for
  // each stock, for this query to work
  db.query("insert into stockhistory select sh.n+1, s.ticker, s.price from stockinfo s left outer join (select ticker, max(n) as n from stockhistory group by ticker) sh on (s.ticker=sh.ticker)");
}

void stockSplit(sstring ticker, float pricechange)
{
  TDatabase db(DB_SNEEZY);

  db.query("update stockinfo set shares=shares*2, price=(price/2)+%f where ticker='%s'", pricechange, ticker.c_str());

  db.query("update stockhistory set price=price/2 where ticker='%s'",
	   ticker.c_str());

  //  db.query("insert into stockhistory select max(n), ticker, max(price) from stockhistory where ticker='%s' group by ticker", ticker.c_str());
  //  db.query("insert into stockhistory select max(n), ticker, min(price) from stockhistory where ticker='%s' group by ticker", ticker.c_str());
}

void stockReverseSplit(sstring ticker, float pricechange)
{
  TDatabase db(DB_SNEEZY);

  db.query("update stockinfo set shares=shares/2, price=(price*2)+%f where ticker='%s'", pricechange, ticker.c_str());

  db.query("update stockhistory set price=price*2 where ticker='%s'",
	   ticker.c_str());

  //  db.query("insert into stockhistory select max(n), ticker, max(price) from stockhistory where ticker='%s' group by ticker", ticker.c_str());
  //  db.query("insert into stockhistory select max(n), ticker, min(price) from stockhistory where ticker='%s' group by ticker", ticker.c_str());
}

void updateStocks()
{
  TDatabase db(DB_SNEEZY), stocks(DB_SNEEZY);

  stocks.query("select ticker, shares, price, (((random() * volatility * 2) - volatility) / 100) as pricechange from stockinfo");

  while(stocks.fetchRow()){
    float pricechange=convertTo<float>(stocks["pricechange"]);
    float price=convertTo<float>(stocks["price"]);
    sstring ticker=stocks["ticker"];
    int shares=convertTo<int>(stocks["shares"]);

    if(shares<=0)
      continue;

    if((price+pricechange) < ::number(1,5)){
      stockReverseSplit(ticker, pricechange);
    } else if(price+pricechange > ::number(100, 125)){
      stockSplit(ticker, pricechange);
    } else {
      db.query("update stockinfo set price=price+%f where ticker='%s'",
	       pricechange, ticker.c_str());
    }

  }

}

int stockBoard(TBeing *ch, cmdTypeT cmd, const char *arg, TObj *o1, TObj *o2)
{
  int found=0;
  TThing *o;
  TObj *to;
  TDatabase db(DB_SNEEZY);

  if(cmd != CMD_LOOK)
    return FALSE;

  for (o = ch->roomp->getStuff(); o; o = o->nextThing) {
    to = dynamic_cast<TObj *>(o);
    if (to && to->spec == SPEC_STOCK_BOARD &&
	isname(arg, to->name)){
      found=1;
      break;
    }
  }

  if(!found)
    return FALSE;

  ch->sendTo("You examine the board:\n\r");
  ch->sendTo("------------------------------------------------------------\n\r");
  ch->sendTo("Ticker   Bid    Ask  Change  Market Cap                                \n\r");
  ch->sendTo("------------------------------------------------------------\n\r");

  db.query("select distinct si.ticker, si.price, sh.price as dayprice, si.shares from stockinfo si, stockhistory sh, (select ticker, max(n) as n from stockhistory group by ticker) sn where sh.n=sn.n and si.ticker=sh.ticker");

  float price, pricediff;
  int shares;
  while(db.fetchRow()){
    price=convertTo<float>(db["price"]);
    shares=convertTo<int>(db["shares"]);
    pricediff=price-convertTo<float>(db["dayprice"]);

    ch->sendTo(COLOR_BASIC, fmt("%-6s  <Y>%.2f  %.2f<1>  %s%+.2f<1>   %s\n\r") %
	       db["ticker"] % (price*0.97) % (price * 1.03) %
	       (pricediff>0?"<G>":"<R>") % pricediff %
	       talenDisplay((int)(price*(float)shares)));

  }

  ch->sendTo("------------------------------------------------------------\n\r");
  ch->sendTo("Price charts can be seen at: http://sneezy.saw.net/peel/stocks/\n\r");
  ch->sendTo("------------------------------------------------------------\n\r");

  return TRUE;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <cstdint>
#include <cmath>
#include <vector>
#include <initializer_list>
#include <iostream>
#include <map>
#include <fstream>
#include <memory>
#include "Eigen/Dense"

#include "model.h"

using namespace std;

double DirichletMultinomialLogProbability(double alphas[4], ReadData data) {
	// TODO: Cache most of the math here
	// TODO: Does not include the multinomail coefficient
	int read_count = data.reads[0]+data.reads[1]+data.reads[2]+data.reads[3];
	double alpha_total = alphas[0]+alphas[1]+alphas[2]+alphas[3];
	double result = 0.0;
	for(int i : {0,1,2,3}) {
		for(int x = 0; x < data.reads[i]; ++x) {
			result += log(alphas[i]+x);
		}
	}
	for(int x = 0; x < read_count; ++x)
		result -= log(alpha_total+x);
	return result;
}

DiploidProbs DiploidPopulation(const ModelParams &params, int ref_allele) {
	ReadData d;
	DiploidProbs result;
	double alphas[4];
	for(int i : {0,1,2,3})
		alphas[i] = params.theta*params.nuc_freq[i];
	for(int i : {0,1,2,3}) {
		for(int j=0;j<i;++j) {
			d.key = 0;
			d.reads[ref_allele] = 1;
			d.reads[i] += 1;
			d.reads[j] += 1;
			result[i+j*4] = DirichletMultinomialLogProbability(alphas, d);
			result[j+i*4] = result[i+j*4];
		}
		d.key = 0;
		d.reads[ref_allele] = 1;
		d.reads[i] += 2;
		result[i+i*4] = DirichletMultinomialLogProbability(alphas, d);
	}
	return result.exp();
}

HaploidProbs HaploidPopulation(const ModelParams &params, int ref_allele) {
	ReadData d;
	HaploidProbs result;
	double alphas[4];
	for(int i : {0,1,2,3})
		alphas[i] = params.theta*params.nuc_freq[i];
    for(int i : {0,1,2,3}){
        d.key = 0;
        d.reads[i] = 1;
        d.reads[ref_allele] = 1;
        result[i] = DirichletMultinomialLogProbability(alphas, d);
    }
    return result.exp();
}

GenotypeProbs PopulationProbs(const ModelParams &params, int ref_allele, int ploidy){
    if(ploidy==2){
        DiploidProbs result = DiploidPopulation(params, ref_allele);
        return result;
    }
    HaploidProbs result = HaploidPopulation(params, ref_allele);
    return result;
}

TransitionMatrix F81(const ModelParams &params){
	double beta = 1.0;
	for(auto d : params.nuc_freq)
		beta -= d*d;
	beta = 1.0/beta;
	beta = exp(-beta*params.mutation_rate);
	Eigen::Matrix4d m;
	for(int i : {0,1,2,3}) {
		for(int j : {0,1,2,3}) {
			m(i,j) = params.nuc_freq[i]*(1.0-beta);
		}
		m(i,i) += beta;
    }
    return m;
}

MutationMatrix MutationAccumulation(const ModelParams &params, bool and_mut){
	TransitionMatrix m = F81(params);
    if(params.ploidy_ancestor == 1){//haploid->haploid design
        if(!and_mut){
            return m;
        }
        MutationMatrix result = MutationMatrix(4,4);
        //identity matrix initialzer is for Matrices only, these are (despite teh
        //name) arrays so buid it up:
        for(int i : {0,1,2,3}){
            for (int j : {0,1,2,3}){
                if(i == j){
                    result(i,j) = m(i,j);
                }
                else{
                    result(i,j) = 0.0;
                }
            }
        }
        return result;
    }
    if(params.ploidy_descendant == 1){//diploid -> haploid design
        MutationMatrix result = MutationMatrix(16,4);
        for(int i : {0,1,2,3}) {
		    for(int j : {0,1,2,3}) {
			    for(int k : {0,1,2,3}) {
				    result(i*4+j,k) = 0.0;
        				if(!and_mut || i != k)
					result(i*4+j,k) += 0.5*m(i,k);
		        		if(!and_mut || j != k)
					result(i*4+j,k) += 0.5*m(j,k);
                }
            }
	    }
	    return result;
    }
    //only diploid->diploid left
    MutationMatrix result = MutationMatrix(16,16);
	for(int i : {0,1,2,3}) {
		for(int j : {0,1,2,3}) {
			for(int k : {0,1,2,3}) {
    			for(int l : {0,1,2,3}) {
    				result(i*4+j,k*4+l) = 0.0;
    				if(!and_mut || i != k || j != l ){//TODO: check this is right transition prob
					    result(i*4+j,k*4+l) += m(i,k) * m(j,l);
                    }
                }
            }
        }
    }
    return result;
}

GenotypeProbs Sequencing(const ModelParams &params, int ref_allele, ReadData data, int ploidy) {
    if(ploidy == 2){
    	DiploidProbs result;
	    double alphas_total = (1.0-params.phi_diploid)/params.phi_diploid;
    	for(int i : {0,1,2,3}) {
	    	for(int j=0;j<i;++j) {
		    	double alphas[4];
			    for(int k : {0,1,2,3}) {
				    if(k == i || k == j)
					    alphas[k] = (0.5-params.error_prob/3.0)*alphas_total;
    				else
	    				alphas[k] = (params.error_prob/3.0)*alphas_total;
		    	}
			    result[i*4+j] = DirichletMultinomialLogProbability(alphas, data);
    			result[j*4+i] = result[i*4+j];
	    	}
		    double alphas[4];
    		for(int k : {0,1,2,3}) {
	    		if(k == i)
		    		alphas[k] = (1.0-params.error_prob)*alphas_total;
			    else
				    alphas[k] = params.error_prob/3.0*alphas_total;
    		}
	    	result[i*4+i] = DirichletMultinomialLogProbability(alphas, data);
    	}
	    double scale = result.maxCoeff();
    	return (result - scale).exp();
    }
    	HaploidProbs result;
	double alphas_total = (1.0-params.phi_haploid)/params.phi_haploid;
	for(int i : {0,1,2,3}) {
		double alphas[4];
		for(int k : {0,1,2,3}) {
			if(k == i)
				alphas[k] = (1.0-params.error_prob)*alphas_total;
			else
				alphas[k] = params.error_prob/3.0*alphas_total;
		}
		result[i] = DirichletMultinomialLogProbability(alphas, data);
	}
	double scale = result.maxCoeff();
	return (result - scale).exp();
}

double TetMAProbability(const ModelParams &params, const ModelInput site_data, const MutationMatrix m, const MutationMatrix mn) {
    DiploidProbs pop_genotypes = DiploidPopulation(params, site_data.reference);
	auto it = site_data.all_reads.begin();
	DiploidProbs anc_genotypes = Sequencing(params, site_data.reference, *it, 2);
	anc_genotypes *= pop_genotypes;
	DiploidProbs num_genotypes = anc_genotypes;
	for(++it; it != site_data.all_reads.end(); ++it) {
		DiploidProbs p = Sequencing(params, site_data.reference, *it, 1);
		anc_genotypes *= (m.matrix()*p.matrix()).array();
		num_genotypes *= (mn.matrix()*p.matrix()).array();

	}

//    cerr << "\n" << anc_genotypes.sum() << '\t' << num_genotypes.sum() << endl;
	return 1.0 - num_genotypes.sum()/anc_genotypes.sum();
}

double TetMAProbOneMutation(const ModelParams &params, const ModelInput site_data) {
	MutationMatrix m = MutationAccumulation(params, false);
	MutationMatrix mt = MutationAccumulation(params, true);
	MutationMatrix mn = m-mt;
	DiploidProbs pop_genotypes = DiploidPopulation(params, site_data.reference);
	auto it = site_data.all_reads.begin();
	DiploidProbs anc_genotypes = Sequencing(params, site_data.reference, *it, 2);
	anc_genotypes *= pop_genotypes;

  	DiploidProbs denom = anc_genotypes;   //product of p(Ri|A)

    DiploidProbs nomut_genotypes = anc_genotypes; //Product of p(Ri & noMutatoin|A)
    DiploidProbs mut_genotypes = DiploidProbs::Zero();      //Sum of p(Ri&Mutation|A=x)
	for(++it; it != site_data.all_reads.end(); ++it) {
        DiploidProbs p = Sequencing(params, site_data.reference, *it, 1);
        DiploidProbs dgen =  (mn.matrix()*p.matrix()).array();
        DiploidProbs agen = (m.matrix()*p.matrix()).array();
        nomut_genotypes *= dgen;
        mut_genotypes += (agen/dgen - 1); //(agen+dgen)/agen
        denom *= agen;
    }
    double result = (nomut_genotypes * mut_genotypes).sum() / denom.sum();
    return(result);
}

int main(){
      ModelParams p = {
        0.0001,
        {0.38, 0.12, 0.12, 0.38},
        1e-2,
        0.01,
       0.01,
        0.05,
        1,1
};
   MutationMatrix m = MutationAccumulation(p, true);
   MutationMatrix mn = MutationAccumulation(p, false);
   cerr << m << endl << endl;
   cout << mn - m << endl;
   return 0;
}
// Uncommon and compile with this:
// clang++ -std=c++11 -Ithird-party/bamtools/src/ -Lboost_progam_options model.cc
//
// to play around with / debug results.
//int main(){
//    ModelParams p = {
//        0.0001,
//        {0.38, 0.12, 0.12, 0.38},
//        1e-8,
//        0.01,
//        0.01,
//        0.05,
//    };
//    ModelInput two_vars = { 2,
//        {
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0,  0,  0, 30},
//        { 0, 0,   0, 30},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//
//        }
//    };
//
//
//    ModelInput  one_vars = { 1,
//        {
//        { 0, 30,  0,  0},
//        { 0,  0,  0, 30},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0, 0}}
//    };
//
//   ModelInput no_vars = {1,
//      {{0,   0,  0, 5},
//       {3,   0,  0, 4},
//       {8,   0,  0, 0},
//       {3,   0,  0, 3},
//       {0,  0,  0, 2},
//       {2,  0,  0, 3},
//       {0,  0,  0, 0},
//       {0,  0,  0, 5},
//       {0,  0,  0, 4},
//       {1,  0,  0, 3},
//       {0,  0,  0, 28},
//       {0 ,  0,  0, 3}}
//   };
//
//    cout << "___With the no-variant data___" << endl;
////    cout << "P(one|data)= "<<  TetMAProbOneMutation(p,no_vars)<< endl;
////    cout << "P(any|data)= " << TetMAProbability(p,no_vars) << endl;
//
//    cout << TetMAProbability(p,no_vars) << endl;
//
//    cout << "___With the one-variant data___" << endl;
////    cout << "P(one|data)= "<<  TetMAProbOneMutation(p,one_vars)<< endl;
////    cout << "P(any|data)= " << TetMAProbability(p,one_vars) << endl;
//      cout << TetMAProbability(p,one_vars) << endl;
//
//    cout << "___With the two-variant data___" << endl;
//    cout << "P(one|data)= "<<  TetMAProbOneMutation(p,two_vars)<< endl;
//    cout << "P(any|data)= " << TetMAProbability(p,two_vars) << endl;
//
//
//    cout << "calculating the same number once: " << TetMAProbOneMutation(p,two_vars) << endl;
//    cout << "then another time: " << TetMAProbOneMutation(p,two_vars) << endl;
//    TetMAProbOneMutation(p,no_vars);
//    cout << "And once more after calling from the the no-vars data: " << TetMAProbOneMutation(p,two_vars) << endl;
//    return 0;
//}
////

<commit_msg>Haploid population probs = nuc. freqs (ignore theta)<commit_after>#include <iostream>
#include <cstdint>
#include <cmath>
#include <vector>
#include <initializer_list>
#include <iostream>
#include <map>
#include <fstream>
#include <memory>
#include "Eigen/Dense"

#include "model.h"

using namespace std;

double DirichletMultinomialLogProbability(double alphas[4], ReadData data) {
	// TODO: Cache most of the math here
	// TODO: Does not include the multinomail coefficient
	int read_count = data.reads[0]+data.reads[1]+data.reads[2]+data.reads[3];
	double alpha_total = alphas[0]+alphas[1]+alphas[2]+alphas[3];
	double result = 0.0;
	for(int i : {0,1,2,3}) {
		for(int x = 0; x < data.reads[i]; ++x) {
			result += log(alphas[i]+x);
		}
	}
	for(int x = 0; x < read_count; ++x)
		result -= log(alpha_total+x);
	return result;
}

//'Population' refers to the pop. from which the ancestor comes
// We deal only with GC/AT bias and (in the diploid case) heterozygosity

DiploidProbs DiploidPopulation(const ModelParams &params, int ref_allele) {
	ReadData d;
	DiploidProbs result;
	double alphas[4];
	for(int i : {0,1,2,3})
		alphas[i] = params.theta*params.nuc_freq[i];
	for(int i : {0,1,2,3}) {
		for(int j=0;j<i;++j) {
			d.key = 0;
			d.reads[ref_allele] = 1;
			d.reads[i] += 1;
			d.reads[j] += 1;
			result[i+j*4] = DirichletMultinomialLogProbability(alphas, d);
			result[j+i*4] = result[i+j*4];
		}
		d.key = 0;
		d.reads[ref_allele] = 1;
		d.reads[i] += 2;
		result[i+i*4] = DirichletMultinomialLogProbability(alphas, d);
	}
	return result.exp();
}

GenotypeProbs PopulationProbs(const ModelParams &params, int ref_allele, int ploidy){
    if(ploidy==2){
        DiploidProbs result = DiploidPopulation(params, ref_allele);
        return result;
    }
    HaploidProbs result = params.nuc_freq;
    return result;
}

//Create the mutation matrices which represent the probabilites of different
//histories with or without mutation.

HaploidProbs F81(const ModelParams &params){
	double beta = 1.0;
	for(auto d : params.nuc_freq)
		beta -= d*d;
	beta = 1.0/beta;
	beta = exp(-beta*params.mutation_rate);
	Eigen::Matrix4d m;
	for(int i : {0,1,2,3}) {
		for(int j : {0,1,2,3}) {
			m(i,j) = params.nuc_freq[i]*(1.0-beta);
		}
		m(i,i) += beta;
    }
    return m;
}

MutationMatrix MutationAccumulation(const ModelParams &params, bool and_mut){
	TransitionMatrix m = F81(params);
    if(params.ploidy_ancestor == 1){//haploid->haploid design
        if(!and_mut){
            return m;
        }
        MutationMatrix result = MutationMatrix(4,4);
        //identity matrix initialzer is for Matrices only, these are (despite teh
        //name) arrays so buid it up:
        for(int i : {0,1,2,3}){
            for (int j : {0,1,2,3}){
                if(i == j){
                    result(i,j) = m(i,j);
                }
                else{
                    result(i,j) = 0.0;
                }
            }
        }
        return result;
    }
    if(params.ploidy_descendant == 1){//diploid -> haploid design
        MutationMatrix result = MutationMatrix(16,4);
        for(int i : {0,1,2,3}) {
		    for(int j : {0,1,2,3}) {
			    for(int k : {0,1,2,3}) {
				    result(i*4+j,k) = 0.0;
        				if(!and_mut || i != k)
					result(i*4+j,k) += 0.5*m(i,k);
		        		if(!and_mut || j != k)
					result(i*4+j,k) += 0.5*m(j,k);
                }
            }
	    }
	    return result;
    }
    //only diploid->diploid left
    MutationMatrix result = MutationMatrix(16,16);
	for(int i : {0,1,2,3}) {
		for(int j : {0,1,2,3}) {
			for(int k : {0,1,2,3}) {
    			for(int l : {0,1,2,3}) {
    				result(i*4+j,k*4+l) = 0.0;
    				if(!and_mut || i != k || j != l ){//TODO: check this is right transition prob
					    result(i*4+j,k*4+l) += m(i,k) * m(j,l);
                    }
                }
            }
        }
    }
    return result;
}

GenotypeProbs Sequencing(const ModelParams &params, int ref_allele, ReadData data, int ploidy) {
    if(ploidy == 2){
    	DiploidProbs result;
	    double alphas_total = (1.0-params.phi_diploid)/params.phi_diploid;
    	for(int i : {0,1,2,3}) {
	    	for(int j=0;j<i;++j) {
		    	double alphas[4];
			    for(int k : {0,1,2,3}) {
				    if(k == i || k == j)
					    alphas[k] = (0.5-params.error_prob/3.0)*alphas_total;
    				else
	    				alphas[k] = (params.error_prob/3.0)*alphas_total;
		    	}
			    result[i*4+j] = DirichletMultinomialLogProbability(alphas, data);
    			result[j*4+i] = result[i*4+j];
	    	}
		    double alphas[4];
    		for(int k : {0,1,2,3}) {
	    		if(k == i)
		    		alphas[k] = (1.0-params.error_prob)*alphas_total;
			    else
				    alphas[k] = params.error_prob/3.0*alphas_total;
    		}
	    	result[i*4+i] = DirichletMultinomialLogProbability(alphas, data);
    	}
	    double scale = result.maxCoeff();
    	return (result - scale).exp();
    }
    	HaploidProbs result;
	double alphas_total = (1.0-params.phi_haploid)/params.phi_haploid;
	for(int i : {0,1,2,3}) {
		double alphas[4];
		for(int k : {0,1,2,3}) {
			if(k == i)
				alphas[k] = (1.0-params.error_prob)*alphas_total;
			else
				alphas[k] = params.error_prob/3.0*alphas_total;
		}
		result[i] = DirichletMultinomialLogProbability(alphas, data);
	}
	double scale = result.maxCoeff();
	return (result - scale).exp();
}

double TetMAProbability(const ModelParams &params, const ModelInput site_data, const MutationMatrix m, const MutationMatrix mn) {
    DiploidProbs pop_genotypes = DiploidPopulation(params, site_data.reference);
	auto it = site_data.all_reads.begin();
	DiploidProbs anc_genotypes = Sequencing(params, site_data.reference, *it, 2);
	anc_genotypes *= pop_genotypes;
	DiploidProbs num_genotypes = anc_genotypes;
	for(++it; it != site_data.all_reads.end(); ++it) {
		DiploidProbs p = Sequencing(params, site_data.reference, *it, 1);
		anc_genotypes *= (m.matrix()*p.matrix()).array();
		num_genotypes *= (mn.matrix()*p.matrix()).array();

	}

//    cerr << "\n" << anc_genotypes.sum() << '\t' << num_genotypes.sum() << endl;
	return 1.0 - num_genotypes.sum()/anc_genotypes.sum();
}

double TetMAProbOneMutation(const ModelParams &params, const ModelInput site_data) {
	MutationMatrix m = MutationAccumulation(params, false);
	MutationMatrix mt = MutationAccumulation(params, true);
	MutationMatrix mn = m-mt;
	DiploidProbs pop_genotypes = DiploidPopulation(params, site_data.reference);
	auto it = site_data.all_reads.begin();
	DiploidProbs anc_genotypes = Sequencing(params, site_data.reference, *it, 2);
	anc_genotypes *= pop_genotypes;

  	DiploidProbs denom = anc_genotypes;   //product of p(Ri|A)

    DiploidProbs nomut_genotypes = anc_genotypes; //Product of p(Ri & noMutatoin|A)
    DiploidProbs mut_genotypes = DiploidProbs::Zero();      //Sum of p(Ri&Mutation|A=x)
	for(++it; it != site_data.all_reads.end(); ++it) {
        DiploidProbs p = Sequencing(params, site_data.reference, *it, 1);
        DiploidProbs dgen =  (mn.matrix()*p.matrix()).array();
        DiploidProbs agen = (m.matrix()*p.matrix()).array();
        nomut_genotypes *= dgen;
        mut_genotypes += (agen/dgen - 1); //(agen+dgen)/agen
        denom *= agen;
    }
    double result = (nomut_genotypes * mut_genotypes).sum() / denom.sum();
    return(result);
}

int main(){
      ModelParams p = {
        0.0001,
        {0.38, 0.12, 0.12, 0.38},
        1e-2,
        0.01,
       0.01,
        0.05,
        1,1
};
   MutationMatrix mt = MutationAccumulation(p, true);
   MutationMatrix m = MutationAccumulation(p, false);
   cerr << m << endl << endl;
   cout << m -mt << endl;
   return 0;
}
// Uncommon and compile with this:
// clang++ -std=c++11 -Ithird-party/bamtools/src/ -Lboost_progam_options model.cc
//
// to play around with / debug results.
//int main(){
//    ModelParams p = {
//        0.0001,
//        {0.38, 0.12, 0.12, 0.38},
//        1e-8,
//        0.01,
//        0.01,
//        0.05,
//    };
//    ModelInput two_vars = { 2,
//        {
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0,  0,  0, 30},
//        { 0, 0,   0, 30},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//
//        }
//    };
//
//
//    ModelInput  one_vars = { 1,
//        {
//        { 0, 30,  0,  0},
//        { 0,  0,  0, 30},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0,  0},
//        { 0, 30,  0, 0}}
//    };
//
//   ModelInput no_vars = {1,
//      {{0,   0,  0, 5},
//       {3,   0,  0, 4},
//       {8,   0,  0, 0},
//       {3,   0,  0, 3},
//       {0,  0,  0, 2},
//       {2,  0,  0, 3},
//       {0,  0,  0, 0},
//       {0,  0,  0, 5},
//       {0,  0,  0, 4},
//       {1,  0,  0, 3},
//       {0,  0,  0, 28},
//       {0 ,  0,  0, 3}}
//   };
//
//    cout << "___With the no-variant data___" << endl;
////    cout << "P(one|data)= "<<  TetMAProbOneMutation(p,no_vars)<< endl;
////    cout << "P(any|data)= " << TetMAProbability(p,no_vars) << endl;
//
//    cout << TetMAProbability(p,no_vars) << endl;
//
//    cout << "___With the one-variant data___" << endl;
////    cout << "P(one|data)= "<<  TetMAProbOneMutation(p,one_vars)<< endl;
////    cout << "P(any|data)= " << TetMAProbability(p,one_vars) << endl;
//      cout << TetMAProbability(p,one_vars) << endl;
//
//    cout << "___With the two-variant data___" << endl;
//    cout << "P(one|data)= "<<  TetMAProbOneMutation(p,two_vars)<< endl;
//    cout << "P(any|data)= " << TetMAProbability(p,two_vars) << endl;
//
//
//    cout << "calculating the same number once: " << TetMAProbOneMutation(p,two_vars) << endl;
//    cout << "then another time: " << TetMAProbOneMutation(p,two_vars) << endl;
//    TetMAProbOneMutation(p,no_vars);
//    cout << "And once more after calling from the the no-vars data: " << TetMAProbOneMutation(p,two_vars) << endl;
//    return 0;
//}
////

<|endoftext|>