<commit_before>//
//  File:	MapGenerator.cpp
//  Class	MapGenerator
//  Author	Jonatan Rapp & Alexander Hederstaf
//			All code is my own except where credited to others.
//
//  Copyright © 2012 Catch22. All Rights Reserved.
//
//  Date: Oct 9, 2012
//
//

#include "MapGenerator.hpp"
#include <algorithm>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include "../../Helper/Logger.hpp"

// All possible GeneratedBlocks
const GeneratedBlock Ip2 = GeneratedBlock(2, INCLINE, 4);
const GeneratedBlock Hp2 = GeneratedBlock(2, HORIZONTAL, 4);

const GeneratedBlock Ip1 = GeneratedBlock(1, INCLINE, 4);
const GeneratedBlock Hp1 = GeneratedBlock(1, HORIZONTAL, 4);

const GeneratedBlock I0 = GeneratedBlock(0, INCLINE, 64);
const GeneratedBlock H0 = GeneratedBlock(0, HORIZONTAL, 244);
const GeneratedBlock D0 = GeneratedBlock(0, DECLINE, 64);

const GeneratedBlock Hn1 = GeneratedBlock(-1, HORIZONTAL, 4);
const GeneratedBlock Dn1 = GeneratedBlock(-1, DECLINE, 4);

const GeneratedBlock Hn2 = GeneratedBlock(-2, HORIZONTAL, 4);
// End of GeneratedBlocks

MapGenerator::~MapGenerator()
{

}

MapGenerator::MapGenerator()
{
	recentlyUsedBuffer.resize(BUFFER_SIZE, H0);
	bufferElementCounter.resize(11, 0);
	bufferElementCounter[4] = 20; //20x Horizontal dy = 0 blocks

	all.insert(Ip2);
	all.insert(Hp2);
	all.insert(Ip1);
	all.insert(Hp1);
	all.insert(I0);
	all.insert(H0);
	all.insert(D0);
	all.insert(Hn1);
	all.insert(Dn1);
	all.insert(Hn2);

	zeroIncline.insert(I0);

	zeroDecline.insert(D0);

	plusTwo.insert(Ip2);
	plusTwo.insert(Hp2);

	plusOne.insert(Ip1);
	plusOne.insert(Hp1);

	allZeroes.insert(I0);
	allZeroes.insert(H0);
	allZeroes.insert(D0);

	allDeltaY.insert(Ip2);
	allDeltaY.insert(Hp2);
	allDeltaY.insert(Ip1);
	allDeltaY.insert(Hp1);
	allDeltaY.insert(Hn1);
	allDeltaY.insert(Dn1);
	allDeltaY.insert(Hn2);
}

set<GeneratedBlock> MapGenerator::getPossibleSet(GeneratedBlock* previousBlock)
{
	set<GeneratedBlock> possibleSet = all;

	if (!previousBlock)  {
		return possibleSet;
	}

	if (previousBlock->type == DECLINE) {
		// remove zeroIncline
		set<GeneratedBlock> tmp;
		set_difference(possibleSet.begin(), possibleSet.end(), zeroIncline.begin(), zeroIncline.end(), inserter(tmp, tmp.end()));
		possibleSet = tmp;
	} else if (previousBlock->type == INCLINE) {
		// remove zeroDecline
		set<GeneratedBlock> tmp;
		set_difference(possibleSet.begin(), possibleSet.end(), zeroDecline.begin(), zeroDecline.end(), inserter(tmp, tmp.end()));
		possibleSet = tmp;
	}

	if (previousBlock->dy != 0) {
		// remove allDeltaY
		set<GeneratedBlock> tmp;
		set_difference(possibleSet.begin(), possibleSet.end(), allDeltaY.begin(), allDeltaY.end(), inserter(tmp, tmp.end()));
		possibleSet = tmp;
	} else {
		if (previousBlock->type != HORIZONTAL) {
			// remove plusTwo
			set<GeneratedBlock> tmp;
			set_difference(possibleSet.begin(), possibleSet.end(), plusTwo.begin(), plusTwo.end(), inserter(tmp, tmp.end()));
			possibleSet = tmp;
		}
		if (previousBlock->type == DECLINE) {
			// remove plusOne
			set<GeneratedBlock> tmp;
			set_difference(possibleSet.begin(), possibleSet.end(), plusOne.begin(), plusOne.end(), inserter(tmp, tmp.end()));
			possibleSet = tmp;
		}
	}
	if (previousBlock->dy < 0) {
		// remove zeroIncline
		set<GeneratedBlock> tmp;
		set_difference(possibleSet.begin(), possibleSet.end(), zeroIncline.begin(), zeroIncline.end(), inserter(tmp, tmp.end()));
		possibleSet = tmp;
	}
	return possibleSet;
}

set<GeneratedBlock> MapGenerator::getAllowedSet(set<GeneratedBlock> possibleSet, Vector2d* startVector)
{
	set<GeneratedBlock> allowedSet;
	set<GeneratedBlock>::iterator it;
	for (it = possibleSet.begin(); it != possibleSet.end(); it++) {
		int dy = (*it).dy;
		int t = (*it).type;
		t = dy + (-1) * (t - 1); // t is now an int representing deltaY added by slope type.
		if (((startVector->m_y + t) <= HEIGHT_MAX && (startVector->m_y + t) >= HEIGHT_MIN)
				&& (startVector->m_y <= HEIGHT_MAX && startVector->m_y >= HEIGHT_MIN)) {
			// Only keep GeneratedBlocks that stay within the allowed height margin
			allowedSet.insert(*it);
		}
	}
	return allowedSet;
}

void MapGenerator::addToBuffer(GeneratedBlock usedBlock)
{
	vector<GeneratedBlock>::iterator it = recentlyUsedBuffer.begin();
	recentlyUsedBuffer.erase(recentlyUsedBuffer.begin());
	GeneratedBlock removed = *it;
	int position = distance(all.begin(), all.find(removed));
	bufferElementCounter[position]--;

	recentlyUsedBuffer.push_back(usedBlock);
	position = distance(all.begin(), all.find(usedBlock));
	bufferElementCounter[position]++;
}

void MapGenerator::modifyChances(set<GeneratedBlock>& allowedSet, Vector2d* startVector)
{
	set<GeneratedBlock>::iterator it;
	for (it = allowedSet.begin(); it != allowedSet.end(); ++it) {
		GeneratedBlock block = *it;
		block.chance -=  2 * bufferElementCounter[distance(all.begin(), all.find(block))];
		if ((block.dy + ((-1) * (block.type - 1)) < 0 && startVector->m_y < 4)
				|| (block.dy + ((-1) * (block.type - 1)) > 0 && startVector->m_y > 4)) {
			int z = abs((int) (startVector->m_y - 4));
			if (z == 1) {
				block.chance *= 3;
			}
			if (z == 2) {
				block.chance *= 2;
			}
			block.chance /= 4;
		}
		allowedSet.erase(*it);
		allowedSet.insert(block);
	}
}

GeneratedBlock MapGenerator::selectBlock(set<GeneratedBlock> allowedSet)
{
	set<GeneratedBlock>::iterator it;
	int totalChance = 1;
	for (it = allowedSet.begin(); it != allowedSet.end(); ++it) {
		totalChance += (*it).chance;
	}
	int roll = (rand() % totalChance) + 1;
	for (it = allowedSet.begin(); it != allowedSet.end(); ++it) {
		if (roll <= (*it).chance) {
			break;
		} else {
			roll-=(*it).chance;
		}
	}
	return *it;
}

Platform* MapGenerator::generatePlatform(Vector2d* startVector)
{
	// 1. get possible set
	// 2. get allowed set
	// 3. modify chance (using buffer & deltaY)
	// 4. select GeneratedBlock
	// 5. add platformblock to platform
	// 6. add used GeneratedBlock to buffer

	Platform* platform = new Platform();

	set<GeneratedBlock> possibleSet = allZeroes;
	set<GeneratedBlock> allowedSet = getAllowedSet(possibleSet, startVector);
	modifyChances(allowedSet, startVector);
	GeneratedBlock selectedBlock = selectBlock(allowedSet);

	PlatformBlock* block = new PlatformBlock(selectedBlock.type, startVector);
	platform->addPlatformBlock(block);

	Vector2d* newStartVector = block->getEndVector();

	addToBuffer(selectedBlock);

	int length = (PLATFORM_LENGTH_MIN - 1) + (rand() % (2 + PLATFORM_LENGTH_MAX - PLATFORM_LENGTH_MIN));

	for (int i = 0; i < length; i++) {

		possibleSet = getPossibleSet(&selectedBlock);
		allowedSet = getAllowedSet(possibleSet, newStartVector);
		modifyChances(allowedSet, newStartVector);
		selectedBlock = selectBlock(allowedSet);

		*newStartVector+=Vector2d(0.f, selectedBlock.dy);

		block = new PlatformBlock(selectedBlock.type, newStartVector);
		platform->addPlatformBlock(block);

		newStartVector = block->getEndVector();

		addToBuffer(selectedBlock);
	}
	return platform;
}

Platform* MapGenerator::generateFlatPlatform(Vector2d* startVector, int length)
{
	Platform* platform = new Platform();
	for (int i = 0; i < length; i++) {
		PlatformBlock* block = new PlatformBlock(HORIZONTAL, startVector);
		platform->addPlatformBlock(block);
		startVector = block->getEndVector();
	}
	return platform;
}

bool MapGenerator::testFunc()
{/*
	printf("Size all = %i\n", (int)all.size());

	set<GeneratedBlock> testSet = getPossibleSet(0);
	printf("Size (input: 0) = %i\n", (int)testSet.size());

	GeneratedBlock lastBlock = H0;

	testSet = getPossibleSet(&lastBlock);
	printf("Size (input: 0,H) = %i\n", (int)testSet.size());

	testSet = getAllowedSet(testSet, new Vector2d(0, 5));

	printf("Size (input: 0,H) V(0, 3) = %i\n", (int)testSet.size());

	int nbr = testSet.size();
	set<GeneratedBlock> printSet = testSet;
	printf("allowedSet --------\n");
	for (int i = 0; i < nbr; i++) {
		set<GeneratedBlock>::iterator it = printSet.begin();
		int y = (&*it)->dy;
		int t = (&*it)->type;
		int c = (&*it)->chance;
		printf("Element #%i: dy: %i, type %i, chance %i\n", i, y, t, c);
		printSet.erase(it);
	}

	modifyChances(testSet, new Vector2d(0, 5));

	printf("After modifyChances()--------\n");
	printSet = testSet;
	for (int i = 0; i < nbr; i++) {
		set<GeneratedBlock>::iterator it = printSet.begin();
		int y = (&*it)->dy;
		int t = (&*it)->type;
		int c = (&*it)->chance;
		printf("Element #%i: dy: %i, type %i, chance %i\n", i, y, t, c);
		printSet.erase(it);
	}
*/
	GeneratedBlock lastBlock = H0;
	Vector2d* startVector = new Vector2d(2,6);
	int qwe = 6 + (rand() % 7);
	for (int v = 0; v < qwe; v++) {
		set<GeneratedBlock> possibleSet = getPossibleSet(&lastBlock);
		set<GeneratedBlock> allowedSet = getAllowedSet(possibleSet, startVector);
		modifyChances(allowedSet, startVector);
		GeneratedBlock selBlock = selectBlock(allowedSet);
		Vector2d* endVector = new Vector2d(2 + startVector->m_x, selBlock.dy + ((-1) * (selBlock.type - 1)) + startVector->m_y);
		printf("----------------\n");
		printf("GenBlock dY   = %i", selBlock.dy);
		printf("GenBlock type = %i", selBlock.type);
		printf("StartVector = (%i, %i)\n", (int)startVector->m_x, (int)startVector->m_y);
		printf("EndVector   = (%i, %i)\n", (int)endVector->m_x, (int)endVector->m_y);
		lastBlock = selBlock;
		startVector = endVector;
	}
/*
	GeneratedBlock selBlock = selectBlock(testSet);
	printf("Selected Block:\n");
	printf("dY = %i\n", selBlock.dy);
	printf("Type = %i\n", selBlock.type);
	printf("Chance = %i\n", selBlock.chance);
*/
	return true;
}
<commit_msg>Fixed random selectBlock function  - baserandom number set to 0, was set to 1 as result of sonar test.  - this causued division by zero but is now avoided with a check.<commit_after>//
//  File:	MapGenerator.cpp
//  Class	MapGenerator
//  Author	Jonatan Rapp & Alexander Hederstaf
//			All code is my own except where credited to others.
//
//  Copyright © 2012 Catch22. All Rights Reserved.
//
//  Date: Oct 9, 2012
//
//

#include "MapGenerator.hpp"
#include <algorithm>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include "../../Helper/Logger.hpp"

// All possible GeneratedBlocks
const GeneratedBlock Ip2 = GeneratedBlock(2, INCLINE, 4);
const GeneratedBlock Hp2 = GeneratedBlock(2, HORIZONTAL, 4);

const GeneratedBlock Ip1 = GeneratedBlock(1, INCLINE, 4);
const GeneratedBlock Hp1 = GeneratedBlock(1, HORIZONTAL, 4);

const GeneratedBlock I0 = GeneratedBlock(0, INCLINE, 64);
const GeneratedBlock H0 = GeneratedBlock(0, HORIZONTAL, 244);
const GeneratedBlock D0 = GeneratedBlock(0, DECLINE, 64);

const GeneratedBlock Hn1 = GeneratedBlock(-1, HORIZONTAL, 4);
const GeneratedBlock Dn1 = GeneratedBlock(-1, DECLINE, 4);

const GeneratedBlock Hn2 = GeneratedBlock(-2, HORIZONTAL, 4);
// End of GeneratedBlocks

MapGenerator::~MapGenerator()
{

}

MapGenerator::MapGenerator()
{
	recentlyUsedBuffer.resize(BUFFER_SIZE, H0);
	bufferElementCounter.resize(11, 0);
	bufferElementCounter[4] = 20; //20x Horizontal dy = 0 blocks

	all.insert(Ip2);
	all.insert(Hp2);
	all.insert(Ip1);
	all.insert(Hp1);
	all.insert(I0);
	all.insert(H0);
	all.insert(D0);
	all.insert(Hn1);
	all.insert(Dn1);
	all.insert(Hn2);

	zeroIncline.insert(I0);

	zeroDecline.insert(D0);

	plusTwo.insert(Ip2);
	plusTwo.insert(Hp2);

	plusOne.insert(Ip1);
	plusOne.insert(Hp1);

	allZeroes.insert(I0);
	allZeroes.insert(H0);
	allZeroes.insert(D0);

	allDeltaY.insert(Ip2);
	allDeltaY.insert(Hp2);
	allDeltaY.insert(Ip1);
	allDeltaY.insert(Hp1);
	allDeltaY.insert(Hn1);
	allDeltaY.insert(Dn1);
	allDeltaY.insert(Hn2);
}

set<GeneratedBlock> MapGenerator::getPossibleSet(GeneratedBlock* previousBlock)
{
	set<GeneratedBlock> possibleSet = all;

	if (!previousBlock)  {
		return possibleSet;
	}

	if (previousBlock->type == DECLINE) {
		// remove zeroIncline
		set<GeneratedBlock> tmp;
		set_difference(possibleSet.begin(), possibleSet.end(), zeroIncline.begin(), zeroIncline.end(), inserter(tmp, tmp.end()));
		possibleSet = tmp;
	} else if (previousBlock->type == INCLINE) {
		// remove zeroDecline
		set<GeneratedBlock> tmp;
		set_difference(possibleSet.begin(), possibleSet.end(), zeroDecline.begin(), zeroDecline.end(), inserter(tmp, tmp.end()));
		possibleSet = tmp;
	}

	if (previousBlock->dy != 0) {
		// remove allDeltaY
		set<GeneratedBlock> tmp;
		set_difference(possibleSet.begin(), possibleSet.end(), allDeltaY.begin(), allDeltaY.end(), inserter(tmp, tmp.end()));
		possibleSet = tmp;
	} else {
		if (previousBlock->type != HORIZONTAL) {
			// remove plusTwo
			set<GeneratedBlock> tmp;
			set_difference(possibleSet.begin(), possibleSet.end(), plusTwo.begin(), plusTwo.end(), inserter(tmp, tmp.end()));
			possibleSet = tmp;
		}
		if (previousBlock->type == DECLINE) {
			// remove plusOne
			set<GeneratedBlock> tmp;
			set_difference(possibleSet.begin(), possibleSet.end(), plusOne.begin(), plusOne.end(), inserter(tmp, tmp.end()));
			possibleSet = tmp;
		}
	}
	if (previousBlock->dy < 0) {
		// remove zeroIncline
		set<GeneratedBlock> tmp;
		set_difference(possibleSet.begin(), possibleSet.end(), zeroIncline.begin(), zeroIncline.end(), inserter(tmp, tmp.end()));
		possibleSet = tmp;
	}
	return possibleSet;
}

set<GeneratedBlock> MapGenerator::getAllowedSet(set<GeneratedBlock> possibleSet, Vector2d* startVector)
{
	set<GeneratedBlock> allowedSet;
	set<GeneratedBlock>::iterator it;
	for (it = possibleSet.begin(); it != possibleSet.end(); it++) {
		int dy = (*it).dy;
		int t = (*it).type;
		t = dy + (-1) * (t - 1); // t is now an int representing deltaY added by slope type.
		if (((startVector->m_y + t) <= HEIGHT_MAX && (startVector->m_y + t) >= HEIGHT_MIN)
				&& (startVector->m_y <= HEIGHT_MAX && startVector->m_y >= HEIGHT_MIN)) {
			// Only keep GeneratedBlocks that stay within the allowed height margin
			allowedSet.insert(*it);
		}
	}
	return allowedSet;
}

void MapGenerator::addToBuffer(GeneratedBlock usedBlock)
{
	vector<GeneratedBlock>::iterator it = recentlyUsedBuffer.begin();
	recentlyUsedBuffer.erase(recentlyUsedBuffer.begin());
	GeneratedBlock removed = *it;
	int position = distance(all.begin(), all.find(removed));
	bufferElementCounter[position]--;

	recentlyUsedBuffer.push_back(usedBlock);
	position = distance(all.begin(), all.find(usedBlock));
	bufferElementCounter[position]++;
}

void MapGenerator::modifyChances(set<GeneratedBlock>& allowedSet, Vector2d* startVector)
{
	set<GeneratedBlock>::iterator it;
	for (it = allowedSet.begin(); it != allowedSet.end(); ++it) {
		GeneratedBlock block = *it;
		block.chance -=  2 * bufferElementCounter[distance(all.begin(), all.find(block))];
		if ((block.dy + ((-1) * (block.type - 1)) < 0 && startVector->m_y < 4)
				|| (block.dy + ((-1) * (block.type - 1)) > 0 && startVector->m_y > 4)) {
			int z = abs((int) (startVector->m_y - 4));
			if (z == 1) {
				block.chance *= 3;
			}
			if (z == 2) {
				block.chance *= 2;
			}
			block.chance /= 4;
		}
		allowedSet.erase(*it);
		allowedSet.insert(block);
	}
}

GeneratedBlock* MapGenerator::selectBlock(set<GeneratedBlock> allowedSet)
{
	set<GeneratedBlock>::iterator it;
	int totalChance = 0;
	for (it = allowedSet.begin(); it != allowedSet.end(); ++it) {
		totalChance += (*it).chance;
	}

	if (totalChance == 0) {
		// set is empty or sum of chance in all blocks are zero
		return 0;
	}

	int roll = (rand() % totalChance) + 1;
	for (it = allowedSet.begin(); it != allowedSet.end(); ++it) {
		if (roll <= (*it).chance) {
			break;
		} else {
			roll-=(*it).chance;
		}
	}
	return *it;
}

Platform* MapGenerator::generatePlatform(Vector2d* startVector)
{
	// 1. get possible set
	// 2. get allowed set
	// 3. modify chance (using buffer & deltaY)
	// 4. select GeneratedBlock
	// 5. add platformblock to platform
	// 6. add used GeneratedBlock to buffer

	Platform* platform = new Platform();

	set<GeneratedBlock> possibleSet = allZeroes;
	set<GeneratedBlock> allowedSet = getAllowedSet(possibleSet, startVector);
	modifyChances(allowedSet, startVector);
	GeneratedBlock selectedBlock = selectBlock(allowedSet);

	PlatformBlock* block = new PlatformBlock(selectedBlock.type, startVector);
	platform->addPlatformBlock(block);

	Vector2d* newStartVector = block->getEndVector();

	addToBuffer(selectedBlock);

	int length = (PLATFORM_LENGTH_MIN - 1) + (rand() % (2 + PLATFORM_LENGTH_MAX - PLATFORM_LENGTH_MIN));

	for (int i = 0; i < length; i++) {

		possibleSet = getPossibleSet(&selectedBlock);
		allowedSet = getAllowedSet(possibleSet, newStartVector);
		modifyChances(allowedSet, newStartVector);
		selectedBlock = selectBlock(allowedSet);

		*newStartVector+=Vector2d(0.f, selectedBlock.dy);

		block = new PlatformBlock(selectedBlock.type, newStartVector);
		platform->addPlatformBlock(block);

		newStartVector = block->getEndVector();

		addToBuffer(selectedBlock);
	}
	return platform;
}

Platform* MapGenerator::generateFlatPlatform(Vector2d* startVector, int length)
{
	Platform* platform = new Platform();
	for (int i = 0; i < length; i++) {
		PlatformBlock* block = new PlatformBlock(HORIZONTAL, startVector);
		platform->addPlatformBlock(block);
		startVector = block->getEndVector();
	}
	return platform;
}

bool MapGenerator::testFunc()
{/*
	printf("Size all = %i\n", (int)all.size());

	set<GeneratedBlock> testSet = getPossibleSet(0);
	printf("Size (input: 0) = %i\n", (int)testSet.size());

	GeneratedBlock lastBlock = H0;

	testSet = getPossibleSet(&lastBlock);
	printf("Size (input: 0,H) = %i\n", (int)testSet.size());

	testSet = getAllowedSet(testSet, new Vector2d(0, 5));

	printf("Size (input: 0,H) V(0, 3) = %i\n", (int)testSet.size());

	int nbr = testSet.size();
	set<GeneratedBlock> printSet = testSet;
	printf("allowedSet --------\n");
	for (int i = 0; i < nbr; i++) {
		set<GeneratedBlock>::iterator it = printSet.begin();
		int y = (&*it)->dy;
		int t = (&*it)->type;
		int c = (&*it)->chance;
		printf("Element #%i: dy: %i, type %i, chance %i\n", i, y, t, c);
		printSet.erase(it);
	}

	modifyChances(testSet, new Vector2d(0, 5));

	printf("After modifyChances()--------\n");
	printSet = testSet;
	for (int i = 0; i < nbr; i++) {
		set<GeneratedBlock>::iterator it = printSet.begin();
		int y = (&*it)->dy;
		int t = (&*it)->type;
		int c = (&*it)->chance;
		printf("Element #%i: dy: %i, type %i, chance %i\n", i, y, t, c);
		printSet.erase(it);
	}
*/
	GeneratedBlock lastBlock = H0;
	Vector2d* startVector = new Vector2d(2,6);
	int qwe = 6 + (rand() % 7);
	for (int v = 0; v < qwe; v++) {
		set<GeneratedBlock> possibleSet = getPossibleSet(&lastBlock);
		set<GeneratedBlock> allowedSet = getAllowedSet(possibleSet, startVector);
		modifyChances(allowedSet, startVector);
		GeneratedBlock selBlock = selectBlock(allowedSet);
		Vector2d* endVector = new Vector2d(2 + startVector->m_x, selBlock.dy + ((-1) * (selBlock.type - 1)) + startVector->m_y);
		printf("----------------\n");
		printf("GenBlock dY   = %i", selBlock.dy);
		printf("GenBlock type = %i", selBlock.type);
		printf("StartVector = (%i, %i)\n", (int)startVector->m_x, (int)startVector->m_y);
		printf("EndVector   = (%i, %i)\n", (int)endVector->m_x, (int)endVector->m_y);
		lastBlock = selBlock;
		startVector = endVector;
	}
/*
	GeneratedBlock selBlock = selectBlock(testSet);
	printf("Selected Block:\n");
	printf("dY = %i\n", selBlock.dy);
	printf("Type = %i\n", selBlock.type);
	printf("Chance = %i\n", selBlock.chance);
*/
	return true;
}
<|endoftext|>


<commit_before>#include "chainerx/cuda/cuda_device.h"

#include <cstddef>
#include <memory>

#include <cuda_runtime.h>

#include "chainerx/cuda/cuda_runtime.h"
#include "chainerx/cuda/cuda_set_device_scope.h"
#include "chainerx/cuda/memory_pool.h"
#include "chainerx/device.h"
#include "chainerx/error.h"
#include "chainerx/macro.h"
#include "chainerx/native/native_device.h"

namespace chainerx {
namespace cuda {

std::shared_ptr<void> CudaDevice::Allocate(size_t bytesize) {
    void* ptr = device_memory_pool_->Malloc(bytesize);
    return std::shared_ptr<void>{ptr, [weak_pool = std::weak_ptr<MemoryPool>{device_memory_pool_}](void* ptr) {
                                     if (std::shared_ptr<MemoryPool> pool = weak_pool.lock()) {
                                         pool->FreeNoExcept(ptr);
                                     }
                                 }};
}

std::shared_ptr<void> CudaDevice::AllocatePinnedMemory(size_t bytesize) {
    void* ptr = pinned_memory_pool_->Malloc(bytesize);
    return std::shared_ptr<void>{ptr, [weak_pool = std::weak_ptr<MemoryPool>{pinned_memory_pool_}](void* ptr) {
                                     if (std::shared_ptr<MemoryPool> pool = weak_pool.lock()) {
                                         pool->FreeNoExcept(ptr);
                                     }
                                 }};
}

void CudaDevice::MemoryCopyFromHostAsync(void* dst, const void* src, size_t bytesize) {
    std::shared_ptr<void> pinned_src_ptr = AllocatePinnedMemory(bytesize);

    CudaSetDeviceScope scope{index()};

    // cudaMemcpyAsync is slightly faster than cudaMemcpy, although both should be synchronous involving not page-locked regions.
    CheckCudaError(cudaMemcpyAsync(pinned_src_ptr.get(), src, bytesize, cudaMemcpyHostToHost));

    CheckCudaError(cudaMemcpyAsync(dst, pinned_src_ptr.get(), bytesize, cudaMemcpyHostToDevice));
}

std::shared_ptr<void> CudaDevice::MakeDataFromForeignPointer(const std::shared_ptr<void>& data) {
    if (data == nullptr) {
        return data;
    }

    // check memory validity
    void* ptr = data.get();
    cudaPointerAttributes attr{};
    cudaError_t status = cudaPointerGetAttributes(&attr, ptr);
    switch (status) {
        case cudaSuccess:
            if (attr.device != index()) {
                throw ChainerxError{"CUDA memory: ", ptr, " must reside on the device: ", index()};
            }
            break;
        case cudaErrorInvalidValue:
            throw ChainerxError{"Memory: ", ptr, " is not a CUDA memory"};
        default:
            Throw(status);
    }
    return data;
}

void CudaDevice::MemoryCopyFrom(void* dst, const void* src, size_t bytesize, Device& src_device) {
    CHAINERX_ASSERT(bytesize == 0 || IsPointerCudaMemory(dst));
    if (bytesize == 0) {
        return;
    }
    CudaSetDeviceScope scope{index()};
    if (&src_device == this || nullptr != dynamic_cast<CudaDevice*>(&src_device)) {
        // Copy between CUDA devices
        CheckCudaError(cudaMemcpyAsync(dst, src, bytesize, cudaMemcpyDeviceToDevice));
    } else {
        CHAINERX_ASSERT(
                nullptr != dynamic_cast<native::NativeDevice*>(&src_device) &&
                "CudaDevice only supports copy between cuda or native devices.");
        // Copy from native device
        MemoryCopyFromHostAsync(dst, src, bytesize);
    }
}

void CudaDevice::MemoryCopyTo(void* dst, const void* src, size_t bytesize, Device& dst_device) {
    CHAINERX_ASSERT(bytesize == 0 || src == nullptr || IsPointerCudaMemory(src));
    if (bytesize == 0) {
        return;
    }
    CudaSetDeviceScope scope{index()};
    if (&dst_device == this || nullptr != dynamic_cast<CudaDevice*>(&dst_device)) {
        // Copy between CUDA devices
        CheckCudaError(cudaMemcpyAsync(dst, src, bytesize, cudaMemcpyDeviceToDevice));
    } else {
        CHAINERX_ASSERT(
                nullptr != dynamic_cast<native::NativeDevice*>(&dst_device) &&
                "CudaDevice only supports copy between cuda or native devices.");
        // Copy to native device
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyDeviceToHost));
    }
}

std::shared_ptr<void> CudaDevice::TransferDataFrom(
        Device& src_device, const std::shared_ptr<void>& src_ptr, size_t offset, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = Allocate(bytesize);
    MemoryCopyFrom(dst_ptr.get(), &(static_cast<int8_t*>(src_ptr.get())[offset]), bytesize, src_device);
    return dst_ptr;
}

std::shared_ptr<void> CudaDevice::TransferDataTo(Device& dst_device, const std::shared_ptr<void>& src_ptr, size_t offset, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = dst_device.Allocate(bytesize);
    MemoryCopyTo(dst_ptr.get(), &(static_cast<int8_t*>(src_ptr.get())[offset]), bytesize, dst_device);
    return dst_ptr;
}

std::shared_ptr<void> CudaDevice::FromHostMemory(const std::shared_ptr<void>& src_ptr, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = Allocate(bytesize);
    MemoryCopyFromHostAsync(dst_ptr.get(), src_ptr.get(), bytesize);
    return dst_ptr;
}

}  // namespace cuda
}  // namespace chainerx
<commit_msg>Fix for readability<commit_after>#include "chainerx/cuda/cuda_device.h"

#include <cstddef>
#include <memory>

#include <cuda_runtime.h>

#include "chainerx/cuda/cuda_runtime.h"
#include "chainerx/cuda/cuda_set_device_scope.h"
#include "chainerx/cuda/memory_pool.h"
#include "chainerx/device.h"
#include "chainerx/error.h"
#include "chainerx/macro.h"
#include "chainerx/native/native_device.h"

namespace chainerx {
namespace cuda {

std::shared_ptr<void> CudaDevice::Allocate(size_t bytesize) {
    auto deleter = [weak_pool = std::weak_ptr<MemoryPool>{device_memory_pool_}](void* ptr) {
        if (std::shared_ptr<MemoryPool> pool = weak_pool.lock()) {
            pool->FreeNoExcept(ptr);
        }
    };
    return std::shared_ptr<void>{device_memory_pool_->Malloc(bytesize), std::move(deleter)};
}

std::shared_ptr<void> CudaDevice::AllocatePinnedMemory(size_t bytesize) {
    auto deleter = [weak_pool = std::weak_ptr<MemoryPool>{pinned_memory_pool_}](void* ptr) {
        if (std::shared_ptr<MemoryPool> pool = weak_pool.lock()) {
            pool->FreeNoExcept(ptr);
        }
    };
    return std::shared_ptr<void>{pinned_memory_pool_->Malloc(bytesize), std::move(deleter)};
}

void CudaDevice::MemoryCopyFromHostAsync(void* dst, const void* src, size_t bytesize) {
    std::shared_ptr<void> pinned_src_ptr = AllocatePinnedMemory(bytesize);

    CudaSetDeviceScope scope{index()};

    // cudaMemcpyAsync is slightly faster than cudaMemcpy, although both should be synchronous involving not page-locked regions.
    CheckCudaError(cudaMemcpyAsync(pinned_src_ptr.get(), src, bytesize, cudaMemcpyHostToHost));

    CheckCudaError(cudaMemcpyAsync(dst, pinned_src_ptr.get(), bytesize, cudaMemcpyHostToDevice));
}

std::shared_ptr<void> CudaDevice::MakeDataFromForeignPointer(const std::shared_ptr<void>& data) {
    if (data == nullptr) {
        return data;
    }

    // check memory validity
    void* ptr = data.get();
    cudaPointerAttributes attr{};
    cudaError_t status = cudaPointerGetAttributes(&attr, ptr);
    switch (status) {
        case cudaSuccess:
            if (attr.device != index()) {
                throw ChainerxError{"CUDA memory: ", ptr, " must reside on the device: ", index()};
            }
            break;
        case cudaErrorInvalidValue:
            throw ChainerxError{"Memory: ", ptr, " is not a CUDA memory"};
        default:
            Throw(status);
    }
    return data;
}

void CudaDevice::MemoryCopyFrom(void* dst, const void* src, size_t bytesize, Device& src_device) {
    CHAINERX_ASSERT(bytesize == 0 || IsPointerCudaMemory(dst));
    if (bytesize == 0) {
        return;
    }
    CudaSetDeviceScope scope{index()};
    if (&src_device == this || nullptr != dynamic_cast<CudaDevice*>(&src_device)) {
        // Copy between CUDA devices
        CheckCudaError(cudaMemcpyAsync(dst, src, bytesize, cudaMemcpyDeviceToDevice));
    } else {
        CHAINERX_ASSERT(
                nullptr != dynamic_cast<native::NativeDevice*>(&src_device) &&
                "CudaDevice only supports copy between cuda or native devices.");
        // Copy from native device
        MemoryCopyFromHostAsync(dst, src, bytesize);
    }
}

void CudaDevice::MemoryCopyTo(void* dst, const void* src, size_t bytesize, Device& dst_device) {
    CHAINERX_ASSERT(bytesize == 0 || src == nullptr || IsPointerCudaMemory(src));
    if (bytesize == 0) {
        return;
    }
    CudaSetDeviceScope scope{index()};
    if (&dst_device == this || nullptr != dynamic_cast<CudaDevice*>(&dst_device)) {
        // Copy between CUDA devices
        CheckCudaError(cudaMemcpyAsync(dst, src, bytesize, cudaMemcpyDeviceToDevice));
    } else {
        CHAINERX_ASSERT(
                nullptr != dynamic_cast<native::NativeDevice*>(&dst_device) &&
                "CudaDevice only supports copy between cuda or native devices.");
        // Copy to native device
        CheckCudaError(cudaMemcpy(dst, src, bytesize, cudaMemcpyDeviceToHost));
    }
}

std::shared_ptr<void> CudaDevice::TransferDataFrom(
        Device& src_device, const std::shared_ptr<void>& src_ptr, size_t offset, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = Allocate(bytesize);
    MemoryCopyFrom(dst_ptr.get(), &(static_cast<int8_t*>(src_ptr.get())[offset]), bytesize, src_device);
    return dst_ptr;
}

std::shared_ptr<void> CudaDevice::TransferDataTo(Device& dst_device, const std::shared_ptr<void>& src_ptr, size_t offset, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = dst_device.Allocate(bytesize);
    MemoryCopyTo(dst_ptr.get(), &(static_cast<int8_t*>(src_ptr.get())[offset]), bytesize, dst_device);
    return dst_ptr;
}

std::shared_ptr<void> CudaDevice::FromHostMemory(const std::shared_ptr<void>& src_ptr, size_t bytesize) {
    std::shared_ptr<void> dst_ptr = Allocate(bytesize);
    MemoryCopyFromHostAsync(dst_ptr.get(), src_ptr.get(), bytesize);
    return dst_ptr;
}

}  // namespace cuda
}  // namespace chainerx
<|endoftext|>


<commit_before>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/media/media_stream_audio_processor_options.h"

#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "content/common/media/media_stream_options.h"
#include "content/renderer/media/media_stream_constraints_util.h"
#include "content/renderer/media/media_stream_source.h"
#include "content/renderer/media/rtc_media_constraints.h"
#include "media/audio/audio_parameters.h"
#include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
#include "third_party/webrtc/modules/audio_processing/typing_detection.h"

namespace content {

const char MediaAudioConstraints::kEchoCancellation[] = "echoCancellation";
const char MediaAudioConstraints::kGoogEchoCancellation[] =
    "googEchoCancellation";
const char MediaAudioConstraints::kGoogExperimentalEchoCancellation[] =
    "googEchoCancellation2";
const char MediaAudioConstraints::kGoogAutoGainControl[] =
    "googAutoGainControl";
const char MediaAudioConstraints::kGoogExperimentalAutoGainControl[] =
    "googAutoGainControl2";
const char MediaAudioConstraints::kGoogNoiseSuppression[] =
    "googNoiseSuppression";
const char MediaAudioConstraints::kGoogExperimentalNoiseSuppression[] =
    "googNoiseSuppression2";
const char MediaAudioConstraints::kGoogHighpassFilter[] = "googHighpassFilter";
const char MediaAudioConstraints::kGoogTypingNoiseDetection[] =
    "googTypingNoiseDetection";
const char MediaAudioConstraints::kGoogAudioMirroring[] = "googAudioMirroring";

namespace {

// Constant constraint keys which enables default audio constraints on
// mediastreams with audio.
struct {
  const char* key;
  bool value;
} const kDefaultAudioConstraints[] = {
  { MediaAudioConstraints::kEchoCancellation, true },
  { MediaAudioConstraints::kGoogEchoCancellation, true },
#if defined(OS_ANDROID) || defined(OS_IOS)
  { MediaAudioConstraints::kGoogExperimentalEchoCancellation, false },
#else
  // Enable the extended filter mode AEC on all non-mobile platforms.
  { MediaAudioConstraints::kGoogExperimentalEchoCancellation, true },
#endif
  { MediaAudioConstraints::kGoogAutoGainControl, true },
  { MediaAudioConstraints::kGoogExperimentalAutoGainControl, true },
  { MediaAudioConstraints::kGoogNoiseSuppression, true },
  { MediaAudioConstraints::kGoogHighpassFilter, true },
  { MediaAudioConstraints::kGoogTypingNoiseDetection, true },
  { MediaAudioConstraints::kGoogExperimentalNoiseSuppression, false },
#if defined(OS_WIN)
  { kMediaStreamAudioDucking, true },
#else
  { kMediaStreamAudioDucking, false },
#endif
};

bool IsAudioProcessingConstraint(const std::string& key) {
  // |kMediaStreamAudioDucking| does not require audio processing.
  return key != kMediaStreamAudioDucking;
}

} // namespace

// TODO(xians): Remove this method after the APM in WebRtc is deprecated.
void MediaAudioConstraints::ApplyFixedAudioConstraints(
    RTCMediaConstraints* constraints) {
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++i) {
    bool already_set_value;
    if (!webrtc::FindConstraint(constraints, kDefaultAudioConstraints[i].key,
                                &already_set_value, NULL)) {
      const std::string value = kDefaultAudioConstraints[i].value ?
          webrtc::MediaConstraintsInterface::kValueTrue :
          webrtc::MediaConstraintsInterface::kValueFalse;
      constraints->AddOptional(kDefaultAudioConstraints[i].key, value, false);
    } else {
      DVLOG(1) << "Constraint " << kDefaultAudioConstraints[i].key
               << " already set to " << already_set_value;
    }
  }
}

MediaAudioConstraints::MediaAudioConstraints(
    const blink::WebMediaConstraints& constraints, int effects)
    : constraints_(constraints),
      effects_(effects),
      default_audio_processing_constraint_value_(true) {
  // The default audio processing constraints are turned off when
  // - gUM has a specific kMediaStreamSource, which is used by tab capture
  //   and screen capture.
  // - |kEchoCancellation| is explicitly set to false.
  std::string value_str;
  bool value_bool = false;
  if ((GetConstraintValueAsString(constraints, kMediaStreamSource,
                                  &value_str)) ||
      (GetConstraintValueAsBoolean(constraints_, kEchoCancellation,
                                   &value_bool) && !value_bool)) {
    default_audio_processing_constraint_value_ = false;
  }
}

MediaAudioConstraints::~MediaAudioConstraints() {}

// TODO(xians): Remove this method after the APM in WebRtc is deprecated.
bool MediaAudioConstraints::NeedsAudioProcessing() {
  if (GetEchoCancellationProperty())
    return true;

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++i) {
    // |kEchoCancellation| and |kGoogEchoCancellation| have been convered by
    // GetEchoCancellationProperty().
    if (kDefaultAudioConstraints[i].key != kEchoCancellation &&
        kDefaultAudioConstraints[i].key != kGoogEchoCancellation &&
        IsAudioProcessingConstraint(kDefaultAudioConstraints[i].key) &&
        GetProperty(kDefaultAudioConstraints[i].key)) {
      return true;
    }
  }

  return false;
}

bool MediaAudioConstraints::GetProperty(const std::string& key) {
  // Return the value if the constraint is specified in |constraints|,
  // otherwise return the default value.
  bool value = false;
  if (!GetConstraintValueAsBoolean(constraints_, key, &value))
    value = GetDefaultValueForConstraint(constraints_, key);

  return value;
}

bool MediaAudioConstraints::GetEchoCancellationProperty() {
  // If platform echo canceller is enabled, disable the software AEC.
  if (effects_ & media::AudioParameters::ECHO_CANCELLER)
    return false;

  // If |kEchoCancellation| is specified in the constraints, it will
  // override the value of |kGoogEchoCancellation|.
  bool value = false;
  if (GetConstraintValueAsBoolean(constraints_, kEchoCancellation, &value))
    return value;

  return GetProperty(kGoogEchoCancellation);
}

bool MediaAudioConstraints::IsValid() {
  blink::WebVector<blink::WebMediaConstraint> mandatory;
  constraints_.getMandatoryConstraints(mandatory);
  for (size_t i = 0; i < mandatory.size(); ++i) {
    const std::string key = mandatory[i].m_name.utf8();
    if (key == kMediaStreamSource || key == kMediaStreamSourceId ||
        key == MediaStreamSource::kSourceId) {
      // Ignore Chrome specific Tab capture and |kSourceId| constraints.
      continue;
    }

    bool valid = false;
    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++j) {
      if (key == kDefaultAudioConstraints[j].key) {
        bool value = false;
        valid = GetMandatoryConstraintValueAsBoolean(constraints_, key, &value);
        break;
      }
    }

    if (!valid) {
      DLOG(ERROR) << "Invalid MediaStream constraint. Name: " << key;
      return false;
    }
  }

  return true;
}

bool MediaAudioConstraints::GetDefaultValueForConstraint(
    const blink::WebMediaConstraints& constraints, const std::string& key) {
  // |kMediaStreamAudioDucking| is not restricted by
  // |default_audio_processing_constraint_value_| since it does not require
  // audio processing.
  if (!default_audio_processing_constraint_value_ &&
      IsAudioProcessingConstraint(key))
    return false;

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++i) {
    if (kDefaultAudioConstraints[i].key == key)
      return kDefaultAudioConstraints[i].value;
  }

  return false;
}

void EnableEchoCancellation(AudioProcessing* audio_processing) {
#if defined(OS_ANDROID) || defined(OS_IOS)
  const std::string group_name =
      base::FieldTrialList::FindFullName("ReplaceAECMWithAEC");
  if (group_name.empty() || group_name != "Enabled") {
    // Mobile devices are using AECM.
    int err = audio_processing->echo_control_mobile()->set_routing_mode(
        webrtc::EchoControlMobile::kSpeakerphone);
    err |= audio_processing->echo_control_mobile()->Enable(true);
    CHECK_EQ(err, 0);
    return;
  }
#endif
  int err = audio_processing->echo_cancellation()->set_suppression_level(
      webrtc::EchoCancellation::kHighSuppression);

  // Enable the metrics for AEC.
  err |= audio_processing->echo_cancellation()->enable_metrics(true);
  err |= audio_processing->echo_cancellation()->enable_delay_logging(true);
  err |= audio_processing->echo_cancellation()->Enable(true);
  CHECK_EQ(err, 0);
}

void EnableNoiseSuppression(AudioProcessing* audio_processing) {
  int err = audio_processing->noise_suppression()->set_level(
      webrtc::NoiseSuppression::kHigh);
  err |= audio_processing->noise_suppression()->Enable(true);
  CHECK_EQ(err, 0);
}

void EnableExperimentalNoiseSuppression(AudioProcessing* audio_processing) {
  CHECK_EQ(audio_processing->EnableExperimentalNs(true), 0);
}

void EnableHighPassFilter(AudioProcessing* audio_processing) {
  CHECK_EQ(audio_processing->high_pass_filter()->Enable(true), 0);
}

void EnableTypingDetection(AudioProcessing* audio_processing,
                           webrtc::TypingDetection* typing_detector) {
  int err = audio_processing->voice_detection()->Enable(true);
  err |= audio_processing->voice_detection()->set_likelihood(
      webrtc::VoiceDetection::kVeryLowLikelihood);
  CHECK_EQ(err, 0);

  // Configure the update period to 1s (100 * 10ms) in the typing detector.
  typing_detector->SetParameters(0, 0, 0, 0, 0, 100);
}

void EnableExperimentalEchoCancellation(AudioProcessing* audio_processing) {
  webrtc::Config config;
  config.Set<webrtc::DelayCorrection>(new webrtc::DelayCorrection(true));
  audio_processing->SetExtraOptions(config);
}

void StartEchoCancellationDump(AudioProcessing* audio_processing,
                               base::File aec_dump_file) {
  DCHECK(aec_dump_file.IsValid());

  FILE* stream = base::FileToFILE(aec_dump_file.Pass(), "w");
  if (!stream) {
    LOG(ERROR) << "Failed to open AEC dump file";
    return;
  }

  if (audio_processing->StartDebugRecording(stream))
    DLOG(ERROR) << "Fail to start AEC debug recording";
}

void StopEchoCancellationDump(AudioProcessing* audio_processing) {
  if (audio_processing->StopDebugRecording())
    DLOG(ERROR) << "Fail to stop AEC debug recording";
}

void EnableAutomaticGainControl(AudioProcessing* audio_processing) {
#if defined(OS_ANDROID) || defined(OS_IOS)
  const webrtc::GainControl::Mode mode = webrtc::GainControl::kFixedDigital;
#else
  const webrtc::GainControl::Mode mode = webrtc::GainControl::kAdaptiveAnalog;
#endif
  int err = audio_processing->gain_control()->set_mode(mode);
  err |= audio_processing->gain_control()->Enable(true);
  CHECK_EQ(err, 0);
}

void GetAecStats(AudioProcessing* audio_processing,
                 webrtc::AudioProcessorInterface::AudioProcessorStats* stats) {
  // These values can take on valid negative values, so use the lowest possible
  // level as default rather than -1.
  stats->echo_return_loss = -100;
  stats->echo_return_loss_enhancement = -100;

  // These values can also be negative, but in practice -1 is only used to
  // signal insufficient data, since the resolution is limited to multiples
  // of 4ms.
  stats->echo_delay_median_ms = -1;
  stats->echo_delay_std_ms = -1;

  // TODO(ajm): Re-enable this metric once we have a reliable implementation.
  stats->aec_quality_min = -1.0f;

  if (!audio_processing->echo_cancellation()->are_metrics_enabled() ||
      !audio_processing->echo_cancellation()->is_delay_logging_enabled() ||
      !audio_processing->echo_cancellation()->is_enabled()) {
    return;
  }

  // TODO(ajm): we may want to use VoECallReport::GetEchoMetricsSummary
  // here, but it appears to be unsuitable currently. Revisit after this is
  // investigated: http://b/issue?id=5666755
  webrtc::EchoCancellation::Metrics echo_metrics;
  if (!audio_processing->echo_cancellation()->GetMetrics(&echo_metrics)) {
    stats->echo_return_loss = echo_metrics.echo_return_loss.instant;
    stats->echo_return_loss_enhancement =
        echo_metrics.echo_return_loss_enhancement.instant;
  }

  int median = 0, std = 0;
  if (!audio_processing->echo_cancellation()->GetDelayMetrics(&median, &std)) {
    stats->echo_delay_median_ms = median;
    stats->echo_delay_std_ms = std;
  }
}

}  // namespace content
<commit_msg>Use Config to enable experimental noise suppression<commit_after>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/media/media_stream_audio_processor_options.h"

#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/field_trial.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "content/common/media/media_stream_options.h"
#include "content/renderer/media/media_stream_constraints_util.h"
#include "content/renderer/media/media_stream_source.h"
#include "content/renderer/media/rtc_media_constraints.h"
#include "media/audio/audio_parameters.h"
#include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
#include "third_party/webrtc/modules/audio_processing/typing_detection.h"

namespace content {

const char MediaAudioConstraints::kEchoCancellation[] = "echoCancellation";
const char MediaAudioConstraints::kGoogEchoCancellation[] =
    "googEchoCancellation";
const char MediaAudioConstraints::kGoogExperimentalEchoCancellation[] =
    "googEchoCancellation2";
const char MediaAudioConstraints::kGoogAutoGainControl[] =
    "googAutoGainControl";
const char MediaAudioConstraints::kGoogExperimentalAutoGainControl[] =
    "googAutoGainControl2";
const char MediaAudioConstraints::kGoogNoiseSuppression[] =
    "googNoiseSuppression";
const char MediaAudioConstraints::kGoogExperimentalNoiseSuppression[] =
    "googNoiseSuppression2";
const char MediaAudioConstraints::kGoogHighpassFilter[] = "googHighpassFilter";
const char MediaAudioConstraints::kGoogTypingNoiseDetection[] =
    "googTypingNoiseDetection";
const char MediaAudioConstraints::kGoogAudioMirroring[] = "googAudioMirroring";

namespace {

// Constant constraint keys which enables default audio constraints on
// mediastreams with audio.
struct {
  const char* key;
  bool value;
} const kDefaultAudioConstraints[] = {
  { MediaAudioConstraints::kEchoCancellation, true },
  { MediaAudioConstraints::kGoogEchoCancellation, true },
#if defined(OS_ANDROID) || defined(OS_IOS)
  { MediaAudioConstraints::kGoogExperimentalEchoCancellation, false },
#else
  // Enable the extended filter mode AEC on all non-mobile platforms.
  { MediaAudioConstraints::kGoogExperimentalEchoCancellation, true },
#endif
  { MediaAudioConstraints::kGoogAutoGainControl, true },
  { MediaAudioConstraints::kGoogExperimentalAutoGainControl, true },
  { MediaAudioConstraints::kGoogNoiseSuppression, true },
  { MediaAudioConstraints::kGoogHighpassFilter, true },
  { MediaAudioConstraints::kGoogTypingNoiseDetection, true },
  { MediaAudioConstraints::kGoogExperimentalNoiseSuppression, false },
#if defined(OS_WIN)
  { kMediaStreamAudioDucking, true },
#else
  { kMediaStreamAudioDucking, false },
#endif
};

bool IsAudioProcessingConstraint(const std::string& key) {
  // |kMediaStreamAudioDucking| does not require audio processing.
  return key != kMediaStreamAudioDucking;
}

} // namespace

// TODO(xians): Remove this method after the APM in WebRtc is deprecated.
void MediaAudioConstraints::ApplyFixedAudioConstraints(
    RTCMediaConstraints* constraints) {
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++i) {
    bool already_set_value;
    if (!webrtc::FindConstraint(constraints, kDefaultAudioConstraints[i].key,
                                &already_set_value, NULL)) {
      const std::string value = kDefaultAudioConstraints[i].value ?
          webrtc::MediaConstraintsInterface::kValueTrue :
          webrtc::MediaConstraintsInterface::kValueFalse;
      constraints->AddOptional(kDefaultAudioConstraints[i].key, value, false);
    } else {
      DVLOG(1) << "Constraint " << kDefaultAudioConstraints[i].key
               << " already set to " << already_set_value;
    }
  }
}

MediaAudioConstraints::MediaAudioConstraints(
    const blink::WebMediaConstraints& constraints, int effects)
    : constraints_(constraints),
      effects_(effects),
      default_audio_processing_constraint_value_(true) {
  // The default audio processing constraints are turned off when
  // - gUM has a specific kMediaStreamSource, which is used by tab capture
  //   and screen capture.
  // - |kEchoCancellation| is explicitly set to false.
  std::string value_str;
  bool value_bool = false;
  if ((GetConstraintValueAsString(constraints, kMediaStreamSource,
                                  &value_str)) ||
      (GetConstraintValueAsBoolean(constraints_, kEchoCancellation,
                                   &value_bool) && !value_bool)) {
    default_audio_processing_constraint_value_ = false;
  }
}

MediaAudioConstraints::~MediaAudioConstraints() {}

// TODO(xians): Remove this method after the APM in WebRtc is deprecated.
bool MediaAudioConstraints::NeedsAudioProcessing() {
  if (GetEchoCancellationProperty())
    return true;

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++i) {
    // |kEchoCancellation| and |kGoogEchoCancellation| have been convered by
    // GetEchoCancellationProperty().
    if (kDefaultAudioConstraints[i].key != kEchoCancellation &&
        kDefaultAudioConstraints[i].key != kGoogEchoCancellation &&
        IsAudioProcessingConstraint(kDefaultAudioConstraints[i].key) &&
        GetProperty(kDefaultAudioConstraints[i].key)) {
      return true;
    }
  }

  return false;
}

bool MediaAudioConstraints::GetProperty(const std::string& key) {
  // Return the value if the constraint is specified in |constraints|,
  // otherwise return the default value.
  bool value = false;
  if (!GetConstraintValueAsBoolean(constraints_, key, &value))
    value = GetDefaultValueForConstraint(constraints_, key);

  return value;
}

bool MediaAudioConstraints::GetEchoCancellationProperty() {
  // If platform echo canceller is enabled, disable the software AEC.
  if (effects_ & media::AudioParameters::ECHO_CANCELLER)
    return false;

  // If |kEchoCancellation| is specified in the constraints, it will
  // override the value of |kGoogEchoCancellation|.
  bool value = false;
  if (GetConstraintValueAsBoolean(constraints_, kEchoCancellation, &value))
    return value;

  return GetProperty(kGoogEchoCancellation);
}

bool MediaAudioConstraints::IsValid() {
  blink::WebVector<blink::WebMediaConstraint> mandatory;
  constraints_.getMandatoryConstraints(mandatory);
  for (size_t i = 0; i < mandatory.size(); ++i) {
    const std::string key = mandatory[i].m_name.utf8();
    if (key == kMediaStreamSource || key == kMediaStreamSourceId ||
        key == MediaStreamSource::kSourceId) {
      // Ignore Chrome specific Tab capture and |kSourceId| constraints.
      continue;
    }

    bool valid = false;
    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++j) {
      if (key == kDefaultAudioConstraints[j].key) {
        bool value = false;
        valid = GetMandatoryConstraintValueAsBoolean(constraints_, key, &value);
        break;
      }
    }

    if (!valid) {
      DLOG(ERROR) << "Invalid MediaStream constraint. Name: " << key;
      return false;
    }
  }

  return true;
}

bool MediaAudioConstraints::GetDefaultValueForConstraint(
    const blink::WebMediaConstraints& constraints, const std::string& key) {
  // |kMediaStreamAudioDucking| is not restricted by
  // |default_audio_processing_constraint_value_| since it does not require
  // audio processing.
  if (!default_audio_processing_constraint_value_ &&
      IsAudioProcessingConstraint(key))
    return false;

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++i) {
    if (kDefaultAudioConstraints[i].key == key)
      return kDefaultAudioConstraints[i].value;
  }

  return false;
}

void EnableEchoCancellation(AudioProcessing* audio_processing) {
#if defined(OS_ANDROID) || defined(OS_IOS)
  const std::string group_name =
      base::FieldTrialList::FindFullName("ReplaceAECMWithAEC");
  if (group_name.empty() || group_name != "Enabled") {
    // Mobile devices are using AECM.
    int err = audio_processing->echo_control_mobile()->set_routing_mode(
        webrtc::EchoControlMobile::kSpeakerphone);
    err |= audio_processing->echo_control_mobile()->Enable(true);
    CHECK_EQ(err, 0);
    return;
  }
#endif
  int err = audio_processing->echo_cancellation()->set_suppression_level(
      webrtc::EchoCancellation::kHighSuppression);

  // Enable the metrics for AEC.
  err |= audio_processing->echo_cancellation()->enable_metrics(true);
  err |= audio_processing->echo_cancellation()->enable_delay_logging(true);
  err |= audio_processing->echo_cancellation()->Enable(true);
  CHECK_EQ(err, 0);
}

void EnableNoiseSuppression(AudioProcessing* audio_processing) {
  int err = audio_processing->noise_suppression()->set_level(
      webrtc::NoiseSuppression::kHigh);
  err |= audio_processing->noise_suppression()->Enable(true);
  CHECK_EQ(err, 0);
}

void EnableExperimentalNoiseSuppression(AudioProcessing* audio_processing) {
  webrtc::Config config;
  config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(true));
  audio_processing->SetExtraOptions(config);
}

void EnableHighPassFilter(AudioProcessing* audio_processing) {
  CHECK_EQ(audio_processing->high_pass_filter()->Enable(true), 0);
}

void EnableTypingDetection(AudioProcessing* audio_processing,
                           webrtc::TypingDetection* typing_detector) {
  int err = audio_processing->voice_detection()->Enable(true);
  err |= audio_processing->voice_detection()->set_likelihood(
      webrtc::VoiceDetection::kVeryLowLikelihood);
  CHECK_EQ(err, 0);

  // Configure the update period to 1s (100 * 10ms) in the typing detector.
  typing_detector->SetParameters(0, 0, 0, 0, 0, 100);
}

void EnableExperimentalEchoCancellation(AudioProcessing* audio_processing) {
  webrtc::Config config;
  config.Set<webrtc::DelayCorrection>(new webrtc::DelayCorrection(true));
  audio_processing->SetExtraOptions(config);
}

void StartEchoCancellationDump(AudioProcessing* audio_processing,
                               base::File aec_dump_file) {
  DCHECK(aec_dump_file.IsValid());

  FILE* stream = base::FileToFILE(aec_dump_file.Pass(), "w");
  if (!stream) {
    LOG(ERROR) << "Failed to open AEC dump file";
    return;
  }

  if (audio_processing->StartDebugRecording(stream))
    DLOG(ERROR) << "Fail to start AEC debug recording";
}

void StopEchoCancellationDump(AudioProcessing* audio_processing) {
  if (audio_processing->StopDebugRecording())
    DLOG(ERROR) << "Fail to stop AEC debug recording";
}

void EnableAutomaticGainControl(AudioProcessing* audio_processing) {
#if defined(OS_ANDROID) || defined(OS_IOS)
  const webrtc::GainControl::Mode mode = webrtc::GainControl::kFixedDigital;
#else
  const webrtc::GainControl::Mode mode = webrtc::GainControl::kAdaptiveAnalog;
#endif
  int err = audio_processing->gain_control()->set_mode(mode);
  err |= audio_processing->gain_control()->Enable(true);
  CHECK_EQ(err, 0);
}

void GetAecStats(AudioProcessing* audio_processing,
                 webrtc::AudioProcessorInterface::AudioProcessorStats* stats) {
  // These values can take on valid negative values, so use the lowest possible
  // level as default rather than -1.
  stats->echo_return_loss = -100;
  stats->echo_return_loss_enhancement = -100;

  // These values can also be negative, but in practice -1 is only used to
  // signal insufficient data, since the resolution is limited to multiples
  // of 4ms.
  stats->echo_delay_median_ms = -1;
  stats->echo_delay_std_ms = -1;

  // TODO(ajm): Re-enable this metric once we have a reliable implementation.
  stats->aec_quality_min = -1.0f;

  if (!audio_processing->echo_cancellation()->are_metrics_enabled() ||
      !audio_processing->echo_cancellation()->is_delay_logging_enabled() ||
      !audio_processing->echo_cancellation()->is_enabled()) {
    return;
  }

  // TODO(ajm): we may want to use VoECallReport::GetEchoMetricsSummary
  // here, but it appears to be unsuitable currently. Revisit after this is
  // investigated: http://b/issue?id=5666755
  webrtc::EchoCancellation::Metrics echo_metrics;
  if (!audio_processing->echo_cancellation()->GetMetrics(&echo_metrics)) {
    stats->echo_return_loss = echo_metrics.echo_return_loss.instant;
    stats->echo_return_loss_enhancement =
        echo_metrics.echo_return_loss_enhancement.instant;
  }

  int median = 0, std = 0;
  if (!audio_processing->echo_cancellation()->GetDelayMetrics(&median, &std)) {
    stats->echo_delay_median_ms = median;
    stats->echo_delay_std_ms = std;
  }
}

}  // namespace content
<|endoftext|>


<commit_before>// Implementations of various convex hull algorithms
// using the C++ Standard Library.
// For clarity, the implementations do not check for
// duplicate or collinear points.

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

struct point {
	float x;
	float y;

	point(float xIn, float yIn) : x(xIn), y(yIn) { }
};

// The z-value of the cross product of segments
// (a, b) and (a, c). Positive means c is ccw
// from (a, b), negative cw. Zero means its collinear.
float ccw(const point& a, const point& b, const point& c) {
	return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}

// Returns true if a is lexicographically before b.
bool isLeftOf(const point& a, const point& b) {
	return (a.x < b.x || (a.x == b.x && a.y < b.y));
}

// Used to sort points in ccw order about a pivot.
struct ccwSorter {
	const point& pivot;

	ccwSorter(const point& inPivot) : pivot(inPivot) { }

	bool operator()(const point& a, const point& b) {
		return ccw(pivot, a, b) < 0;
	}
};

// The length of segment (a, b).
float len(const point& a, const point& b) {
	return sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
}

// The unsigned distance of p from segment (a, b).
float dist(const point& a, const point& b, const point& p) {
	return fabs((b.x - a.x) * (a.y - p.y) - (b.y - a.y) * (a.x - p.x)) / len(a, b);
}

// Returns the index of the farthest point from segment (a, b).
size_t getFarthest(const point& a, const point& b, const vector<point>& v) {
	size_t idxMax = 0;
	float distMax = dist(a, b, v[idxMax]);

	for (size_t i = 1; i < v.size(); ++i) {
		float distCurr = dist(a, b, v[i]);
		if (distCurr > distMax) {
			idxMax = i;
			distMax = distCurr;
		}
	}

	return idxMax;
}

// The gift-wrapping algorithm for convex hull.
// https://en.wikipedia.org/wiki/Gift_wrapping_algorithm
vector<point> giftWrapping(const vector<point>& v) {
	// Start with the leftmost point.
	size_t startIdx = min_element(v.begin(), v.end(), isLeftOf) - v.begin();
	size_t hIdx = startIdx;

	vector<point> hull;
	do {
		// Add our current point to the hull.
		hull.push_back(v[hIdx]);

		// Find the index of the input point that is farthest
		// ccw from our last hull point.
		ccwSorter isCcw(v[hIdx]);
		size_t endIdx = 0;
		for (size_t i = 1; i < v.size(); ++i) {
			if ((endIdx == hIdx) || isCcw(v[endIdx], v[i])) {
				endIdx = i;
			}
		}

		// Make that our new
		hIdx = endIdx;
	} while (hIdx != startIdx);

	return hull;
}

// The Graham scan algorithm for convex hull.
// https://en.wikipedia.org/wiki/Graham_scan
vector<point> GrahamScan(vector<point> v) {
	// Put our leftmost point at index 0
	swap(v[0], *min_element(v.begin(), v.end(), isLeftOf));

	// Sort the rest of the points in counter-clockwise order
	// from our leftmost point.
	sort(v.begin() + 1, v.end(), ccwSorter(v[0]));

	// Add our first three points to the hull.
	vector<point> hull;
	auto it = v.begin();
	hull.push_back(*it++);
	hull.push_back(*it++);
	hull.push_back(*it++);

	while (it != v.end()) {
		// Pop off any points that make a convex angle with *it
		while (ccw(*(hull.rbegin() + 1), *(hull.rbegin()), *it) >= 0) {
			hull.pop_back();
		}
		hull.push_back(*it++);
	}

	return hull;
}

// The monotone chain algorithm for convex hull.
vector<point> monotoneChain(vector<point> v) {
	// Sort our points in lexicographic order.
	sort(v.begin(), v.end(), isLeftOf);

	// Find the lower half of the convex hull.
	vector<point> lower;
	for (auto it = v.begin(); it != v.end(); ++it) {
		// Pop off any points that make a convex angle with *it
		while (lower.size() >= 2 && ccw(*(lower.rbegin() + 1), *(lower.rbegin()), *it) >= 0) {
			lower.pop_back();
		}
		lower.push_back(*it);
	}

	// Find the upper half of the convex hull.
	vector<point> upper;
	for (auto it = v.rbegin(); it != v.rend(); ++it) {
		// Pop off any points that make a convex angle with *it
		while (upper.size() >= 2 && ccw(*(upper.rbegin() + 1), *(upper.rbegin()), *it) >= 0) {
			upper.pop_back();
		}
		upper.push_back(*it);
	}

	vector<point> hull;
	hull.insert(hull.end(), lower.begin(), lower.end());
	// Both hulls include both endpoints, so leave them out when we
	// append the upper hull.
	hull.insert(hull.end(), upper.begin() + 1, upper.end() - 1);
	return hull;
}

// Recursive call of the quickhull algorithm.
void quickHull(const vector<point>& v, const point& a, const point& b,
			   vector<point>& hull) {
	if (v.empty()) {
		return;
	}

	point f = v[getFarthest(a, b, v)];

	// Collect points to the left of segment (a, f)
	vector<point> left;
	for (auto p : v) {
		if (ccw(a, f, p) > 0) {
			left.push_back(p);
		}
	}
	quickHull(left, a, f, hull);

	// Add f to the hull
	hull.push_back(f);

	// Collect points to the left of segment (f, b)
	vector<point> right;
	for (auto p : v) {
		if (ccw(f, b, p) > 0) {
			right.push_back(p);
		}
	}
	quickHull(right, f, b, hull);
}

// QuickHull algorithm.
// https://en.wikipedia.org/wiki/QuickHull
vector<point> quickHull(const vector<point>& v) {
	vector<point> hull;

	// Start with the leftmost and rightmost points.
	point a = *min_element(v.begin(), v.end(), isLeftOf);
	point b = *max_element(v.begin(), v.end(), isLeftOf);

	// Split the points on either side of segment (a, b)
	vector<point> left, right;
	for (auto p : v) {
		ccw(a, b, p) > 0 ? left.push_back(p) : right.push_back(p);
	}

	// Be careful to add points to the hull
	// in the correct order. Add our leftmost point.
	hull.push_back(a);

	// Add hull points from the left (top)
	quickHull(left, a, b, hull);

	// Add our rightmost point
	hull.push_back(b);

	// Add hull points from the right (bottom)
	quickHull(right, b, a, hull);

	return hull;
}

vector<point> getPoints() {
	vector<point> v;

	const float lo = -100.0;
	const float hi = 100.0;

	for (int i = 0; i < 100; ++i) {
		float x = lo +
			static_cast<float>(
				rand()) / static_cast<float>(RAND_MAX / (hi - lo));

		float y = lo +
			static_cast<float>(
				rand()) / static_cast<float>(RAND_MAX / (hi - lo));

		v.push_back(point(x, y));
	}

	return v;
}

void print(const vector<point>& v) {
	for (auto p : v) {
		cout << p.x << ", " << p.y << endl;
	}
}

int main() {
	vector<point> v = getPoints();

	vector<point> h = quickHull(v);
	cout << "quickHull point count: " << h.size() << endl;
	print(h);

 	h = giftWrapping(v);
	cout << endl << "giftWrapping point count: " << h.size() << endl;
	print(h);

	h = monotoneChain(v);
	cout << endl << "monotoneChain point count: " << h.size() << endl;
	print(h);

	h = GrahamScan(v);
	cout << endl << "GrahamScan point count: " << h.size() << endl;
	print(h);

	return 0;
}
<commit_msg>simplify gift-wrapping algorithm<commit_after>// Implementations of various convex hull algorithms
// using the C++ Standard Library.
// For clarity, the implementations do not check for
// duplicate or collinear points.

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

struct point {
	float x;
	float y;

	point(float xIn, float yIn) : x(xIn), y(yIn) { }
};

// The z-value of the cross product of segments
// (a, b) and (a, c). Positive means c is ccw
// from (a, b), negative cw. Zero means its collinear.
float ccw(const point& a, const point& b, const point& c) {
	return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}

// Returns true if a is lexicographically before b.
bool isLeftOf(const point& a, const point& b) {
	return (a.x < b.x || (a.x == b.x && a.y < b.y));
}

// Used to sort points in ccw order about a pivot.
struct ccwSorter {
	const point& pivot;

	ccwSorter(const point& inPivot) : pivot(inPivot) { }

	bool operator()(const point& a, const point& b) {
		return ccw(pivot, a, b) < 0;
	}
};

// The length of segment (a, b).
float len(const point& a, const point& b) {
	return sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
}

// The unsigned distance of p from segment (a, b).
float dist(const point& a, const point& b, const point& p) {
	return fabs((b.x - a.x) * (a.y - p.y) - (b.y - a.y) * (a.x - p.x)) / len(a, b);
}

// Returns the index of the farthest point from segment (a, b).
size_t getFarthest(const point& a, const point& b, const vector<point>& v) {
	size_t idxMax = 0;
	float distMax = dist(a, b, v[idxMax]);

	for (size_t i = 1; i < v.size(); ++i) {
		float distCurr = dist(a, b, v[i]);
		if (distCurr > distMax) {
			idxMax = i;
			distMax = distCurr;
		}
	}

	return idxMax;
}

// The gift-wrapping algorithm for convex hull.
// https://en.wikipedia.org/wiki/Gift_wrapping_algorithm
vector<point> giftWrapping(vector<point> v) {
	// Move the leftmost point to the beginning of our vector.
	// It will be the first point in our convext hull.
	swap(v[0], *min_element(v.begin(), v.end(), isLeftOf));

	vector<point> hull;
	// Repeatedly find the first ccw point from our last hull point
	// and put it at the front of our array.
	// Stop when we see our first point again.
	do {
		hull.push_back(v[0]);
		swap(v[0], *min_element(v.begin() + 1, v.end(), ccwSorter(v[0])));
	} while (v[0].x != hull[0].x && v[0].y != hull[0].y);

	return hull;
}

// The Graham scan algorithm for convex hull.
// https://en.wikipedia.org/wiki/Graham_scan
vector<point> GrahamScan(vector<point> v) {
	// Put our leftmost point at index 0
	swap(v[0], *min_element(v.begin(), v.end(), isLeftOf));

	// Sort the rest of the points in counter-clockwise order
	// from our leftmost point.
	sort(v.begin() + 1, v.end(), ccwSorter(v[0]));

	// Add our first three points to the hull.
	vector<point> hull;
	auto it = v.begin();
	hull.push_back(*it++);
	hull.push_back(*it++);
	hull.push_back(*it++);

	while (it != v.end()) {
		// Pop off any points that make a convex angle with *it
		while (ccw(*(hull.rbegin() + 1), *(hull.rbegin()), *it) >= 0) {
			hull.pop_back();
		}
		hull.push_back(*it++);
	}

	return hull;
}

// The monotone chain algorithm for convex hull.
vector<point> monotoneChain(vector<point> v) {
	// Sort our points in lexicographic order.
	sort(v.begin(), v.end(), isLeftOf);

	// Find the lower half of the convex hull.
	vector<point> lower;
	for (auto it = v.begin(); it != v.end(); ++it) {
		// Pop off any points that make a convex angle with *it
		while (lower.size() >= 2 && ccw(*(lower.rbegin() + 1), *(lower.rbegin()), *it) >= 0) {
			lower.pop_back();
		}
		lower.push_back(*it);
	}

	// Find the upper half of the convex hull.
	vector<point> upper;
	for (auto it = v.rbegin(); it != v.rend(); ++it) {
		// Pop off any points that make a convex angle with *it
		while (upper.size() >= 2 && ccw(*(upper.rbegin() + 1), *(upper.rbegin()), *it) >= 0) {
			upper.pop_back();
		}
		upper.push_back(*it);
	}

	vector<point> hull;
	hull.insert(hull.end(), lower.begin(), lower.end());
	// Both hulls include both endpoints, so leave them out when we
	// append the upper hull.
	hull.insert(hull.end(), upper.begin() + 1, upper.end() - 1);
	return hull;
}

// Recursive call of the quickhull algorithm.
void quickHull(const vector<point>& v, const point& a, const point& b,
			   vector<point>& hull) {
	if (v.empty()) {
		return;
	}

	point f = v[getFarthest(a, b, v)];

	// Collect points to the left of segment (a, f)
	vector<point> left;
	for (auto p : v) {
		if (ccw(a, f, p) > 0) {
			left.push_back(p);
		}
	}
	quickHull(left, a, f, hull);

	// Add f to the hull
	hull.push_back(f);

	// Collect points to the left of segment (f, b)
	vector<point> right;
	for (auto p : v) {
		if (ccw(f, b, p) > 0) {
			right.push_back(p);
		}
	}
	quickHull(right, f, b, hull);
}

// QuickHull algorithm.
// https://en.wikipedia.org/wiki/QuickHull
vector<point> quickHull(const vector<point>& v) {
	vector<point> hull;

	// Start with the leftmost and rightmost points.
	point a = *min_element(v.begin(), v.end(), isLeftOf);
	point b = *max_element(v.begin(), v.end(), isLeftOf);

	// Split the points on either side of segment (a, b)
	vector<point> left, right;
	for (auto p : v) {
		ccw(a, b, p) > 0 ? left.push_back(p) : right.push_back(p);
	}

	// Be careful to add points to the hull
	// in the correct order. Add our leftmost point.
	hull.push_back(a);

	// Add hull points from the left (top)
	quickHull(left, a, b, hull);

	// Add our rightmost point
	hull.push_back(b);

	// Add hull points from the right (bottom)
	quickHull(right, b, a, hull);

	return hull;
}

vector<point> getPoints() {
	vector<point> v;

	const float lo = -100.0;
	const float hi = 100.0;

	for (int i = 0; i < 100; ++i) {
		float x = lo +
			static_cast<float>(
				rand()) / static_cast<float>(RAND_MAX / (hi - lo));

		float y = lo +
			static_cast<float>(
				rand()) / static_cast<float>(RAND_MAX / (hi - lo));

		v.push_back(point(x, y));
	}

	return v;
}

void print(const vector<point>& v) {
	for (auto p : v) {
		cout << p.x << ", " << p.y << endl;
	}
}

int main() {
	vector<point> v = getPoints();

	vector<point> h = quickHull(v);
	cout << "quickHull point count: " << h.size() << endl;
	print(h);

 	h = giftWrapping(v);
	cout << endl << "giftWrapping point count: " << h.size() << endl;
	print(h);

	h = monotoneChain(v);
	cout << endl << "monotoneChain point count: " << h.size() << endl;
	print(h);

	h = GrahamScan(v);
	cout << endl << "GrahamScan point count: " << h.size() << endl;
	print(h);

	return 0;
}
<|endoftext|>


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

#include "clotho/utility/random_generator.hpp"
#include "clotho/classifiers/bernoulli_classifier.hpp"

namespace clotho {
namespace utility {

template < class URNG >
class random_generator< URNG, clotho::classifiers::bernoulli_classifier< URNG, double, bool > > {
public:
    typedef random_generator< URNG, clotho::classifiers::bernoulli_classifier< URNG, double, bool > > self_type;
    typedef clotho::classifiers::bernoulli_classifier< URNG, double, bool >   result_type;

    random_generator( URNG & rng, boost::property_tree::ptree & config ) :
        m_rng( &rng )
        , m_p( 0.5 ) {
        parseConfig( config );
    }

    random_generator( URNG & rng, double p = 0.5 ) :
        m_rng( &rng )
        , m_p( p ) {
    }

    result_type operator()() {
        return result_type( m_rng, m_p );
    }

protected:

    void parseConfig( boost::property_tree::ptree & config ) {
        const string ppath = "classifiers.toolkit.bernoulli.p";

        if( config.get_child_optional( ppath ) == boost::none ) {
            config.put( ppath, m_p );
        } else {
            m_p = config.get< double >( ppath );

            assert( 0. <= m_p <= 1.);
        }
    }

    URNG        * m_rng;
    double      m_p;
};

}   // namespace utility {
}   // namespace clotho {

#endif  // BERNOULLI_CLASSIFIER_GENERATOR_HPP_
<commit_msg>Stupid error<commit_after>//   Copyright 2015 Patrick Putnam
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
#ifndef BERNOULLI_CLASSIFIER_GENERATOR_HPP_
#define BERNOULLI_CLASSIFIER_GENERATOR_HPP_

#include "clotho/utility/random_generator.hpp"
#include "clotho/classifiers/bernoulli_classifier.hpp"

namespace clotho {
namespace utility {

template < class URNG >
class random_generator< URNG, clotho::classifiers::bernoulli_classifier< URNG, double, bool > > {
public:
    typedef random_generator< URNG, clotho::classifiers::bernoulli_classifier< URNG, double, bool > > self_type;
    typedef clotho::classifiers::bernoulli_classifier< URNG, double, bool >   result_type;

    random_generator( URNG & rng, boost::property_tree::ptree & config ) :
        m_rng( &rng )
        , m_p( 0.5 ) {
        parseConfig( config );
    }

    random_generator( URNG & rng, double p = 0.5 ) :
        m_rng( &rng )
        , m_p( p ) {
    }

    result_type operator()() {
        return result_type( m_rng, m_p );
    }

protected:

    void parseConfig( boost::property_tree::ptree & config ) {
        const string ppath = "classifiers.toolkit.bernoulli.p";

        if( config.get_child_optional( ppath ) == boost::none ) {
            config.put( ppath, m_p );
        } else {
            m_p = config.get< double >( ppath );

            assert( 0. <= m_p && m_p <= 1.);
        }
    }

    URNG        * m_rng;
    double      m_p;
};

}   // namespace utility {
}   // namespace clotho {

#endif  // BERNOULLI_CLASSIFIER_GENERATOR_HPP_
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016, Microsoft

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <ostream>
#include <stack>

#include "BitFunnel/Configuration/Factories.h"
#include "BitFunnel/Configuration/IFileSystem.h"
#include "BitFunnel/Index/DocumentHandle.h"
#include "BitFunnel/Index/Factories.h"
#include "BitFunnel/Index/IDocument.h"
#include "BitFunnel/Index/IDocumentCache.h"
#include "BitFunnel/Index/IDocumentFrequencyTable.h"
#include "BitFunnel/Index/IIngestor.h"
#include "BitFunnel/Index/ISimpleIndex.h"
#include "BitFunnel/Index/RowIdSequence.h"
#include "CsvTsv/Csv.h"
#include "DocumentHandleInternal.h"
#include "LoggerInterfaces/Check.h"
#include "RowTableAnalyzer.h"
#include "RowTableDescriptor.h"
#include "Shard.h"
#include "TermToText.h"

namespace BitFunnel
{
    void Factories::AnalyzeRowTables(ISimpleIndex const & index,
                                     char const * outDir)
    {
        char const end = '\0';     // TODO: Workaround for issue #386.
        CHECK_NE(*outDir, end)
          << "Output directory not set. ";

        RowTableAnalyzer statistics(index);
        statistics.AnalyzeColumns(outDir);
        statistics.AnalyzeRows(outDir);
    }

    RowTableAnalyzer::RowTableAnalyzer(ISimpleIndex const & index)
        : m_index(index)
    {
    }

    //*************************************************************************
    //
    // Row Statistics
    //
    //*************************************************************************
    class RowStatistics
    {
    public:
        static void WriteHeader(std::ostream& out)
        {
            CsvTsv::CsvTableFormatter formatter(out);
            formatter.WritePrologue(std::vector<char const *>{
                "shard", "idfX10", "rank", "mean", "min", "max", "var", "count"});
        }

        void WriteSummary(std::ostream& out,
                          ShardId const & shardId,
                          Term::IdfX10 idfX10) const
        {
            for (Rank rank = 0; rank < c_maxRankValue; ++rank)
            {
                Accumulator const & accumulator = m_accumulators[rank];
                if (accumulator.GetCount() == 0)
                    continue;

                out << shardId << ","
                    << static_cast<uint32_t>(idfX10) << ","
                    << rank << ","
                    << accumulator.GetMean() << ","
                    << accumulator.GetMin() << ","
                    << accumulator.GetMax() << ","
                    << accumulator.GetVariance() << ","
                    << accumulator.GetCount() << std::endl;
            }
        }

        void RecordDensity(Rank rank, double density)
        {
            m_accumulators.at(rank).Record(density);
        }

        void Reset()
        {
            for (auto it = m_accumulators.begin(); it != m_accumulators.end(); ++it)
            {
                it->Reset();
            }
        }

    private:
        std::array<Accumulator, c_maxRankValue> m_accumulators;
    };

    //*************************************************************************
    //
    // Analyze rows
    //
    //*************************************************************************
    void RowTableAnalyzer::AnalyzeRows(char const * outDir) const
    {
        //
        // Gather row statistics for ingested documents.
        // (documents need not be cached)
        //
        auto & fileManager = m_index.GetFileManager();
        auto & ingestor = m_index.GetIngestor();

        // Obtain data for converting a term hash to text, if file exists
        std::unique_ptr<ITermToText> termToText;
        try
        {
            // Will fail with exception if file does not exist or can't be read
            // TODO: Create with factory?
            termToText = Factories::CreateTermToText(*fileManager.TermToText().OpenForRead());
        }
        catch (RecoverableError e)
        {
            termToText = Factories::CreateTermToText();
        }

        auto fileSystem = Factories::CreateFileSystem();
        auto outFileManager =
            Factories::CreateFileManager(outDir,
                outDir,
                outDir,
                *fileSystem);

        auto summaryOut = outFileManager->RowDensitySummary().OpenForWrite();
        RowStatistics::WriteHeader(*summaryOut);

        for (ShardId shardId = 0; shardId < ingestor.GetShardCount(); ++shardId)
        {
            AnalyzeRowsInOneShard(shardId,
                                  *termToText,
                                  *outFileManager->RowDensities(shardId).OpenForWrite(),
                                  *summaryOut);
        }
    }

    void RowTableAnalyzer::AnalyzeRowsInOneShard(
        ShardId const & shardId,
        ITermToText const & termToText,
        std::ostream& out,
        std::ostream& summaryOut) const
    {
        auto & shard = m_index.GetIngestor().GetShard(shardId);
        std::array<std::vector<double>, c_maxRankValue+1> densities;

        for (Rank rank = 0; rank <= c_maxRankValue; ++rank)
        {
            densities[rank] = shard.GetDensities(rank);
        }

        RowStatistics statistics;
        Term::IdfX10 curIdfX10 = 0;

        // Use CsvTableFormatter to escape terms that contain commas and quotes.
        CsvTsv::CsvTableFormatter formatter(out);
        auto & fileManager = m_index.GetFileManager();
        auto terms(Factories::CreateDocumentFrequencyTable(
            *fileManager.DocFreqTable(shardId).OpenForRead()));

        for (auto dfEntry : *terms)
        {
            Term term = dfEntry.GetTerm();
            Term::IdfX10 idfX10 = Term::ComputeIdfX10(dfEntry.GetFrequency(), Term::c_maxIdfX10Value);

            // When we cross an idfX10 bucket boundary, output statistics
            // accumulated since previous idfX10 bucket. Then reset
            // statistics accumulators for the next bucket.
            if (idfX10 != curIdfX10)
            {
                curIdfX10 = idfX10;
                statistics.WriteSummary(summaryOut, shardId, curIdfX10);
                statistics.Reset();
            }

            RowIdSequence rows(term, m_index.GetTermTable(shardId));

            std::string termName = termToText.Lookup(term.GetRawHash());
            if (!termName.empty())
            {
                // Print out the term text if we have it.
                formatter.WriteField(termName);
            }
            else
            {
                // Otherwise print out the term's hash.
                formatter.SetHexMode(1);
                formatter.WriteField(term.GetRawHash());
                formatter.SetHexMode(0);
            }

            formatter.WriteField(dfEntry.GetFrequency());

            std::stack<RowId> rowsReversed;
            for (auto row : rows)
            {
                rowsReversed.push(row);
            }

            while (!rowsReversed.empty())
            {
                auto row = rowsReversed.top();
                auto rank = row.GetRank();
                auto index = row.GetIndex();
                auto density = densities.at(rank).at(index);
                statistics.RecordDensity(rank, density);

                formatter.WriteField("r");
                out << row.GetRank();
                formatter.WriteField(index);
                formatter.WriteField(density);

                rowsReversed.pop();
            }
            formatter.WriteRowEnd();
        }
        statistics.WriteSummary(summaryOut, shardId, curIdfX10);
    }

    //*************************************************************************
    //
    // Analyze columns
    //
    //*************************************************************************
    void RowTableAnalyzer::AnalyzeColumns(char const * outDir) const
    {
        auto & ingestor = m_index.GetIngestor();

        auto fileSystem = Factories::CreateFileSystem();
        auto outFileManager =
            Factories::CreateFileManager(outDir,
                                         outDir,
                                         outDir,
                                         *fileSystem);

        auto summaryOut = outFileManager->ColumnDensitySummary().OpenForWrite();

        // TODO: Consider using CsvTsv::CsvTableFormatter::WritePrologue() here.
        *summaryOut << "shard,rank,mean,min,max,var,count" << std::endl;

        std::array<Accumulator, c_maxRankValue+1> accumulators;

        for (auto shardId = 0; shardId < ingestor.GetShardCount(); ++shardId)
        {
            IShard& shard = ingestor.GetShard(shardId);

            // Write out each document's raw column data per shard.
            // No need to use CsvTableFormatter for escaping because all values
            // are numbers.
            auto out = outFileManager->ColumnDensities(shardId).OpenForWrite();
            Column::WriteHeader(*out);

            auto itPtr = shard.GetIterator();
            auto & it = *itPtr;

            for (; !it.AtEnd(); ++it)
            {
                const DocumentHandleInternal handle(*it);
                const DocId docId = handle.GetDocId();
                Slice const & slice = handle.GetSlice();

                // TODO: handle.GetPostingCount() instead of 0
                Column column(docId, shardId, 0);

                void const * buffer = slice.GetSliceBuffer();
                const DocIndex docIndex = handle.GetIndex();

                for (Rank rank = 0; rank <= c_maxRankValue; ++rank)
                {
                    RowTableDescriptor rowTable = slice.GetRowTable(rank);

                    size_t bitCount = 0;
                    const size_t rowCount = rowTable.GetRowCount();
                    for (RowIndex row = 0; row < rowCount; ++row)
                    {
                        if (rowTable.GetBit(buffer, row, docIndex) != 0)
                        {
                            ++bitCount;
                        }
                    }

                    column.SetCount(rank, bitCount);

                    double density =
                        (rowCount == 0) ? 0.0 : static_cast<double>(bitCount) / rowCount;
                    column.SetDensity(rank, density);
                    accumulators[rank].Record(density);
                }

                column.Write(*out);
            }

            //
            // Generate document summary by rank for shard
            //
            for (Rank rank = 0; rank < c_maxRankValue; ++rank)
            {
                Accumulator accumulator = accumulators[rank];
                if (accumulator.GetCount() == 0)
                    continue;

                *summaryOut
                    << shardId << ","
                    << rank << ","
                    << accumulator.GetMean() << ","
                    << accumulator.GetMin() << ","
                    << accumulator.GetMax() << ","
                    << accumulator.GetVariance() << ","
                    << accumulator.GetCount() << std::endl;

                accumulators[rank].Reset();
            }
        }
    }

    RowTableAnalyzer::Column::Column(DocId id, ShardId shard, size_t postings)
      : m_id(id),
        m_postings(postings),
        m_shard(shard),
        m_bits{}
    {
    }

    void RowTableAnalyzer::Column::SetCount(Rank rank, size_t count)
    {
        m_bits[rank] = count;
    }

    void RowTableAnalyzer::Column::SetDensity(Rank rank, double density)
    {
        m_densities[rank] = density;
    }

    void RowTableAnalyzer::Column::WriteHeader(std::ostream& out)
    {
        out << "id,postings,shard";

        for (Rank rank = 0; rank <= c_maxRankValue; ++rank)
        {
            out << ",rank" << rank;
        }

        out << std::endl;
    }

    void RowTableAnalyzer::Column::Write(std::ostream& out)
    {
        out << m_id
            << "," << m_postings
            << "," << m_shard;

        for (Rank rank = 0; rank <= c_maxRankValue; ++rank)
        {
            // TODO: Consider writing out or eliminating m_bits.
            out << "," << m_densities[rank];
        }

        out << std::endl;
    }
}
<commit_msg>Fix Linux signed/unsigned comparison mismatch<commit_after>// The MIT License (MIT)

// Copyright (c) 2016, Microsoft

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <ostream>
#include <stack>

#include "BitFunnel/Configuration/Factories.h"
#include "BitFunnel/Configuration/IFileSystem.h"
#include "BitFunnel/Index/DocumentHandle.h"
#include "BitFunnel/Index/Factories.h"
#include "BitFunnel/Index/IDocument.h"
#include "BitFunnel/Index/IDocumentCache.h"
#include "BitFunnel/Index/IDocumentFrequencyTable.h"
#include "BitFunnel/Index/IIngestor.h"
#include "BitFunnel/Index/ISimpleIndex.h"
#include "BitFunnel/Index/RowIdSequence.h"
#include "CsvTsv/Csv.h"
#include "DocumentHandleInternal.h"
#include "LoggerInterfaces/Check.h"
#include "RowTableAnalyzer.h"
#include "RowTableDescriptor.h"
#include "Shard.h"
#include "TermToText.h"

namespace BitFunnel
{
    void Factories::AnalyzeRowTables(ISimpleIndex const & index,
                                     char const * outDir)
    {
        char const end = '\0';     // TODO: Workaround for issue #386.
        CHECK_NE(*outDir, end)
          << "Output directory not set. ";

        RowTableAnalyzer statistics(index);
        statistics.AnalyzeColumns(outDir);
        statistics.AnalyzeRows(outDir);
    }

    RowTableAnalyzer::RowTableAnalyzer(ISimpleIndex const & index)
        : m_index(index)
    {
    }

    //*************************************************************************
    //
    // Row Statistics
    //
    //*************************************************************************
    class RowStatistics
    {
    public:
        static void WriteHeader(std::ostream& out)
        {
            CsvTsv::CsvTableFormatter formatter(out);
            formatter.WritePrologue(std::vector<char const *>{
                "shard", "idfX10", "rank", "mean", "min", "max", "var", "count"});
        }

        void WriteSummary(std::ostream& out,
                          ShardId const & shardId,
                          Term::IdfX10 idfX10) const
        {
            for (Rank rank = 0; rank < c_maxRankValue; ++rank)
            {
                Accumulator const & accumulator = m_accumulators[rank];
                if (accumulator.GetCount() == 0)
                    continue;

                out << shardId << ","
                    << static_cast<uint32_t>(idfX10) << ","
                    << rank << ","
                    << accumulator.GetMean() << ","
                    << accumulator.GetMin() << ","
                    << accumulator.GetMax() << ","
                    << accumulator.GetVariance() << ","
                    << accumulator.GetCount() << std::endl;
            }
        }

        void RecordDensity(Rank rank, double density)
        {
            m_accumulators.at(rank).Record(density);
        }

        void Reset()
        {
            for (auto it = m_accumulators.begin(); it != m_accumulators.end(); ++it)
            {
                it->Reset();
            }
        }

    private:
        std::array<Accumulator, c_maxRankValue> m_accumulators;
    };

    //*************************************************************************
    //
    // Analyze rows
    //
    //*************************************************************************
    void RowTableAnalyzer::AnalyzeRows(char const * outDir) const
    {
        //
        // Gather row statistics for ingested documents.
        // (documents need not be cached)
        //
        auto & fileManager = m_index.GetFileManager();
        auto & ingestor = m_index.GetIngestor();

        // Obtain data for converting a term hash to text, if file exists
        std::unique_ptr<ITermToText> termToText;
        try
        {
            // Will fail with exception if file does not exist or can't be read
            // TODO: Create with factory?
            termToText = Factories::CreateTermToText(*fileManager.TermToText().OpenForRead());
        }
        catch (RecoverableError e)
        {
            termToText = Factories::CreateTermToText();
        }

        auto fileSystem = Factories::CreateFileSystem();
        auto outFileManager =
            Factories::CreateFileManager(outDir,
                outDir,
                outDir,
                *fileSystem);

        auto summaryOut = outFileManager->RowDensitySummary().OpenForWrite();
        RowStatistics::WriteHeader(*summaryOut);

        for (ShardId shardId = 0; shardId < ingestor.GetShardCount(); ++shardId)
        {
            AnalyzeRowsInOneShard(shardId,
                                  *termToText,
                                  *outFileManager->RowDensities(shardId).OpenForWrite(),
                                  *summaryOut);
        }
    }

    void RowTableAnalyzer::AnalyzeRowsInOneShard(
        ShardId const & shardId,
        ITermToText const & termToText,
        std::ostream& out,
        std::ostream& summaryOut) const
    {
        auto & shard = m_index.GetIngestor().GetShard(shardId);
        std::array<std::vector<double>, c_maxRankValue+1> densities;

        for (Rank rank = 0; rank <= c_maxRankValue; ++rank)
        {
            densities[rank] = shard.GetDensities(rank);
        }

        RowStatistics statistics;
        Term::IdfX10 curIdfX10 = 0;

        // Use CsvTableFormatter to escape terms that contain commas and quotes.
        CsvTsv::CsvTableFormatter formatter(out);
        auto & fileManager = m_index.GetFileManager();
        auto terms(Factories::CreateDocumentFrequencyTable(
            *fileManager.DocFreqTable(shardId).OpenForRead()));

        for (auto dfEntry : *terms)
        {
            Term term = dfEntry.GetTerm();
            Term::IdfX10 idfX10 = Term::ComputeIdfX10(dfEntry.GetFrequency(), Term::c_maxIdfX10Value);

            // When we cross an idfX10 bucket boundary, output statistics
            // accumulated since previous idfX10 bucket. Then reset
            // statistics accumulators for the next bucket.
            if (idfX10 != curIdfX10)
            {
                curIdfX10 = idfX10;
                statistics.WriteSummary(summaryOut, shardId, curIdfX10);
                statistics.Reset();
            }

            RowIdSequence rows(term, m_index.GetTermTable(shardId));

            std::string termName = termToText.Lookup(term.GetRawHash());
            if (!termName.empty())
            {
                // Print out the term text if we have it.
                formatter.WriteField(termName);
            }
            else
            {
                // Otherwise print out the term's hash.
                formatter.SetHexMode(1);
                formatter.WriteField(term.GetRawHash());
                formatter.SetHexMode(0);
            }

            formatter.WriteField(dfEntry.GetFrequency());

            std::stack<RowId> rowsReversed;
            for (auto row : rows)
            {
                rowsReversed.push(row);
            }

            while (!rowsReversed.empty())
            {
                auto row = rowsReversed.top();
                auto rank = row.GetRank();
                auto index = row.GetIndex();
                auto density = densities.at(rank).at(index);
                statistics.RecordDensity(rank, density);

                formatter.WriteField("r");
                out << row.GetRank();
                formatter.WriteField(index);
                formatter.WriteField(density);

                rowsReversed.pop();
            }
            formatter.WriteRowEnd();
        }
        statistics.WriteSummary(summaryOut, shardId, curIdfX10);
    }

    //*************************************************************************
    //
    // Analyze columns
    //
    //*************************************************************************
    void RowTableAnalyzer::AnalyzeColumns(char const * outDir) const
    {
        auto & ingestor = m_index.GetIngestor();

        auto fileSystem = Factories::CreateFileSystem();
        auto outFileManager =
            Factories::CreateFileManager(outDir,
                                         outDir,
                                         outDir,
                                         *fileSystem);

        auto summaryOut = outFileManager->ColumnDensitySummary().OpenForWrite();

        // TODO: Consider using CsvTsv::CsvTableFormatter::WritePrologue() here.
        *summaryOut << "shard,rank,mean,min,max,var,count" << std::endl;

        std::array<Accumulator, c_maxRankValue+1> accumulators;

        for (ShardId shardId = 0; shardId < ingestor.GetShardCount(); ++shardId)
        {
            IShard& shard = ingestor.GetShard(shardId);

            // Write out each document's raw column data per shard.
            // No need to use CsvTableFormatter for escaping because all values
            // are numbers.
            auto out = outFileManager->ColumnDensities(shardId).OpenForWrite();
            Column::WriteHeader(*out);

            auto itPtr = shard.GetIterator();
            auto & it = *itPtr;

            for (; !it.AtEnd(); ++it)
            {
                const DocumentHandleInternal handle(*it);
                const DocId docId = handle.GetDocId();
                Slice const & slice = handle.GetSlice();

                // TODO: handle.GetPostingCount() instead of 0
                Column column(docId, shardId, 0);

                void const * buffer = slice.GetSliceBuffer();
                const DocIndex docIndex = handle.GetIndex();

                for (Rank rank = 0; rank <= c_maxRankValue; ++rank)
                {
                    RowTableDescriptor rowTable = slice.GetRowTable(rank);

                    size_t bitCount = 0;
                    const size_t rowCount = rowTable.GetRowCount();
                    for (RowIndex row = 0; row < rowCount; ++row)
                    {
                        if (rowTable.GetBit(buffer, row, docIndex) != 0)
                        {
                            ++bitCount;
                        }
                    }

                    column.SetCount(rank, bitCount);

                    double density =
                        (rowCount == 0) ? 0.0 : static_cast<double>(bitCount) / rowCount;
                    column.SetDensity(rank, density);
                    accumulators[rank].Record(density);
                }

                column.Write(*out);
            }

            //
            // Generate document summary by rank for shard
            //
            for (Rank rank = 0; rank < c_maxRankValue; ++rank)
            {
                Accumulator accumulator = accumulators[rank];
                if (accumulator.GetCount() == 0)
                    continue;

                *summaryOut
                    << shardId << ","
                    << rank << ","
                    << accumulator.GetMean() << ","
                    << accumulator.GetMin() << ","
                    << accumulator.GetMax() << ","
                    << accumulator.GetVariance() << ","
                    << accumulator.GetCount() << std::endl;

                accumulators[rank].Reset();
            }
        }
    }

    RowTableAnalyzer::Column::Column(DocId id, ShardId shard, size_t postings)
      : m_id(id),
        m_postings(postings),
        m_shard(shard),
        m_bits{}
    {
    }

    void RowTableAnalyzer::Column::SetCount(Rank rank, size_t count)
    {
        m_bits[rank] = count;
    }

    void RowTableAnalyzer::Column::SetDensity(Rank rank, double density)
    {
        m_densities[rank] = density;
    }

    void RowTableAnalyzer::Column::WriteHeader(std::ostream& out)
    {
        out << "id,postings,shard";

        for (Rank rank = 0; rank <= c_maxRankValue; ++rank)
        {
            out << ",rank" << rank;
        }

        out << std::endl;
    }

    void RowTableAnalyzer::Column::Write(std::ostream& out)
    {
        out << m_id
            << "," << m_postings
            << "," << m_shard;

        for (Rank rank = 0; rank <= c_maxRankValue; ++rank)
        {
            // TODO: Consider writing out or eliminating m_bits.
            out << "," << m_densities[rank];
        }

        out << std::endl;
    }
}
<|endoftext|>