<commit_before>// Copyright (c) 2017 Daniel Cooke
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <utility>
#include <random>
#include <chrono>
#include <iostream>

#include "mappable/mappable_fwd.hpp"
#include "IntervalTree.h" // https://github.com/ekg/intervaltree

using namespace mappable;

struct Read : public Mappable<Read>, Comparable<Read>
{
    ContigRegion region;
    std::string sequence;
    const auto& mapped_region() const noexcept { return region; } // required for Mappable
    template <typename R, typename S> Read(R&& region, S&& sequence)
    : region {std::forward<R>(region)}, sequence {std::forward<S>(sequence)} {}
};

bool operator==(const Read& lhs, const Read& rhs) noexcept
{
    return lhs.region == rhs.region && lhs.sequence == rhs.sequence;
}
bool operator<(const Read& lhs, const Read& rhs) noexcept
{
    return lhs.region == rhs.region ? lhs.sequence < rhs.sequence : lhs.region < rhs.region;
}

using ReadSet = MappableFlatSet<Read>;

using IntervalTreeRead = Interval<std::string>;

static constexpr std::array<char, 4> dnaBases {'A', 'C', 'G', 'T'};

std::string generate_sequence(const unsigned size)
{
    static std::default_random_engine gen {};
    std::uniform_int_distribution<char> dist {0, 4};
    std::string result(size, '.');
    std::generate_n(std::begin(result), size, [&] () { return dnaBases[dist(gen)]; });
    return result;
}

auto generate_mappable_data(const std::size_t num_reads, const unsigned read_size, const unsigned contig_size)
{
    static std::default_random_engine gen {};
    std::uniform_int_distribution<ContigRegion::Size> position_dist {0, contig_size};
    std::vector<Read> result {};
    result.reserve(num_reads);
    std::generate_n(std::back_inserter(result), num_reads, [&] () -> Read {
        auto begin = position_dist(gen);
        return {ContigRegion {begin, std::min(begin + read_size, contig_size)}, generate_sequence(read_size)};
    });
    return result;
}

auto generate_interval_tree_data(const std::size_t num_reads, const unsigned read_size, const unsigned contig_size)
{
    static std::default_random_engine gen {};
    std::uniform_int_distribution<ContigRegion::Size> position_dist {0, contig_size};
    std::vector<IntervalTreeRead> result {};
    result.reserve(num_reads);
    std::generate_n(std::back_inserter(result), num_reads, [&] () -> IntervalTreeRead {
        auto begin = position_dist(gen);
        return {begin, std::min(begin + read_size, contig_size), generate_sequence(read_size)};
    });
    return result;
}

auto generate_interval_tree_data(const std::vector<Read>& reads)
{
    std::vector<IntervalTreeRead> result {};
    result.reserve(reads.size());
    std::transform(std::cbegin(reads), std::cend(reads), std::back_inserter(result),
                   [] (const auto& read) -> IntervalTreeRead {
                       return {mapped_begin(read), mapped_end(read), read.sequence};
                   });
    return result;
}

int main()
{
    using D = std::chrono::microseconds;

    constexpr std::size_t num_reads {2'000'000};
    constexpr unsigned read_size {150};
    constexpr ContigRegion::Size contig_size {5'000'000};

    std::cout << "Generating mappable data" << std::endl;
    auto mappable_data = generate_mappable_data(num_reads, read_size, contig_size);
    std::cout << "Generating interval tree data" << std::endl;
    auto interval_tree_data = generate_interval_tree_data(mappable_data);

    std::cout << "Starting benchmarks" << std::endl;

    const ContigRegion small_test_region {contig_size / 2 - 200, contig_size / 2 + 200};
    const ContigRegion big_test_region {contig_size / 2 - contig_size / 4, contig_size / 2 + contig_size / 4};

    auto start = std::chrono::system_clock::now();
    IntervalTree<std::string> tree {interval_tree_data}; // passes by ref
    auto end = std::chrono::system_clock::now();
    auto duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "IntervalTree<std::string> constructed in " << duration.count() << "ms" << std::endl;

    start = std::chrono::system_clock::now();
    std::vector<IntervalTreeRead> interval_tree_overlapping_small;
    tree.findOverlapping(small_test_region.begin(), small_test_region.end(), interval_tree_overlapping_small);
    end = std::chrono::system_clock::now();
    duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "interval_tree_overlapping_small.size() = " << interval_tree_overlapping_small.size()
              << ". Calculated in " << duration.count() << "ms" << std::endl;

    start = std::chrono::system_clock::now();
    std::vector<IntervalTreeRead> interval_tree_contained_small;
    tree.findContained(small_test_region.begin(), small_test_region.end(), interval_tree_contained_small);
    end = std::chrono::system_clock::now();
    duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "interval_tree_contained_small.size() = " << interval_tree_contained_small.size()
              << ". Calculated in " << duration.count() << "ms" << std::endl;

    start = std::chrono::system_clock::now();
    std::vector<IntervalTreeRead> interval_tree_overlapping_big;
    tree.findOverlapping(big_test_region.begin(), big_test_region.end(), interval_tree_overlapping_big);
    end = std::chrono::system_clock::now();
    duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "interval_tree_overlapping_big.size() = " << interval_tree_overlapping_big.size()
              << ". Calculated in " << duration.count() << "ms" << std::endl;

    start = std::chrono::system_clock::now();
    std::vector<IntervalTreeRead> interval_tree_contained_big;
    tree.findContained(big_test_region.begin(), big_test_region.end(), interval_tree_contained_big);
    end = std::chrono::system_clock::now();
    duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "interval_tree_contained_big.size() = " << interval_tree_contained_big.size()
              << ". Calculated in " << duration.count() << "ms" << std::endl;

    start = std::chrono::system_clock::now();
    ReadSet set {std::make_move_iterator(std::begin(mappable_data)),
                 std::make_move_iterator(std::end(mappable_data))};
    end = std::chrono::system_clock::now();
    duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "ReadSet constructed in " << duration.count() << "ms" << std::endl;

    start = std::chrono::system_clock::now();
    const auto mappable_overlapping_small = overlap_range(set, small_test_region);
    end = std::chrono::system_clock::now();
    duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "size(mappable_overlapping_small) = " << size(mappable_overlapping_small)
              << ". Calculated in " << duration.count() << "ms" << std::endl;

    start = std::chrono::system_clock::now();
    const auto mappable_contained_small = contained_range(set, small_test_region);
    end = std::chrono::system_clock::now();
    duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "size(mappable_contained_small) = " << size(mappable_contained_small)
              << ". Calculated in " << duration.count() << "ms" << std::endl;

    start = std::chrono::system_clock::now();
    const auto mappable_overlapping_big = overlap_range(set, big_test_region);
    end = std::chrono::system_clock::now();
    duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "size(mappable_overlapping_big) = " << size(mappable_overlapping_big)
              << ". Calculated in " << duration.count() << "ms" << std::endl;

    start = std::chrono::system_clock::now();
    const auto mappable_contained_big = contained_range(set, big_test_region);
    end = std::chrono::system_clock::now();
    duration = std::chrono::duration_cast<D>(end - start);
    std::cout << "size(mappable_contained_big) = " << size(mappable_contained_big)
              << ". Calculated in " << duration.count() << "ms" << std::endl;
}
<commit_msg>Improve benchmarks<commit_after>// Copyright (c) 2017 Daniel Cooke
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <utility>
#include <random>
#include <chrono>
#include <iostream>

#include "mappable/mappable_fwd.hpp"
#include "IntervalTree.h" // https://github.com/ekg/intervaltree

using namespace mappable;

struct Read : public Mappable<Read>, Comparable<Read>
{
    ContigRegion region;
    std::string sequence;
    const auto& mapped_region() const noexcept { return region; } // required for Mappable
    template <typename R, typename S> Read(R&& region, S&& sequence)
    : region {std::forward<R>(region)}, sequence {std::forward<S>(sequence)} {}
};

bool operator==(const Read& lhs, const Read& rhs) noexcept
{
    return lhs.region == rhs.region && lhs.sequence == rhs.sequence;
}
bool operator<(const Read& lhs, const Read& rhs) noexcept
{
    return lhs.region == rhs.region ? lhs.sequence < rhs.sequence : lhs.region < rhs.region;
}

using ReadSet = MappableFlatSet<Read>;

using IntervalTreeRead = Interval<std::string>;

static constexpr std::array<char, 4> dnaBases {'A', 'C', 'G', 'T'};

std::string generate_sequence(const unsigned size)
{
    static std::default_random_engine gen {};
    std::uniform_int_distribution<char> dist {0, 4};
    std::string result(size, '.');
    std::generate_n(std::begin(result), size, [&] () { return dnaBases[dist(gen)]; });
    return result;
}

auto generate_mappable_data(const std::size_t num_reads, const unsigned read_size, const unsigned contig_size)
{
    static std::default_random_engine gen {};
    std::uniform_int_distribution<ContigRegion::Size> position_dist {0, contig_size};
    std::vector<Read> result {};
    result.reserve(num_reads);
    std::generate_n(std::back_inserter(result), num_reads, [&] () -> Read {
        auto begin = position_dist(gen);
        return {ContigRegion {begin, std::min(begin + read_size, contig_size)}, generate_sequence(read_size)};
    });
    return result;
}

auto generate_interval_tree_data(const std::size_t num_reads, const unsigned read_size, const unsigned contig_size)
{
    static std::default_random_engine gen {};
    std::uniform_int_distribution<ContigRegion::Size> position_dist {0, contig_size};
    std::vector<IntervalTreeRead> result {};
    result.reserve(num_reads);
    std::generate_n(std::back_inserter(result), num_reads, [&] () -> IntervalTreeRead {
        auto begin = position_dist(gen);
        return {begin, std::min(begin + read_size, contig_size), generate_sequence(read_size)};
    });
    return result;
}

auto generate_interval_tree_data(const std::vector<Read>& reads)
{
    std::vector<IntervalTreeRead> result {};
    result.reserve(reads.size());
    std::transform(std::cbegin(reads), std::cend(reads), std::back_inserter(result),
                   [] (const auto& read) -> IntervalTreeRead {
                       return {mapped_begin(read), mapped_end(read), read.sequence};
                   });
    return result;
}

int main()
{
    constexpr std::size_t num_reads {10'000'000};
    constexpr unsigned read_size {150};
    constexpr ContigRegion::Size contig_size {50'000'000};

    std::cout << "Generating test data..." << std::endl;
    auto mappable_data = generate_mappable_data(num_reads, read_size, contig_size);
    auto interval_tree_data = generate_interval_tree_data(mappable_data);

    const ContigRegion small_test_region {contig_size / 2 - 200, contig_size / 2 + 200};
    const ContigRegion big_test_region {contig_size / 2 - contig_size / 4, contig_size / 2 + contig_size / 4};

    using D = std::chrono::microseconds;
    auto start = std::chrono::system_clock::now();
    auto end = start;
    auto duration = std::chrono::duration_cast<D>(end - start);

    std::cout << "Starting benchmarks..." << std::endl;

    //
    // IntervalTree tests
    //
    {
        start = std::chrono::system_clock::now();
        IntervalTree<std::string> tree {interval_tree_data}; // passes by ref
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "IntervalTree<std::string> constructed in " << duration.count() << "ms" << std::endl;

        start = std::chrono::system_clock::now();
        std::vector<IntervalTreeRead> interval_tree_overlapping_small;
        tree.findOverlapping(small_test_region.begin(), small_test_region.end(), interval_tree_overlapping_small);
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "interval_tree_overlapping_small.size() = " << interval_tree_overlapping_small.size()
                  << ". Calculated in " << duration.count() << "ms" << std::endl;
        interval_tree_overlapping_small.clear();
        interval_tree_overlapping_small.shrink_to_fit();

        start = std::chrono::system_clock::now();
        std::vector<IntervalTreeRead> interval_tree_contained_small;
        tree.findContained(small_test_region.begin(), small_test_region.end(), interval_tree_contained_small);
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "interval_tree_contained_small.size() = " << interval_tree_contained_small.size()
                  << ". Calculated in " << duration.count() << "ms" << std::endl;
        interval_tree_contained_small.clear();
        interval_tree_contained_small.shrink_to_fit();

        start = std::chrono::system_clock::now();
        std::vector<IntervalTreeRead> interval_tree_overlapping_big;
        tree.findOverlapping(big_test_region.begin(), big_test_region.end(), interval_tree_overlapping_big);
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "interval_tree_overlapping_big.size() = " << interval_tree_overlapping_big.size()
                  << ". Calculated in " << duration.count() << "ms" << std::endl;
        interval_tree_overlapping_big.clear();
        interval_tree_overlapping_big.shrink_to_fit();

        start = std::chrono::system_clock::now();
        std::vector<IntervalTreeRead> interval_tree_contained_big;
        tree.findContained(big_test_region.begin(), big_test_region.end(), interval_tree_contained_big);
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "interval_tree_contained_big.size() = " << interval_tree_contained_big.size()
                  << ". Calculated in " << duration.count() << "ms" << std::endl;
        interval_tree_contained_big.clear();
        interval_tree_contained_big.shrink_to_fit();
    }

    //
    // Mappable tests
    //
    {
        start = std::chrono::system_clock::now();
        ReadSet set {std::make_move_iterator(std::begin(mappable_data)),
                     std::make_move_iterator(std::end(mappable_data))};
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "ReadSet constructed in " << duration.count() << "ms" << std::endl;

        start = std::chrono::system_clock::now();
        const auto mappable_overlapping_small = overlap_range(set, small_test_region);
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "size(mappable_overlapping_small) = " << size(mappable_overlapping_small)
                  << ". Calculated in " << duration.count() << "ms" << std::endl;

        start = std::chrono::system_clock::now();
        const auto mappable_contained_small = contained_range(set, small_test_region);
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "size(mappable_contained_small) = " << size(mappable_contained_small)
                  << ". Calculated in " << duration.count() << "ms" << std::endl;

        start = std::chrono::system_clock::now();
        const auto mappable_overlapping_big = overlap_range(set, big_test_region);
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "size(mappable_overlapping_big) = " << size(mappable_overlapping_big)
                  << ". Calculated in " << duration.count() << "ms" << std::endl;

        start = std::chrono::system_clock::now();
        const auto mappable_contained_big = contained_range(set, big_test_region);
        end = std::chrono::system_clock::now();
        duration = std::chrono::duration_cast<D>(end - start);
        std::cout << "size(mappable_contained_big) = " << size(mappable_contained_big)
                  << ". Calculated in " << duration.count() << "ms" << std::endl;
    }
}
<|endoftext|>


<commit_before>#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <vector>

#include "hash.hpp"

struct Block {
	hash256_t hash = {};
	hash256_t prevBlockHash = {};
	uint32_t bits;

	Block () {}
	Block (const hash256_t& hash, const hash256_t& prevBlockHash, const uint32_t bits) : hash(hash), prevBlockHash(prevBlockHash), bits(bits) {}
};

// find all blocks who are not parents to any other blocks (aka, a chain tip)
auto findChainTips(const std::map<hash256_t, Block>& blocks) {
	std::map<hash256_t, bool> parents;

	for (const auto& blockIter : blocks) {
		const auto block = blockIter.second;
		if (blocks.count(block.prevBlockHash) == 0) continue;

		parents[block.prevBlockHash] = true;
	}

	std::vector<Block> tips;
	for (const auto& blockIter : blocks) {
		const auto block = blockIter.second;
		if (parents.find(block.hash) != parents.end()) continue;

		tips.push_back(block);
	}

	return tips;
}

auto determineWork(std::map<hash256_t, size_t>& workCache, const std::map<hash256_t, Block>& blocks, const Block source) {
	auto visitor = source;
	size_t totalWork = source.bits;

	// naively walk the chain
	while (true) {
		const auto prevBlockIter = blocks.find(visitor.prevBlockHash);
		if (prevBlockIter == blocks.end()) break;

		const auto prevBlockWorkCacheIter = workCache.find(visitor.prevBlockHash);
		if (prevBlockWorkCacheIter != workCache.end()) {
			totalWork += prevBlockWorkCacheIter->second;
			break;
		}

		visitor = prevBlockIter->second;
		totalWork += visitor.bits;
	}

	workCache.emplace(source.hash, totalWork);

	return totalWork;
}

auto findBest(const std::map<hash256_t, Block>& blocks) {
	auto bestBlock = Block();
	size_t mostWork = 0;

	std::map<hash256_t, size_t> workCache;

	for (const auto& blockIter : blocks) {
		const auto block = blockIter.second;
		const auto work = determineWork(workCache, blocks, block);

		if (work > mostWork) {
			bestBlock = block;
			mostWork = work;
		}
	}

	auto visitor = bestBlock;
	std::vector<Block> blockchain;
	blockchain.push_back(visitor);

	// walk the chain
	while (true) {
		const auto prevBlockIter = blocks.find(visitor.prevBlockHash);
		if (prevBlockIter == blocks.end()) break;

		visitor = prevBlockIter->second;
		blockchain.push_back(visitor);
	}

	return blockchain;
}

int main () {
	std::map<hash256_t, Block> blocks;

	// read block headers from stdin until EOF
	{
		while (true) {
			uint8_t rbuf[80];
			const auto read = fread(rbuf, sizeof(rbuf), 1, stdin);

			// EOF?
			if (read == 0) break;

			hash256_t hash, prevBlockHash;
			uint32_t bits;

			hash256(&hash[0], rbuf, 80);
			memcpy(&prevBlockHash[0], rbuf + 4, 32);
			memcpy(&bits, rbuf + 72, 4);

			blocks[hash] = Block(hash, prevBlockHash, bits);
		}

		std::cerr << "Read " << blocks.size() << " headers" << std::endl;
	}

	// how many tips exist?
	{
		const auto chainTips = findChainTips(blocks);
		std::cerr << "Found " << chainTips.size() << " chain tips" << std::endl;
	}

	// what is the best?
	{
		const auto bestBlockChain = findBest(blocks);
		const auto genesis = bestBlockChain.back();
		const auto tip = bestBlockChain.front();

		// output
		std::cerr << "Best chain" << std::endl;
		std::cerr << "- Height: " << bestBlockChain.size() - 1 << std::endl;
		std::cerr << std::hex;
		std::cerr << "- Genesis: ";
		for (size_t i = 31; i < 32; --i) std::cerr << std::setw(2) << std::setfill('0') << (uint32_t) genesis.hash[i];
		std::cerr << std::endl << "- Tip: ";
		for (size_t i = 31; i < 32; --i) std::cerr << std::setw(2) << std::setfill('0') << (uint32_t) tip.hash[i];
		std::cerr << std::endl << std::dec;

		for (auto it = bestBlockChain.crbegin(); it != bestBlockChain.crend(); ++it) {
			fwrite(&it->hash[0], 32, 1, stdout);
		}
	}

	return 0;
}
<commit_msg>bestchain: count feels ambigous<commit_after>#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <vector>

#include "hash.hpp"

struct Block {
	hash256_t hash = {};
	hash256_t prevBlockHash = {};
	uint32_t bits;

	Block () {}
	Block (const hash256_t& hash, const hash256_t& prevBlockHash, const uint32_t bits) : hash(hash), prevBlockHash(prevBlockHash), bits(bits) {}
};

// find all blocks who are not parents to any other blocks (aka, a chain tip)
auto findChainTips(const std::map<hash256_t, Block>& blocks) {
	std::map<hash256_t, bool> parents;

	for (const auto& blockIter : blocks) {
		const auto block = blockIter.second;
		if (blocks.find(block.prevBlockHash) == blocks.end()) continue;

		parents[block.prevBlockHash] = true;
	}

	std::vector<Block> tips;
	for (const auto& blockIter : blocks) {
		const auto block = blockIter.second;
		if (parents.find(block.hash) != parents.end()) continue;

		tips.push_back(block);
	}

	return tips;
}

auto determineWork(std::map<hash256_t, size_t>& workCache, const std::map<hash256_t, Block>& blocks, const Block source) {
	auto visitor = source;
	size_t totalWork = source.bits;

	// naively walk the chain
	while (true) {
		const auto prevBlockIter = blocks.find(visitor.prevBlockHash);
		if (prevBlockIter == blocks.end()) break;

		const auto prevBlockWorkCacheIter = workCache.find(visitor.prevBlockHash);
		if (prevBlockWorkCacheIter != workCache.end()) {
			totalWork += prevBlockWorkCacheIter->second;
			break;
		}

		visitor = prevBlockIter->second;
		totalWork += visitor.bits;
	}

	workCache.emplace(source.hash, totalWork);

	return totalWork;
}

auto findBest(const std::map<hash256_t, Block>& blocks) {
	auto bestBlock = Block();
	size_t mostWork = 0;

	std::map<hash256_t, size_t> workCache;

	for (const auto& blockIter : blocks) {
		const auto block = blockIter.second;
		const auto work = determineWork(workCache, blocks, block);

		if (work > mostWork) {
			bestBlock = block;
			mostWork = work;
		}
	}

	auto visitor = bestBlock;
	std::vector<Block> blockchain;
	blockchain.push_back(visitor);

	// walk the chain
	while (true) {
		const auto prevBlockIter = blocks.find(visitor.prevBlockHash);
		if (prevBlockIter == blocks.end()) break;

		visitor = prevBlockIter->second;
		blockchain.push_back(visitor);
	}

	return blockchain;
}

int main () {
	std::map<hash256_t, Block> blocks;

	// read block headers from stdin until EOF
	{
		while (true) {
			uint8_t rbuf[80];
			const auto read = fread(rbuf, sizeof(rbuf), 1, stdin);

			// EOF?
			if (read == 0) break;

			hash256_t hash, prevBlockHash;
			uint32_t bits;

			hash256(&hash[0], rbuf, 80);
			memcpy(&prevBlockHash[0], rbuf + 4, 32);
			memcpy(&bits, rbuf + 72, 4);

			blocks[hash] = Block(hash, prevBlockHash, bits);
		}

		std::cerr << "Read " << blocks.size() << " headers" << std::endl;
	}

	// how many tips exist?
	{
		const auto chainTips = findChainTips(blocks);
		std::cerr << "Found " << chainTips.size() << " chain tips" << std::endl;
	}

	// what is the best?
	{
		const auto bestBlockChain = findBest(blocks);
		const auto genesis = bestBlockChain.back();
		const auto tip = bestBlockChain.front();

		// output
		std::cerr << "Best chain" << std::endl;
		std::cerr << "- Height: " << bestBlockChain.size() - 1 << std::endl;
		std::cerr << std::hex;
		std::cerr << "- Genesis: ";
		for (size_t i = 31; i < 32; --i) std::cerr << std::setw(2) << std::setfill('0') << (uint32_t) genesis.hash[i];
		std::cerr << std::endl << "- Tip: ";
		for (size_t i = 31; i < 32; --i) std::cerr << std::setw(2) << std::setfill('0') << (uint32_t) tip.hash[i];
		std::cerr << std::endl << std::dec;

		for (auto it = bestBlockChain.crbegin(); it != bestBlockChain.crend(); ++it) {
			fwrite(&it->hash[0], 32, 1, stdout);
		}
	}

	return 0;
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 Chris Ohk, Youngjoong Kim, SeungHyun Jeon

// We are making my contributions/submissions to this project solely in our
// personal capacity and are not conveying any rights to any intellectual
// property of any third parties.

#ifndef ROSETTASTONE_TASK_ENUMS_HPP
#define ROSETTASTONE_TASK_ENUMS_HPP

#ifdef _MSC_VER
#define ROSETTASTONE_EXPAND_LARGE_ENUMS
#else  // _MSC_VER
#undef BETTER_ENUMS_MACRO_FILE
#define BETTER_ENUMS_MACRO_FILE "Rosetta/Commons/EnumMacros.hpp"
#endif  // _MSC_VER

#include <better-enums/enum.h>

namespace RosettaStone
{
//! \brief An enumerator for identifying each task.
#ifndef ROSETTASTONE_DOXYGEN
BETTER_ENUM(TaskID, int, INVALID, DRAW, DRAW_OP, OVERDRAW, MODIFY_MANA, DAMAGE,
            MULLIGAN, PLAY_CARD, ADD_ENCHANTMENT, DESTROY, DISCARD, HEAL_FULL,
            POISONOUS, FREEZE, HEAL, INCLUDE, CONTROL, END_TURN, CHOOSE, ATTACK,
            SET_GAME_TAG, ENQUEUE, RANDOM, SUMMON, TRANSFORM, CONDITION, FLAG);
#else
enum class TaskID
{
    INVALID,
    DRAW,
    DRAW_OP,
    OVERDRAW,
    MODIFY_MANA,
    DAMAGE,
    MULLIGAN,
    PLAY_CARD,
    ADD_ENCHANTMENT,
    DESTROY,
    DISCARD,
    HEAL_FULL,
    POISONOUS,
    FREEZE,
    HEAL,
    INCLUDE,
    CONTROL,
    END_TURN,
    CHOOSE,
    ATTACK,
    SET_GAME_TAG,
    ENQUEUE,
    RANDOM,
    SUMMON,
    TRANSFORM,
    CONDITION,
    FLAG,
};
#endif

//! \brief An enumerator for identifying entity type.
#ifndef ROSETTASTONE_DOXYGEN
BETTER_ENUM(EntityType, int, EMPTY, SOURCE, TARGET, ALL, FRIENDS, ENEMIES, HERO,
            ENEMY_HERO, WEAPON, ENEMY_WEAPON, HAND, ENEMY_HAND, ALL_MINIONS,
            MINIONS, ENEMY_MINIONS, STACK)
#else
enum class EntityType
{
    EMPTY,
    SOURCE,
    TARGET,
    ALL,
    FRIENDS,
    ENEMIES,
    HERO,
    ENEMY_HERO,
    WEAPON,
    ENEMY_WEAPON,
    HAND,
    ENEMY_HAND,
    ALL_MINIONS,
    MINIONS,
    ENEMY_MINIONS,
    STACK
};
#endif
}  // namespace RosettaStone

#endif  // ROSETTASTONE_TASK_ENUMS_HPP
<commit_msg>refactor: Use native enum class instead of better-enums library<commit_after>// Copyright (c) 2019 Chris Ohk, Youngjoong Kim, SeungHyun Jeon

// We are making my contributions/submissions to this project solely in our
// personal capacity and are not conveying any rights to any intellectual
// property of any third parties.

#ifndef ROSETTASTONE_TASK_ENUMS_HPP
#define ROSETTASTONE_TASK_ENUMS_HPP

#ifdef _MSC_VER
#define ROSETTASTONE_EXPAND_LARGE_ENUMS
#else  // _MSC_VER
#undef BETTER_ENUMS_MACRO_FILE
#define BETTER_ENUMS_MACRO_FILE "Rosetta/Commons/EnumMacros.hpp"
#endif  // _MSC_VER

#include <better-enums/enum.h>

namespace RosettaStone
{
//! \brief An enumerator for identifying each task.
enum class TaskID
{
    INVALID,
    DRAW,
    DRAW_OP,
    OVERDRAW,
    MODIFY_MANA,
    DAMAGE,
    MULLIGAN,
    PLAY_CARD,
    ADD_ENCHANTMENT,
    DESTROY,
    DISCARD,
    HEAL_FULL,
    POISONOUS,
    FREEZE,
    HEAL,
    INCLUDE,
    CONTROL,
    END_TURN,
    CHOOSE,
    ATTACK,
    SET_GAME_TAG,
    ENQUEUE,
    RANDOM,
    SUMMON,
    TRANSFORM,
    CONDITION,
    FLAG,

    NUM_TASK_ID
};

//! \brief An enumerator for identifying entity type.
enum class EntityType
{
    EMPTY,
    SOURCE,
    TARGET,
    ALL,
    FRIENDS,
    ENEMIES,
    HERO,
    ENEMY_HERO,
    WEAPON,
    ENEMY_WEAPON,
    HAND,
    ENEMY_HAND,
    ALL_MINIONS,
    MINIONS,
    ENEMY_MINIONS,
    STACK
};
}  // namespace RosettaStone

#endif  // ROSETTASTONE_TASK_ENUMS_HPP
<|endoftext|>


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

  Program:   Visualization Toolkit
  Module:    vtkCorrelativeStatistics.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

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

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/

#include "vtkToolkits.h"

#include "vtkCorrelativeStatistics.h"
#include "vtkBivariateStatisticsAlgorithmPrivate.h"

#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkStringArray.h"
#include "vtkStdString.h"
#include "vtkTable.h"
#include "vtkVariantArray.h"

#include <vtkstd/set>

#include <vtksys/ios/sstream>

vtkCxxRevisionMacro(vtkCorrelativeStatistics, "1.49");
vtkStandardNewMacro(vtkCorrelativeStatistics);

// ----------------------------------------------------------------------
vtkCorrelativeStatistics::vtkCorrelativeStatistics()
{
  this->AssessNames->SetNumberOfValues( 1 );
  this->AssessNames->SetValue( 0, "d^2" );

  this->AssessParameters = vtkStringArray::New();
  this->AssessParameters->SetNumberOfValues( 5 );
  this->AssessParameters->SetValue( 0, "Mean X" );
  this->AssessParameters->SetValue( 1, "Mean Y" );
  this->AssessParameters->SetValue( 2, "Variance X" );
  this->AssessParameters->SetValue( 3, "Variance Y" );
  this->AssessParameters->SetValue( 4, "Covariance" );
}

// ----------------------------------------------------------------------
vtkCorrelativeStatistics::~vtkCorrelativeStatistics()
{
  this->AssessParameters->Delete();
}

// ----------------------------------------------------------------------
void vtkCorrelativeStatistics::PrintSelf( ostream &os, vtkIndent indent )
{
  this->Superclass::PrintSelf( os, indent );
}

// ----------------------------------------------------------------------
void vtkCorrelativeStatistics::ExecuteLearn( vtkTable* inData,
                                             vtkDataObject* outMetaDO )
{
  vtkTable* outMeta = vtkTable::SafeDownCast( outMetaDO );
  if ( ! outMeta )
    {
    return;
    }

  vtkIdType nRow = inData->GetNumberOfRows();
  if ( ! nRow )
    {
    return;
    }

  if ( ! inData->GetNumberOfColumns() )
    {
    return;
    }

  vtkIdTypeArray* idTypeCol = vtkIdTypeArray::New();
  idTypeCol->SetName( "Cardinality" );
  outMeta->AddColumn( idTypeCol );
  idTypeCol->Delete();

  vtkStringArray* stringCol = vtkStringArray::New();
  stringCol->SetName( "Variable X" );
  outMeta->AddColumn( stringCol );
  stringCol->Delete();

  stringCol = vtkStringArray::New();
  stringCol->SetName( "Variable Y" );
  outMeta->AddColumn( stringCol );
  stringCol->Delete();

  vtkDoubleArray* doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "Mean X" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "Mean Y" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "M2 X" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "M2 Y" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "M XY" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  for ( vtkstd::set<vtkstd::pair<vtkStdString,vtkStdString> >::iterator it = this->Internals->Selection.begin();
        it != this->Internals->Selection.end(); ++ it )
    {
    vtkStdString colX = it->first;
    if ( ! inData->GetColumnByName( colX ) )
      {
      vtkWarningMacro( "InData table does not have a column "
                       << colX.c_str()
                       << ". Ignoring this pair." );
      continue;
      }

    vtkStdString colY = it->second;
    if ( ! inData->GetColumnByName( colY ) )
      {
      vtkWarningMacro( "InData table does not have a column "
                       << colY.c_str()
                       << ". Ignoring this pair." );
      continue;
      }

    double meanX = 0.;
    double meanY = 0.;
    double mom2X = 0.;
    double mom2Y = 0.;
    double momXY = 0.;

    double inv_n, x, y, delta, deltaXn;
    for ( vtkIdType r = 0; r < nRow; ++ r )
      {
      inv_n = 1. / ( r + 1. );

      x = inData->GetValueByName( r, colX ).ToDouble();
      delta = x - meanX;
      meanX += delta * inv_n;
      deltaXn = x - meanX;
      mom2X += delta * deltaXn;

      y = inData->GetValueByName( r, colY ).ToDouble();
      delta = y - meanY;
      meanY += delta * inv_n;
      mom2Y += delta * ( y - meanY );

      momXY += delta * deltaXn;
      }

    vtkVariantArray* row = vtkVariantArray::New();

    row->SetNumberOfValues( 8 );

    row->SetValue( 0, nRow );
    row->SetValue( 1, colX );
    row->SetValue( 2, colY );
    row->SetValue( 3, meanX );
    row->SetValue( 4, meanY );
    row->SetValue( 5, mom2X );
    row->SetValue( 6, mom2Y );
    row->SetValue( 7, momXY );

    outMeta->InsertNextRow( row );

    row->Delete();
    }

  return;
}

// ----------------------------------------------------------------------
void vtkCorrelativeStatistics::ExecuteDerive( vtkDataObject* inMetaDO )
{
  vtkTable* inMeta = vtkTable::SafeDownCast( inMetaDO );
  if ( ! inMeta )
    {
    return;
    }

  vtkIdType nCol = inMeta->GetNumberOfColumns();
  if ( nCol < 8 )
    {
    return;
    }

  vtkIdType nRow = inMeta->GetNumberOfRows();
  if ( ! nRow )
    {
    return;
    }

  int numDoubles = 8;
  vtkStdString doubleNames[] = { "Variance X",
                                 "Variance Y",
                                 "Covariance",
                                 "Slope Y/X",
                                 "Intersect Y/X",
                                 "Slope X/Y",
                                 "Intersect X/Y",
                                 "Pearson r" };

  vtkDoubleArray* doubleCol;
  for ( int j = 0; j < numDoubles; ++ j )
    {
    if ( ! inMeta->GetColumnByName( doubleNames[j] ) )
      {
      doubleCol = vtkDoubleArray::New();
      doubleCol->SetName( doubleNames[j] );
      doubleCol->SetNumberOfTuples( nRow );
      inMeta->AddColumn( doubleCol );
      doubleCol->Delete();
      }
    }

  if ( ! inMeta->GetColumnByName( "Linear Correlation" ) )
    {
    vtkStringArray* stringCol = vtkStringArray::New();
    stringCol->SetName( "Linear Correlation" );
    stringCol->SetNumberOfTuples( nRow );
    inMeta->AddColumn( stringCol );
    stringCol->Delete();
    }

  double* doubleVals = new double[numDoubles]; // var x, var y, cov, slope y/x, int. y/x, slope x/y, int. x/y, r

  for ( int i = 0; i < nRow; ++ i )
    {
    vtkStdString c1 = inMeta->GetValueByName( i, "Variable X" ).ToString();
    vtkStdString c2 = inMeta->GetValueByName( i, "Variable Y" ).ToString();
    double m2X = inMeta->GetValueByName( i, "M2 X" ).ToDouble();
    double m2Y = inMeta->GetValueByName( i, "M2 Y" ).ToDouble();
    double mXY = inMeta->GetValueByName( i, "M XY" ).ToDouble();

    double varX, varY, covXY;
    int numSamples = inMeta->GetValueByName(i, "Cardinality" ).ToInt();
    if ( numSamples == 1 )
      {
      varX  = 0.;
      varY  = 0.;
      covXY = 0.;
      }
    else
      {
      double inv_nm1;
      double n = static_cast<double>( numSamples );
      inv_nm1 = 1. / ( n - 1. );
      varX  = m2X * inv_nm1;
      varY  = m2Y * inv_nm1;
      covXY = mXY * inv_nm1;
      }

    doubleVals[0] = varX;
    doubleVals[1] = varY;
    doubleVals[2] = covXY;

    vtkStdString status = "valid";

    double d = varX * varY - covXY * covXY;
    if ( d <= 0. )
      {
      vtkWarningMacro( "Incorrect parameters for column pair ("
                       <<c1.c_str()
                       <<", "
                       <<c2.c_str()
                       <<"): variance/covariance matrix has non-positive determinant." );
      doubleVals[3] = 0.;
      doubleVals[4] = 0.;
      doubleVals[5] = 0.;
      doubleVals[6] = 0.;
      doubleVals[7] = 0.;
      status = "invalid";
      }
    else
      {
      double meanX = inMeta->GetValueByName( i, "Mean X" ).ToDouble();
      double meanY = inMeta->GetValueByName( i, "Mean Y" ).ToDouble();

      // variable Y on variable X:
      //   slope
      doubleVals[3] = covXY / varX;
      //   intersect
      doubleVals[4] = meanY - doubleVals[0] * meanX;

      //   variable X on variable Y:
      //   slope
      doubleVals[5] = covXY / varY;
      //   intersect
      doubleVals[6] = meanX - doubleVals[2] * meanY;

      // correlation coefficient
      doubleVals[7] = covXY / sqrt( varX * varY );
      }

    inMeta->SetValueByName( i, "Linear Correlation", status );
    for ( int j = 0; j < numDoubles; ++ j )
      {
      inMeta->SetValueByName( i, doubleNames[j], doubleVals[j] );
      }
    }

  delete [] doubleVals;
}

// ----------------------------------------------------------------------
class TableColumnPairMahlanobisFunctor : public vtkStatisticsAlgorithm::AssessFunctor
{
public:
  vtkDataArray* DataX;
  vtkDataArray* DataY;
  double MeanX;
  double MeanY;
  double VarX;
  double VarY;
  double CovXY;
  double DInv;

  TableColumnPairMahlanobisFunctor( vtkDataArray* valsX,
                                    vtkDataArray* valsY,
                                    double meanX,
                                    double meanY,
                                    double varX,
                                    double varY,
                                    double covXY,
                                    double dInv )
  {
    this->DataX = valsX;
    this->DataY = valsY;
    this->MeanX = meanX;
    this->MeanY = meanY;
    this->VarX  = varX;
    this->VarY  = varY;
    this->CovXY = covXY;
    this->DInv  = dInv;
  }
  virtual ~TableColumnPairMahlanobisFunctor() { }
  virtual void operator() ( vtkVariantArray* result,
                            vtkIdType id )
  {
    double x = this->DataX->GetTuple1( id ) - this->MeanX;
    double y = this->DataY->GetTuple1( id ) - this->MeanY;

    result->SetNumberOfValues( 1 );
    result->SetValue( 0, ( this->VarY * x * x - 2. * this->CovXY * x * y + this->VarX * y * y ) * this->DInv );
  }
};

// ----------------------------------------------------------------------
void vtkCorrelativeStatistics::SelectAssessFunctor( vtkTable* outData,
                                                    vtkDataObject* inMetaDO,
                                                    vtkStringArray* rowNames,
                                                    AssessFunctor*& dfunc )
{
  vtkTable* inMeta = vtkTable::SafeDownCast( inMetaDO );
  if ( ! inMeta )
    {
    return;
    }

  vtkStdString varNameX = rowNames->GetValue( 0 );
  vtkStdString varNameY = rowNames->GetValue( 1 );

  // Downcast meta columns to string arrays for efficient data access
  vtkStringArray* varX = vtkStringArray::SafeDownCast( inMeta->GetColumnByName( "Variable X" ) );
  vtkStringArray* varY = vtkStringArray::SafeDownCast( inMeta->GetColumnByName( "Variable Y" ) );
  if ( ! varX || ! varY )
    {
    dfunc = 0;
    return;
    }

  // Loop over parameters table until the requested variables are found
  vtkIdType nRowP = inMeta->GetNumberOfRows();
  for ( int r = 0; r < nRowP; ++ r )
    {
    if ( varX->GetValue( r ) == varNameX
         &&
         varY->GetValue( r ) == varNameY )
      {
      // Grab the data for the requested variables
      vtkAbstractArray* arrX = outData->GetColumnByName( varNameX );
      vtkAbstractArray* arrY = outData->GetColumnByName( varNameY );
      if ( ! arrX || ! arrY )
        {
        dfunc = 0;
        return;
        }

      // For descriptive statistics, types must be convertible to DataArrays (e.g., StringArrays do not fit here).
      vtkDataArray* valsX = vtkDataArray::SafeDownCast( arrX );
      vtkDataArray* valsY = vtkDataArray::SafeDownCast( arrY );
      if ( ! valsX || ! valsY )
        {
        dfunc = 0;
        return;
        }

      double meanX = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 0 ) ).ToDouble();
      double meanY = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 1 ) ).ToDouble();
      double variX = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 2 ) ).ToDouble();
      double variY = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 3 ) ).ToDouble();
      double covXY = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 4 ) ).ToDouble();

      double d = variX * variY - covXY * covXY;
      if ( d <= 0. )
        {
        vtkWarningMacro( "Incorrect parameters for column pair:"
                         << " variance/covariance matrix has non-positive determinant"
                         << " (assessment values will be set to -1)." );

        dfunc = 0;
        }
      else
        {
        dfunc = new TableColumnPairMahlanobisFunctor( valsX,
                                                      valsY,
                                                      meanX,
                                                      meanY,
                                                      variX,
                                                      variY,
                                                      covXY,
                                                      1. / d );
        }

      return;
      }
    }

  // The pair of variables of interest was not found in the parameter table
  dfunc = 0;
}
<commit_msg>STYLE: label derived statistics as such<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkCorrelativeStatistics.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

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

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/

#include "vtkToolkits.h"

#include "vtkCorrelativeStatistics.h"
#include "vtkBivariateStatisticsAlgorithmPrivate.h"

#include "vtkDoubleArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkStringArray.h"
#include "vtkStdString.h"
#include "vtkTable.h"
#include "vtkVariantArray.h"

#include <vtkstd/set>

#include <vtksys/ios/sstream>

vtkCxxRevisionMacro(vtkCorrelativeStatistics, "1.50");
vtkStandardNewMacro(vtkCorrelativeStatistics);

// ----------------------------------------------------------------------
vtkCorrelativeStatistics::vtkCorrelativeStatistics()
{
  this->AssessNames->SetNumberOfValues( 1 );
  this->AssessNames->SetValue( 0, "d^2" );

  this->AssessParameters = vtkStringArray::New();
  this->AssessParameters->SetNumberOfValues( 5 );
  this->AssessParameters->SetValue( 0, "Mean X" );
  this->AssessParameters->SetValue( 1, "Mean Y" );
  this->AssessParameters->SetValue( 2, "Variance X" );
  this->AssessParameters->SetValue( 3, "Variance Y" );
  this->AssessParameters->SetValue( 4, "Covariance" );
}

// ----------------------------------------------------------------------
vtkCorrelativeStatistics::~vtkCorrelativeStatistics()
{
  this->AssessParameters->Delete();
}

// ----------------------------------------------------------------------
void vtkCorrelativeStatistics::PrintSelf( ostream &os, vtkIndent indent )
{
  this->Superclass::PrintSelf( os, indent );
}

// ----------------------------------------------------------------------
void vtkCorrelativeStatistics::ExecuteLearn( vtkTable* inData,
                                             vtkDataObject* outMetaDO )
{
  vtkTable* outMeta = vtkTable::SafeDownCast( outMetaDO );
  if ( ! outMeta )
    {
    return;
    }

  vtkIdType nRow = inData->GetNumberOfRows();
  if ( ! nRow )
    {
    return;
    }

  if ( ! inData->GetNumberOfColumns() )
    {
    return;
    }

  vtkIdTypeArray* idTypeCol = vtkIdTypeArray::New();
  idTypeCol->SetName( "Cardinality" );
  outMeta->AddColumn( idTypeCol );
  idTypeCol->Delete();

  vtkStringArray* stringCol = vtkStringArray::New();
  stringCol->SetName( "Variable X" );
  outMeta->AddColumn( stringCol );
  stringCol->Delete();

  stringCol = vtkStringArray::New();
  stringCol->SetName( "Variable Y" );
  outMeta->AddColumn( stringCol );
  stringCol->Delete();

  vtkDoubleArray* doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "Mean X" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "Mean Y" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "M2 X" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "M2 Y" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  doubleCol = vtkDoubleArray::New();
  doubleCol->SetName( "M XY" );
  outMeta->AddColumn( doubleCol );
  doubleCol->Delete();

  for ( vtkstd::set<vtkstd::pair<vtkStdString,vtkStdString> >::iterator it = this->Internals->Selection.begin();
        it != this->Internals->Selection.end(); ++ it )
    {
    vtkStdString colX = it->first;
    if ( ! inData->GetColumnByName( colX ) )
      {
      vtkWarningMacro( "InData table does not have a column "
                       << colX.c_str()
                       << ". Ignoring this pair." );
      continue;
      }

    vtkStdString colY = it->second;
    if ( ! inData->GetColumnByName( colY ) )
      {
      vtkWarningMacro( "InData table does not have a column "
                       << colY.c_str()
                       << ". Ignoring this pair." );
      continue;
      }

    double meanX = 0.;
    double meanY = 0.;
    double mom2X = 0.;
    double mom2Y = 0.;
    double momXY = 0.;

    double inv_n, x, y, delta, deltaXn;
    for ( vtkIdType r = 0; r < nRow; ++ r )
      {
      inv_n = 1. / ( r + 1. );

      x = inData->GetValueByName( r, colX ).ToDouble();
      delta = x - meanX;
      meanX += delta * inv_n;
      deltaXn = x - meanX;
      mom2X += delta * deltaXn;

      y = inData->GetValueByName( r, colY ).ToDouble();
      delta = y - meanY;
      meanY += delta * inv_n;
      mom2Y += delta * ( y - meanY );

      momXY += delta * deltaXn;
      }

    vtkVariantArray* row = vtkVariantArray::New();

    row->SetNumberOfValues( 8 );

    row->SetValue( 0, nRow );
    row->SetValue( 1, colX );
    row->SetValue( 2, colY );
    row->SetValue( 3, meanX );
    row->SetValue( 4, meanY );
    row->SetValue( 5, mom2X );
    row->SetValue( 6, mom2Y );
    row->SetValue( 7, momXY );

    outMeta->InsertNextRow( row );

    row->Delete();
    }

  return;
}

// ----------------------------------------------------------------------
void vtkCorrelativeStatistics::ExecuteDerive( vtkDataObject* inMetaDO )
{
  vtkTable* inMeta = vtkTable::SafeDownCast( inMetaDO );
  if ( ! inMeta )
    {
    return;
    }

  vtkIdType nCol = inMeta->GetNumberOfColumns();
  if ( nCol < 8 )
    {
    return;
    }

  vtkIdType nRow = inMeta->GetNumberOfRows();
  if ( ! nRow )
    {
    return;
    }

  int numDoubles = 8;
  vtkStdString doubleNames[] = { "Variance X",
                                 "Variance Y",
                                 "Covariance",
                                 "Slope Y/X",
                                 "Intersect Y/X",
                                 "Slope X/Y",
                                 "Intersect X/Y",
                                 "Pearson r" };

  vtkDoubleArray* doubleCol;
  for ( int j = 0; j < numDoubles; ++ j )
    {
    if ( ! inMeta->GetColumnByName( doubleNames[j] ) )
      {
      doubleCol = vtkDoubleArray::New();
      doubleCol->SetName( doubleNames[j] );
      doubleCol->SetNumberOfTuples( nRow );
      inMeta->AddColumn( doubleCol );
      doubleCol->Delete();
      }
    }

  if ( ! inMeta->GetColumnByName( "Linear Correlation" ) )
    {
    vtkStringArray* stringCol = vtkStringArray::New();
    stringCol->SetName( "Linear Correlation" );
    stringCol->SetNumberOfTuples( nRow );
    inMeta->AddColumn( stringCol );
    stringCol->Delete();
    }

  double* derivedVals = new double[numDoubles]; // var x, var y, cov, slope y/x, int. y/x, slope x/y, int. x/y, r

  for ( int i = 0; i < nRow; ++ i )
    {
    vtkStdString c1 = inMeta->GetValueByName( i, "Variable X" ).ToString();
    vtkStdString c2 = inMeta->GetValueByName( i, "Variable Y" ).ToString();
    double m2X = inMeta->GetValueByName( i, "M2 X" ).ToDouble();
    double m2Y = inMeta->GetValueByName( i, "M2 Y" ).ToDouble();
    double mXY = inMeta->GetValueByName( i, "M XY" ).ToDouble();

    double varX, varY, covXY;
    int numSamples = inMeta->GetValueByName(i, "Cardinality" ).ToInt();
    if ( numSamples == 1 )
      {
      varX  = 0.;
      varY  = 0.;
      covXY = 0.;
      }
    else
      {
      double inv_nm1;
      double n = static_cast<double>( numSamples );
      inv_nm1 = 1. / ( n - 1. );
      varX  = m2X * inv_nm1;
      varY  = m2Y * inv_nm1;
      covXY = mXY * inv_nm1;
      }

    derivedVals[0] = varX;
    derivedVals[1] = varY;
    derivedVals[2] = covXY;

    vtkStdString status = "valid";

    double d = varX * varY - covXY * covXY;
    if ( d <= 0. )
      {
      vtkWarningMacro( "Incorrect parameters for column pair ("
                       <<c1.c_str()
                       <<", "
                       <<c2.c_str()
                       <<"): variance/covariance matrix has non-positive determinant." );
      derivedVals[3] = 0.;
      derivedVals[4] = 0.;
      derivedVals[5] = 0.;
      derivedVals[6] = 0.;
      derivedVals[7] = 0.;
      status = "invalid";
      }
    else
      {
      double meanX = inMeta->GetValueByName( i, "Mean X" ).ToDouble();
      double meanY = inMeta->GetValueByName( i, "Mean Y" ).ToDouble();

      // variable Y on variable X:
      //   slope
      derivedVals[3] = covXY / varX;
      //   intersect
      derivedVals[4] = meanY - derivedVals[0] * meanX;

      //   variable X on variable Y:
      //   slope
      derivedVals[5] = covXY / varY;
      //   intersect
      derivedVals[6] = meanX - derivedVals[2] * meanY;

      // correlation coefficient
      derivedVals[7] = covXY / sqrt( varX * varY );
      }

    inMeta->SetValueByName( i, "Linear Correlation", status );
    for ( int j = 0; j < numDoubles; ++ j )
      {
      inMeta->SetValueByName( i, doubleNames[j], derivedVals[j] );
      }
    }

  delete [] derivedVals;
}

// ----------------------------------------------------------------------
class TableColumnPairMahlanobisFunctor : public vtkStatisticsAlgorithm::AssessFunctor
{
public:
  vtkDataArray* DataX;
  vtkDataArray* DataY;
  double MeanX;
  double MeanY;
  double VarX;
  double VarY;
  double CovXY;
  double DInv;

  TableColumnPairMahlanobisFunctor( vtkDataArray* valsX,
                                    vtkDataArray* valsY,
                                    double meanX,
                                    double meanY,
                                    double varX,
                                    double varY,
                                    double covXY,
                                    double dInv )
  {
    this->DataX = valsX;
    this->DataY = valsY;
    this->MeanX = meanX;
    this->MeanY = meanY;
    this->VarX  = varX;
    this->VarY  = varY;
    this->CovXY = covXY;
    this->DInv  = dInv;
  }
  virtual ~TableColumnPairMahlanobisFunctor() { }
  virtual void operator() ( vtkVariantArray* result,
                            vtkIdType id )
  {
    double x = this->DataX->GetTuple1( id ) - this->MeanX;
    double y = this->DataY->GetTuple1( id ) - this->MeanY;

    result->SetNumberOfValues( 1 );
    result->SetValue( 0, ( this->VarY * x * x - 2. * this->CovXY * x * y + this->VarX * y * y ) * this->DInv );
  }
};

// ----------------------------------------------------------------------
void vtkCorrelativeStatistics::SelectAssessFunctor( vtkTable* outData,
                                                    vtkDataObject* inMetaDO,
                                                    vtkStringArray* rowNames,
                                                    AssessFunctor*& dfunc )
{
  vtkTable* inMeta = vtkTable::SafeDownCast( inMetaDO );
  if ( ! inMeta )
    {
    return;
    }

  vtkStdString varNameX = rowNames->GetValue( 0 );
  vtkStdString varNameY = rowNames->GetValue( 1 );

  // Downcast meta columns to string arrays for efficient data access
  vtkStringArray* varX = vtkStringArray::SafeDownCast( inMeta->GetColumnByName( "Variable X" ) );
  vtkStringArray* varY = vtkStringArray::SafeDownCast( inMeta->GetColumnByName( "Variable Y" ) );
  if ( ! varX || ! varY )
    {
    dfunc = 0;
    return;
    }

  // Loop over parameters table until the requested variables are found
  vtkIdType nRowP = inMeta->GetNumberOfRows();
  for ( int r = 0; r < nRowP; ++ r )
    {
    if ( varX->GetValue( r ) == varNameX
         &&
         varY->GetValue( r ) == varNameY )
      {
      // Grab the data for the requested variables
      vtkAbstractArray* arrX = outData->GetColumnByName( varNameX );
      vtkAbstractArray* arrY = outData->GetColumnByName( varNameY );
      if ( ! arrX || ! arrY )
        {
        dfunc = 0;
        return;
        }

      // For descriptive statistics, types must be convertible to DataArrays (e.g., StringArrays do not fit here).
      vtkDataArray* valsX = vtkDataArray::SafeDownCast( arrX );
      vtkDataArray* valsY = vtkDataArray::SafeDownCast( arrY );
      if ( ! valsX || ! valsY )
        {
        dfunc = 0;
        return;
        }

      double meanX = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 0 ) ).ToDouble();
      double meanY = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 1 ) ).ToDouble();
      double variX = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 2 ) ).ToDouble();
      double variY = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 3 ) ).ToDouble();
      double covXY = inMeta->GetValueByName( r, this->AssessParameters->GetValue( 4 ) ).ToDouble();

      double d = variX * variY - covXY * covXY;
      if ( d <= 0. )
        {
        vtkWarningMacro( "Incorrect parameters for column pair:"
                         << " variance/covariance matrix has non-positive determinant"
                         << " (assessment values will be set to -1)." );

        dfunc = 0;
        }
      else
        {
        dfunc = new TableColumnPairMahlanobisFunctor( valsX,
                                                      valsY,
                                                      meanX,
                                                      meanY,
                                                      variX,
                                                      variY,
                                                      covXY,
                                                      1. / d );
        }

      return;
      }
    }

  // The pair of variables of interest was not found in the parameter table
  dfunc = 0;
}
<|endoftext|>


<commit_before>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_XCODE_PROJECT_HPP
#define OUZEL_XCODE_PROJECT_HPP

#include <fstream>
#include "OuzelProject.hpp"
#include "PBXBuildFile.hpp"
#include "PBXContainerItemProxy.hpp"
#include "PBXFileReference.hpp"
#include "PBXFrameworksBuildPhase.hpp"
#include "PBXGroup.hpp"
#include "PBXNativeTarget.hpp"
#include "PBXProject.hpp"
#include "PBXReferenceProxy.hpp"
#include "PBXResourcesBuildPhase.hpp"
#include "PBXShellScriptBuildPhase.hpp"
#include "PBXSourcesBuildPhase.hpp"
#include "PBXTargetDependency.hpp"
#include "XCBuildConfiguration.hpp"
#include "XCConfigurationList.hpp"
#include "storage/FileSystem.hpp"
#include "utils/Plist.hpp"

namespace ouzel
{
    namespace xcode
    {
        class Project final
        {
        public:
            Project(const OuzelProject& p):
                project(p)
            {
            }

            void generate()
            {
                const storage::Path projectFilename = project.getPath().getFilename();
                const storage::Path projectDirectory = project.getPath().getDirectory();

                auto xcodeProjectDirectory = projectDirectory / storage::Path{project.getName() + ".xcodeproj"};
                auto xcodeProjectDirectoryType = xcodeProjectDirectory.getType();

                if (xcodeProjectDirectoryType == storage::Path::Type::NotFound)
                    storage::FileSystem::createDirectory(xcodeProjectDirectory);
                else if (xcodeProjectDirectoryType != storage::Path::Type::Directory)
                {
                    storage::FileSystem::deleteFile(xcodeProjectDirectory);
                    storage::FileSystem::createDirectory(xcodeProjectDirectory);
                }

                auto pbxProjectFile = xcodeProjectDirectory / storage::Path{"project.pbxproj"};

                constexpr auto libouzelIosId = Id{0x30, 0x3B, 0x75, 0x33, 0x1C, 0x2A, 0x3C, 0x58, 0x00, 0xFE, 0xDE, 0x92};
                constexpr auto libouzelMacOsId = Id{0x30, 0xA3, 0x96, 0x29, 0x24, 0x37, 0x73, 0xB5, 0x00, 0xD8, 0xE2, 0x8E};
                constexpr auto libouzelTvosId = Id{0x30, 0xA3, 0x96, 0x29, 0x24, 0x37, 0x73, 0xB5, 0x00, 0xD8, 0xE2, 0x8E};
                constexpr auto ouzelId = Id{0x30, 0xA3, 0x96, 0x29, 0x24, 0x37, 0x73, 0xB5, 0x00, 0xD8, 0xE2, 0x8E};

                const auto ouzelProjectPath = project.getOuzelPath() / "build" / "ouzel.xcodeproj";
                const auto& ouzelProjectFileRef = create<PBXFileReference>("ouzel.xcodeproj", ouzelProjectPath,
                                                                           PBXFileType::WrapperPBProject,
                                                                           PBXSourceTree::Group);
                const auto& libouzelIosProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                             PBXContainerItemProxy::Reference,
                                                                             libouzelIosId, "libouzel_ios");

                const auto& libouzelIosReferenceProxy = create<PBXReferenceProxy>("", "libouzel_ios.a",
                                                                                PBXFileType::ArchiveAr,
                                                                                PBXSourceTree::BuildProductsDir,
                                                                                libouzelIosProxy);

                const auto& libouzelMacOsProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                               PBXContainerItemProxy::Reference,
                                                                               libouzelMacOsId, "libouzel_macos");

                const auto& libouzelMacOsReferenceProxy = create<PBXReferenceProxy>("", "libouzel_macos.a",
                                                                                PBXFileType::ArchiveAr,
                                                                                PBXSourceTree::BuildProductsDir,
                                                                                libouzelMacOsProxy);

                const auto& libouzelTvosProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                              PBXContainerItemProxy::Reference,
                                                                              libouzelTvosId, "libouzel_tvos");

                const auto& libouzelTvosReferenceProxy = create<PBXReferenceProxy>("", "libouzel_tvos.a",
                                                                                PBXFileType::ArchiveAr,
                                                                                PBXSourceTree::BuildProductsDir,
                                                                                libouzelTvosProxy);

                const auto& ouzelProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                       PBXContainerItemProxy::Reference,
                                                                       ouzelId, "ouzel");

                const auto& ouzelNativeTargetProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                                PBXContainerItemProxy::NativeTarget,
                                                                                ouzelId, "ouzel");

                const auto& ouzelReferenceProxy = create<PBXReferenceProxy>("", "ouzel",
                                                                            PBXFileType::CompiledMachOExecutable,
                                                                            PBXSourceTree::BuildProductsDir,
                                                                            ouzelProxy);

                const auto& ouzelPoductRefGroup = create<PBXGroup>("Products", storage::Path{},
                                                                   std::vector<PBXFileElementRef>{
                                                                       libouzelIosReferenceProxy,
                                                                       libouzelMacOsReferenceProxy,
                                                                       libouzelTvosReferenceProxy,
                                                                       ouzelReferenceProxy},
                                                                   PBXSourceTree::Group);

                const auto& productFile = create<PBXFileReference>("", storage::Path{project.getName() + ".app"},
                                                                   PBXFileType::WrapperApplication,
                                                                   PBXSourceTree::BuildProductsDir);

                const auto& resourcesGroup = create<PBXGroup>("", storage::Path{"Resources"},
                                                              std::vector<PBXFileElementRef>{},
                                                              PBXSourceTree::Group);

                const auto& productRefGroup = create<PBXGroup>("Products", storage::Path{},
                                                               std::vector<PBXFileElementRef>{productFile},
                                                               PBXSourceTree::Group);

                std::vector<PBXBuildFileRef> buildFiles;
                std::vector<PBXFileElementRef> sourceFiles;

                for (const auto& sourceFile : project.getSourceFiles())
                {
                    const auto extension = sourceFile.getExtension();
                    // TODO: support more file formats
                    const auto fileType = extension == "plist" ? PBXFileType::TextPlistXml :
                        extension == "c" ? PBXFileType::SourcecodeC :
                        extension == "h" ? PBXFileType::SourcecodeCH :
                        extension == "cpp" ? PBXFileType::SourcecodeCppCpp :
                        extension == "hpp" ? PBXFileType::SourcecodeCppH :
                        throw std::runtime_error("Unsupported file type");

                    const auto& fileReference = create<PBXFileReference>("", sourceFile, fileType, PBXSourceTree::Group);
                    sourceFiles.push_back(fileReference);

                    const auto& buildFile = create<PBXBuildFile>(fileReference);
                    buildFiles.push_back(buildFile);
                }

                const auto& sourceGroup = create<PBXGroup>("src", storage::Path{"src"},
                                                           sourceFiles, PBXSourceTree::Group);

                const auto headerSearchPath = std::string(project.getOuzelPath() / "engine");
                const auto& debugConfiguration = create<XCBuildConfiguration>("Debug",
                                                                              std::map<std::string, std::string>{
                                                                                {"CLANG_CXX_LANGUAGE_STANDARD", "c++14"},
                                                                                {"CLANG_CXX_LIBRARY", "libc++"},
                                                                                {"ENABLE_TESTABILITY", "YES"},
                                                                                {"GCC_OPTIMIZATION_LEVEL", "0"},
                                                                                {"GCC_PREPROCESSOR_DEFINITIONS", "DEBUG=1"},
                                                                                {"HEADER_SEARCH_PATHS", headerSearchPath},
                                                                                {"ONLY_ACTIVE_ARCH", "YES"}});

                const auto& releaseConfiguration = create<XCBuildConfiguration>("Release",
                                                                                std::map<std::string, std::string>{
                                                                                {"CLANG_CXX_LANGUAGE_STANDARD", "c++14"},
                                                                                {"CLANG_CXX_LIBRARY", "libc++"},
                                                                                {"HEADER_SEARCH_PATHS", headerSearchPath}});

                const auto& projectConfigurationList = create<XCConfigurationList>(std::vector<XCBuildConfigurationRef>{
                                                                                debugConfiguration,
                                                                                releaseConfiguration},
                                                                                releaseConfiguration.getName());

                std::vector<PBXTargetRef> targets;
                std::vector<PBXFileElementRef> frameworkFiles;

                for (const auto platform : project.getPlatforms())
                {
                    // TODO: do it for all platforms
                    if (platform == Platform::MacOs ||
                        platform == Platform::Ios ||
                        platform == Platform::Tvos)
                    {
                        std::map<std::string, std::string> buildSettings = {{"PRODUCT_NAME", project.getName()}};

                        switch (platform)
                        {
                            case Platform::MacOs:
                                buildSettings["MACOSX_DEPLOYMENT_TARGET"] = "10.8";
                                break;

                            case Platform::Ios:
                                buildSettings["SDKROOT"] = "iphoneos";
                                buildSettings["IPHONEOS_DEPLOYMENT_TARGET"] = "8.0";
                                break;

                            case Platform::Tvos:
                                buildSettings["SDKROOT"] = "appletvos";
                                buildSettings["TVOS_DEPLOYMENT_TARGET"] = "9.0";
                                break;

                            default:
                                throw std::runtime_error("Unsupported platform");
                        }

                        const auto& targetDebugConfiguration = create<XCBuildConfiguration>("Debug", buildSettings);

                        const auto& targetReleaseConfiguration = create<XCBuildConfiguration>("Release", buildSettings);

                        const auto& targetConfigurationList = create<XCConfigurationList>(std::vector<XCBuildConfigurationRef>{
                                                                                targetDebugConfiguration,
                                                                                targetReleaseConfiguration},
                                                                                targetReleaseConfiguration.getName());

                        const auto& sourcesBuildPhase = create<PBXSourcesBuildPhase>(buildFiles);

                        std::vector<PBXFileReferenceRef> frameworkFileReferences;
                        std::vector<PBXBuildFileRef> frameworkBuildFiles;

                        switch (platform)
                        {
                            case Platform::MacOs:
                            {
                                const auto frameworksPath = storage::Path{"System/Library/Frameworks"};
                                for (const auto& framework : {
                                    "AudioToolbox.framework",
                                    "AudioUnit.framework",
                                    "Cocoa.framework",
                                    "CoreAudio.framework",
                                    "CoreVideo.framework",
                                    "GameController.framework",
                                    "IOKit.framework",
                                    "Metal.framework",
                                    "OpenAL.framework",
                                    "OpenGL.framework",
                                    "QuartzCore.framework"
                                })
                                {
                                    const auto& frameworkFileReference = create<PBXFileReference>(framework,
                                                                                frameworksPath / framework,
                                                                                PBXFileType::WrapperFramework,
                                                                                PBXSourceTree::SdkRoot);

                                    frameworkFileReferences.push_back(frameworkFileReference);
                                    frameworkFiles.push_back(frameworkFileReference);
                                }

                                const auto& libouzelMacOsBuildFile = create<PBXBuildFile>(libouzelMacOsReferenceProxy);
                                frameworkBuildFiles.push_back(libouzelMacOsBuildFile);

                                break;
                            }

                            case Platform::Ios:
                            {
                                const auto& libouzelIosBuildFile = create<PBXBuildFile>(libouzelIosReferenceProxy);
                                frameworkBuildFiles.push_back(libouzelIosBuildFile);
                                break;
                            }

                            case Platform::Tvos:
                            {
                                const auto& libouzelTvosBuildFile = create<PBXBuildFile>(libouzelTvosReferenceProxy);
                                frameworkBuildFiles.push_back(libouzelTvosBuildFile);
                                break;
                            }

                            default:
                                throw std::runtime_error("Unsupported platform");
                        }

                        for (const PBXFileReferenceRef& frameworkFileReference : frameworkFileReferences)
                        {
                            const auto& frameworkBuildFile = create<PBXBuildFile>(frameworkFileReference);
                            frameworkBuildFiles.push_back(frameworkBuildFile);
                        }

                        const auto& frameworksBuildPhase = create<PBXFrameworksBuildPhase>(frameworkBuildFiles);

                        // TODO: implement asset build shell script
                        const auto& assetsBuildPhase = create<PBXShellScriptBuildPhase>("$BUILT_PRODUCTS_DIR/ouzel --export-assets $PROJECT_DIR/" + std::string(projectFilename));

                        // TODO: implement resource copy
                        const auto& resourcesBuildPhase = create<PBXResourcesBuildPhase>(std::vector<PBXBuildFileRef>{});

                        const auto& ouzelDependency = create<PBXTargetDependency>("ouzel", ouzelNativeTargetProxy);

                        const auto targetName = project.getName() + (
                            (platform == Platform::MacOs) ? " macOS" :
                            (platform == Platform::Ios) ? " iOS" :
                            (platform == Platform::Tvos) ? " tvOS" : "");

                        const auto& nativeTarget = create<PBXNativeTarget>(targetName,
                                                                           targetConfigurationList,
                                                                           std::vector<PBXBuildPhaseRef>{sourcesBuildPhase, frameworksBuildPhase, assetsBuildPhase, resourcesBuildPhase},
                                                                           std::vector<PBXTargetDependencyRef>{ouzelDependency},
                                                                           productFile);
                        targets.push_back(nativeTarget);
                    }
                }

                const auto& frameworksGroup = create<PBXGroup>("Frameworks", storage::Path{},
                                                               frameworkFiles,
                                                               PBXSourceTree::Group);

                const auto& mainGroup = create<PBXGroup>("", storage::Path{},
                                                         std::vector<PBXFileElementRef>{
                                                             frameworksGroup,
                                                             ouzelProjectFileRef,
                                                             productRefGroup,
                                                             resourcesGroup,
                                                             sourceGroup},
                                                         PBXSourceTree::Group);

                const auto& pbxProject = create<PBXProject>(project.getOrganization(),
                                                            projectConfigurationList,
                                                            mainGroup,
                                                            productRefGroup,
                                                            std::map<std::string, PBXObjectRef>{
                                                                {"ProductGroup", ouzelPoductRefGroup},
                                                                {"ProjectRef", ouzelProjectFileRef}},
                                                            targets);

                rootObject = &pbxProject;

                std::ofstream file(pbxProjectFile, std::ios::trunc);
                file << plist::encode(encode());
            }

        private:
            template <class T, class ...Args>
            T& create(Args&& ...args)
            {
                std::unique_ptr<T> object = std::make_unique<T>(std::forward<Args>(args)...);
                T& result = *object;
                objects.push_back(std::move(object));
                return result;
            }

            plist::Value encode() const {
                plist::Value result = plist::Value::Dictionary{
                    {"archiveVersion", 1},
                    {"classes", plist::Value::Dictionary{}},
                    {"objectVersion", 50},
                    {"objects", plist::Value::Dictionary{}},
                    {"rootObject", rootObject ? toString(rootObject->getId()): ""}
                };

                for (const auto& object : objects)
                    result["objects"][toString(object->getId())] = object->encode();

                return result;
            }

            const OuzelProject& project;
            std::vector<std::unique_ptr<PBXObject>> objects;
            const PBXObject* rootObject;
        };
    }
}

#endif // OUZEL_XCODE_PROJECT_HPP
<commit_msg>Link frameworks to iOS and tvOS targets<commit_after>// Copyright 2015-2020 Elviss Strazdins. All rights reserved.

#ifndef OUZEL_XCODE_PROJECT_HPP
#define OUZEL_XCODE_PROJECT_HPP

#include <fstream>
#include "OuzelProject.hpp"
#include "PBXBuildFile.hpp"
#include "PBXContainerItemProxy.hpp"
#include "PBXFileReference.hpp"
#include "PBXFrameworksBuildPhase.hpp"
#include "PBXGroup.hpp"
#include "PBXNativeTarget.hpp"
#include "PBXProject.hpp"
#include "PBXReferenceProxy.hpp"
#include "PBXResourcesBuildPhase.hpp"
#include "PBXShellScriptBuildPhase.hpp"
#include "PBXSourcesBuildPhase.hpp"
#include "PBXTargetDependency.hpp"
#include "XCBuildConfiguration.hpp"
#include "XCConfigurationList.hpp"
#include "storage/FileSystem.hpp"
#include "utils/Plist.hpp"

namespace ouzel
{
    namespace xcode
    {
        class Project final
        {
        public:
            Project(const OuzelProject& p):
                project(p)
            {
            }

            void generate()
            {
                const storage::Path projectFilename = project.getPath().getFilename();
                const storage::Path projectDirectory = project.getPath().getDirectory();

                auto xcodeProjectDirectory = projectDirectory / storage::Path{project.getName() + ".xcodeproj"};
                auto xcodeProjectDirectoryType = xcodeProjectDirectory.getType();

                if (xcodeProjectDirectoryType == storage::Path::Type::NotFound)
                    storage::FileSystem::createDirectory(xcodeProjectDirectory);
                else if (xcodeProjectDirectoryType != storage::Path::Type::Directory)
                {
                    storage::FileSystem::deleteFile(xcodeProjectDirectory);
                    storage::FileSystem::createDirectory(xcodeProjectDirectory);
                }

                auto pbxProjectFile = xcodeProjectDirectory / storage::Path{"project.pbxproj"};

                constexpr auto libouzelIosId = Id{0x30, 0x3B, 0x75, 0x33, 0x1C, 0x2A, 0x3C, 0x58, 0x00, 0xFE, 0xDE, 0x92};
                constexpr auto libouzelMacOsId = Id{0x30, 0xA3, 0x96, 0x29, 0x24, 0x37, 0x73, 0xB5, 0x00, 0xD8, 0xE2, 0x8E};
                constexpr auto libouzelTvosId = Id{0x30, 0xA3, 0x96, 0x29, 0x24, 0x37, 0x73, 0xB5, 0x00, 0xD8, 0xE2, 0x8E};
                constexpr auto ouzelId = Id{0x30, 0xA3, 0x96, 0x29, 0x24, 0x37, 0x73, 0xB5, 0x00, 0xD8, 0xE2, 0x8E};

                const auto ouzelProjectPath = project.getOuzelPath() / "build" / "ouzel.xcodeproj";
                const auto& ouzelProjectFileRef = create<PBXFileReference>("ouzel.xcodeproj", ouzelProjectPath,
                                                                           PBXFileType::WrapperPBProject,
                                                                           PBXSourceTree::Group);
                const auto& libouzelIosProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                             PBXContainerItemProxy::Reference,
                                                                             libouzelIosId, "libouzel_ios");

                const auto& libouzelIosReferenceProxy = create<PBXReferenceProxy>("", "libouzel_ios.a",
                                                                                PBXFileType::ArchiveAr,
                                                                                PBXSourceTree::BuildProductsDir,
                                                                                libouzelIosProxy);

                const auto& libouzelMacOsProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                               PBXContainerItemProxy::Reference,
                                                                               libouzelMacOsId, "libouzel_macos");

                const auto& libouzelMacOsReferenceProxy = create<PBXReferenceProxy>("", "libouzel_macos.a",
                                                                                PBXFileType::ArchiveAr,
                                                                                PBXSourceTree::BuildProductsDir,
                                                                                libouzelMacOsProxy);

                const auto& libouzelTvosProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                              PBXContainerItemProxy::Reference,
                                                                              libouzelTvosId, "libouzel_tvos");

                const auto& libouzelTvosReferenceProxy = create<PBXReferenceProxy>("", "libouzel_tvos.a",
                                                                                PBXFileType::ArchiveAr,
                                                                                PBXSourceTree::BuildProductsDir,
                                                                                libouzelTvosProxy);

                const auto& ouzelProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                       PBXContainerItemProxy::Reference,
                                                                       ouzelId, "ouzel");

                const auto& ouzelNativeTargetProxy = create<PBXContainerItemProxy>(ouzelProjectFileRef,
                                                                                PBXContainerItemProxy::NativeTarget,
                                                                                ouzelId, "ouzel");

                const auto& ouzelReferenceProxy = create<PBXReferenceProxy>("", "ouzel",
                                                                            PBXFileType::CompiledMachOExecutable,
                                                                            PBXSourceTree::BuildProductsDir,
                                                                            ouzelProxy);

                const auto& ouzelPoductRefGroup = create<PBXGroup>("Products", storage::Path{},
                                                                   std::vector<PBXFileElementRef>{
                                                                       libouzelIosReferenceProxy,
                                                                       libouzelMacOsReferenceProxy,
                                                                       libouzelTvosReferenceProxy,
                                                                       ouzelReferenceProxy},
                                                                   PBXSourceTree::Group);

                const auto& productFile = create<PBXFileReference>("", storage::Path{project.getName() + ".app"},
                                                                   PBXFileType::WrapperApplication,
                                                                   PBXSourceTree::BuildProductsDir);

                const auto& resourcesGroup = create<PBXGroup>("", storage::Path{"Resources"},
                                                              std::vector<PBXFileElementRef>{},
                                                              PBXSourceTree::Group);

                const auto& productRefGroup = create<PBXGroup>("Products", storage::Path{},
                                                               std::vector<PBXFileElementRef>{productFile},
                                                               PBXSourceTree::Group);

                std::vector<PBXBuildFileRef> buildFiles;
                std::vector<PBXFileElementRef> sourceFiles;

                for (const auto& sourceFile : project.getSourceFiles())
                {
                    const auto extension = sourceFile.getExtension();
                    // TODO: support more file formats
                    const auto fileType = extension == "plist" ? PBXFileType::TextPlistXml :
                        extension == "c" ? PBXFileType::SourcecodeC :
                        extension == "h" ? PBXFileType::SourcecodeCH :
                        extension == "cpp" ? PBXFileType::SourcecodeCppCpp :
                        extension == "hpp" ? PBXFileType::SourcecodeCppH :
                        throw std::runtime_error("Unsupported file type");

                    const auto& fileReference = create<PBXFileReference>("", sourceFile, fileType, PBXSourceTree::Group);
                    sourceFiles.push_back(fileReference);

                    const auto& buildFile = create<PBXBuildFile>(fileReference);
                    buildFiles.push_back(buildFile);
                }

                const auto& sourceGroup = create<PBXGroup>("src", storage::Path{"src"},
                                                           sourceFiles, PBXSourceTree::Group);

                const auto headerSearchPath = std::string(project.getOuzelPath() / "engine");
                const auto& debugConfiguration = create<XCBuildConfiguration>("Debug",
                                                                              std::map<std::string, std::string>{
                                                                                {"CLANG_CXX_LANGUAGE_STANDARD", "c++14"},
                                                                                {"CLANG_CXX_LIBRARY", "libc++"},
                                                                                {"ENABLE_TESTABILITY", "YES"},
                                                                                {"GCC_OPTIMIZATION_LEVEL", "0"},
                                                                                {"GCC_PREPROCESSOR_DEFINITIONS", "DEBUG=1"},
                                                                                {"HEADER_SEARCH_PATHS", headerSearchPath},
                                                                                {"ONLY_ACTIVE_ARCH", "YES"}});

                const auto& releaseConfiguration = create<XCBuildConfiguration>("Release",
                                                                                std::map<std::string, std::string>{
                                                                                {"CLANG_CXX_LANGUAGE_STANDARD", "c++14"},
                                                                                {"CLANG_CXX_LIBRARY", "libc++"},
                                                                                {"HEADER_SEARCH_PATHS", headerSearchPath}});

                const auto& projectConfigurationList = create<XCConfigurationList>(std::vector<XCBuildConfigurationRef>{
                                                                                debugConfiguration,
                                                                                releaseConfiguration},
                                                                                releaseConfiguration.getName());

                std::vector<PBXTargetRef> targets;
                std::vector<PBXFileElementRef> frameworkFiles;

                for (const auto platform : project.getPlatforms())
                {
                    // TODO: do it for all platforms
                    if (platform == Platform::MacOs ||
                        platform == Platform::Ios ||
                        platform == Platform::Tvos)
                    {
                        std::map<std::string, std::string> buildSettings = {{"PRODUCT_NAME", project.getName()}};
                        std::vector<const char*> frameworks;

                        switch (platform)
                        {
                            case Platform::MacOs:
                                buildSettings["SDKROOT"] = "macosx";
                                buildSettings["MACOSX_DEPLOYMENT_TARGET"] = "10.8";
                                frameworks = {"AudioToolbox.framework",
                                    "AudioUnit.framework",
                                    "Cocoa.framework",
                                    "CoreAudio.framework",
                                    "CoreVideo.framework",
                                    "GameController.framework",
                                    "IOKit.framework",
                                    "Metal.framework",
                                    "OpenAL.framework",
                                    "OpenGL.framework",
                                    "QuartzCore.framework"};
                                break;

                            case Platform::Ios:
                                buildSettings["SDKROOT"] = "iphoneos";
                                buildSettings["IPHONEOS_DEPLOYMENT_TARGET"] = "8.0";
                                frameworks = {"AudioToolbox.framework",
                                    "AVFoundation.framework",
                                    "Foundation.framework",
                                    "GameController.framework",
                                    "Metal.framework",
                                    "OpenAL.framework",
                                    "OpenGLES.framework",
                                    "QuartzCore.framework",
                                    "UIKit.framework"
                                };
                                break;

                            case Platform::Tvos:
                                buildSettings["SDKROOT"] = "appletvos";
                                buildSettings["TVOS_DEPLOYMENT_TARGET"] = "9.0";
                                frameworks = {"AudioToolbox.framework",
                                    "AVFoundation.framework",
                                    "Foundation.framework",
                                    "GameController.framework",
                                    "Metal.framework",
                                    "OpenAL.framework",
                                    "OpenGLES.framework",
                                    "QuartzCore.framework",
                                    "UIKit.framework"
                                };
                                break;

                            default:
                                throw std::runtime_error("Unsupported platform");
                        }

                        const auto& targetDebugConfiguration = create<XCBuildConfiguration>("Debug", buildSettings);

                        const auto& targetReleaseConfiguration = create<XCBuildConfiguration>("Release", buildSettings);

                        const auto& targetConfigurationList = create<XCConfigurationList>(std::vector<XCBuildConfigurationRef>{
                                                                                targetDebugConfiguration,
                                                                                targetReleaseConfiguration},
                                                                                targetReleaseConfiguration.getName());

                        const auto& sourcesBuildPhase = create<PBXSourcesBuildPhase>(buildFiles);

                        std::vector<PBXFileReferenceRef> frameworkFileReferences;
                        std::vector<PBXBuildFileRef> frameworkBuildFiles;

                        const auto frameworksPath = storage::Path{"System/Library/Frameworks"};
                        for (const auto& framework : frameworks)
                        {
                            const auto& frameworkFileReference = create<PBXFileReference>(framework,
                                                                                frameworksPath / framework,
                                                                                PBXFileType::WrapperFramework,
                                                                                PBXSourceTree::SdkRoot);

                            frameworkFileReferences.push_back(frameworkFileReference);
                            frameworkFiles.push_back(frameworkFileReference);
                        }

                        switch (platform)
                        {
                            case Platform::MacOs:
                            {
                                const auto& libouzelMacOsBuildFile = create<PBXBuildFile>(libouzelMacOsReferenceProxy);
                                frameworkBuildFiles.push_back(libouzelMacOsBuildFile);
                                break;
                            }

                            case Platform::Ios:
                            {
                                const auto& libouzelIosBuildFile = create<PBXBuildFile>(libouzelIosReferenceProxy);
                                frameworkBuildFiles.push_back(libouzelIosBuildFile);
                                break;
                            }

                            case Platform::Tvos:
                            {
                                const auto& libouzelTvosBuildFile = create<PBXBuildFile>(libouzelTvosReferenceProxy);
                                frameworkBuildFiles.push_back(libouzelTvosBuildFile);
                                break;
                            }

                            default:
                                throw std::runtime_error("Unsupported platform");
                        }

                        for (const PBXFileReferenceRef& frameworkFileReference : frameworkFileReferences)
                        {
                            const auto& frameworkBuildFile = create<PBXBuildFile>(frameworkFileReference);
                            frameworkBuildFiles.push_back(frameworkBuildFile);
                        }

                        const auto& frameworksBuildPhase = create<PBXFrameworksBuildPhase>(frameworkBuildFiles);

                        // TODO: implement asset build shell script
                        const auto& assetsBuildPhase = create<PBXShellScriptBuildPhase>("$BUILT_PRODUCTS_DIR/ouzel --export-assets $PROJECT_DIR/" + std::string(projectFilename));

                        // TODO: implement resource copy
                        const auto& resourcesBuildPhase = create<PBXResourcesBuildPhase>(std::vector<PBXBuildFileRef>{});

                        const auto& ouzelDependency = create<PBXTargetDependency>("ouzel", ouzelNativeTargetProxy);

                        const auto targetName = project.getName() + (
                            (platform == Platform::MacOs) ? " macOS" :
                            (platform == Platform::Ios) ? " iOS" :
                            (platform == Platform::Tvos) ? " tvOS" : "");

                        const auto& nativeTarget = create<PBXNativeTarget>(targetName,
                                                                           targetConfigurationList,
                                                                           std::vector<PBXBuildPhaseRef>{sourcesBuildPhase, frameworksBuildPhase, assetsBuildPhase, resourcesBuildPhase},
                                                                           std::vector<PBXTargetDependencyRef>{ouzelDependency},
                                                                           productFile);
                        targets.push_back(nativeTarget);
                    }
                }

                const auto& frameworksGroup = create<PBXGroup>("Frameworks", storage::Path{},
                                                               frameworkFiles,
                                                               PBXSourceTree::Group);

                const auto& mainGroup = create<PBXGroup>("", storage::Path{},
                                                         std::vector<PBXFileElementRef>{
                                                             frameworksGroup,
                                                             ouzelProjectFileRef,
                                                             productRefGroup,
                                                             resourcesGroup,
                                                             sourceGroup},
                                                         PBXSourceTree::Group);

                const auto& pbxProject = create<PBXProject>(project.getOrganization(),
                                                            projectConfigurationList,
                                                            mainGroup,
                                                            productRefGroup,
                                                            std::map<std::string, PBXObjectRef>{
                                                                {"ProductGroup", ouzelPoductRefGroup},
                                                                {"ProjectRef", ouzelProjectFileRef}},
                                                            targets);

                rootObject = &pbxProject;

                std::ofstream file(pbxProjectFile, std::ios::trunc);
                file << plist::encode(encode());
            }

        private:
            template <class T, class ...Args>
            T& create(Args&& ...args)
            {
                std::unique_ptr<T> object = std::make_unique<T>(std::forward<Args>(args)...);
                T& result = *object;
                objects.push_back(std::move(object));
                return result;
            }

            plist::Value encode() const {
                plist::Value result = plist::Value::Dictionary{
                    {"archiveVersion", 1},
                    {"classes", plist::Value::Dictionary{}},
                    {"objectVersion", 50},
                    {"objects", plist::Value::Dictionary{}},
                    {"rootObject", rootObject ? toString(rootObject->getId()): ""}
                };

                for (const auto& object : objects)
                    result["objects"][toString(object->getId())] = object->encode();

                return result;
            }

            const OuzelProject& project;
            std::vector<std::unique_ptr<PBXObject>> objects;
            const PBXObject* rootObject;
        };
    }
}

#endif // OUZEL_XCODE_PROJECT_HPP
<|endoftext|>