<commit_before>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2013 Torus Knot Software Ltd

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 "OgreTerrainPagedWorldSection.h"
#include "OgreTerrainGroup.h"
#include "OgreGrid2DPageStrategy.h"
#include "OgrePagedWorld.h"
#include "OgrePageManager.h"
#include "OgreRoot.h"

namespace Ogre
{
	const uint16 TerrainPagedWorldSection::WORKQUEUE_LOAD_TERRAIN_PAGE_REQUEST = 1;
	const uint64 TerrainPagedWorldSection::LOADING_TERRAIN_PAGE_INTERVAL_MS = 900;

	//---------------------------------------------------------------------
	TerrainPagedWorldSection::TerrainPagedWorldSection(const String& name, PagedWorld* parent, SceneManager* sm)
		: PagedWorldSection(name, parent, sm)
		, mTerrainGroup(0)
		, mTerrainDefiner(0)
		, mHasRunningTasks(false)
	{
		// we always use a grid strategy
		setStrategy(parent->getManager()->getStrategy("Grid2D"));

		WorkQueue* wq = Root::getSingleton().getWorkQueue();
		mWorkQueueChannel = wq->getChannel("Ogre/TerrainPagedWorldSection");
		wq->addRequestHandler(mWorkQueueChannel, this);
		wq->addResponseHandler(mWorkQueueChannel, this);

		mNextLoadingTime = Root::getSingletonPtr()->getTimer()->getMilliseconds();
	}
	//---------------------------------------------------------------------
	TerrainPagedWorldSection::~TerrainPagedWorldSection()
	{
		//remove the pending tasks, but keep the front one, as it may have been in running
		if(!mPagesInLoading.empty())
			mPagesInLoading.erase( ++mPagesInLoading.begin(), mPagesInLoading.end() );

		while(!mPagesInLoading.empty())
		{
			OGRE_THREAD_SLEEP(50);
			Root::getSingleton().getWorkQueue()->processResponses();
		}

		WorkQueue* wq = Root::getSingleton().getWorkQueue();
		wq->removeRequestHandler(mWorkQueueChannel, this);
		wq->removeResponseHandler(mWorkQueueChannel, this);

		OGRE_DELETE mTerrainGroup;
		if(mTerrainDefiner)
			OGRE_DELETE mTerrainDefiner;
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::init(TerrainGroup* grp)
	{
		if (mTerrainGroup == grp)
			return;

		if (mTerrainGroup)
			OGRE_DELETE mTerrainGroup;

		mTerrainGroup = grp;
		syncSettings();

		// Unload all existing terrain pages, because we want the paging system
		// to be in charge of this
		mTerrainGroup->removeAllTerrains();
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::syncSettings()
	{

		// Base grid on terrain settings
		Grid2DPageStrategyData* gridData = getGridStrategyData();
		switch (mTerrainGroup->getAlignment())
		{
		case Terrain::ALIGN_X_Y:
			gridData->setMode(G2D_X_Y);
			break;
		case Terrain::ALIGN_X_Z:
			gridData->setMode(G2D_X_Z);
			break;
		case Terrain::ALIGN_Y_Z:
			gridData->setMode(G2D_Y_Z);
			break;
		}
		gridData->setOrigin(mTerrainGroup->getOrigin());

		gridData->setCellSize(mTerrainGroup->getTerrainWorldSize());

	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setLoadRadius(Real sz)
	{
		getGridStrategyData()->setLoadRadius(sz);
	}
	//---------------------------------------------------------------------
	Real TerrainPagedWorldSection::getLoadRadius() const
	{
		return getGridStrategyData()->getLoadRadius();
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setHoldRadius(Real sz)
	{
		getGridStrategyData()->setHoldRadius(sz);
	}
	//---------------------------------------------------------------------
	Real TerrainPagedWorldSection::getHoldRadius()
	{
		return getGridStrategyData()->getHoldRadius();
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRange(int32 minX, int32 minY, int32 maxX, int32 maxY)
	{
		getGridStrategyData()->setCellRange(minX, minY, maxX, maxY);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRangeMinX(int32 minX)
	{
		getGridStrategyData()->setCellRangeMinX(minX);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRangeMinY(int32 minY)
	{
		getGridStrategyData()->setCellRangeMinY(minY);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRangeMaxX(int32 maxX)
	{
		getGridStrategyData()->setCellRangeMaxX(maxX);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRangeMaxY(int32 maxY)
	{
		getGridStrategyData()->setCellRangeMaxY(maxY);
	}
	//---------------------------------------------------------------------
	int32 TerrainPagedWorldSection::getPageRangeMinX() const
	{
		return getGridStrategyData()->getCellRangeMinX();
	}
	//---------------------------------------------------------------------
	int32 TerrainPagedWorldSection::getPageRangeMinY() const
	{
		return getGridStrategyData()->getCellRangeMinY();
	}
	//---------------------------------------------------------------------
	int32 TerrainPagedWorldSection::getPageRangeMaxX() const
	{
		return getGridStrategyData()->getCellRangeMaxX();
	}
	//---------------------------------------------------------------------
	int32 TerrainPagedWorldSection::getPageRangeMaxY() const
	{
		return getGridStrategyData()->getCellRangeMaxY();
	}
	//---------------------------------------------------------------------
	Grid2DPageStrategy* TerrainPagedWorldSection::getGridStrategy() const
	{
		return static_cast<Grid2DPageStrategy*>(this->getStrategy());
	}
	//---------------------------------------------------------------------
	Grid2DPageStrategyData* TerrainPagedWorldSection::getGridStrategyData() const
	{
		return static_cast<Grid2DPageStrategyData*>(mStrategyData);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::loadSubtypeData(StreamSerialiser& ser)
	{
		// we load the TerrainGroup information from here
		if (!mTerrainGroup)
			mTerrainGroup = OGRE_NEW TerrainGroup(getSceneManager());

		mTerrainGroup->loadGroupDefinition(ser);

		// params that are in the Grid2DStrategyData will have already been loaded
		// as part of the main load() routine

		syncSettings();
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::saveSubtypeData(StreamSerialiser& ser)
	{
		mTerrainGroup->saveGroupDefinition(ser);

		// params that are in the Grid2DStrategyData will have already been saved
		// as part of the main save() routine

	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::loadPage(PageID pageID, bool forceSynchronous)
	{
		if (!mParent->getManager()->getPagingOperationsEnabled())
			return;

		PageMap::iterator i = mPages.find(pageID);
		if (i == mPages.end())
		{
			std::list<PageID>::iterator it = find( mPagesInLoading.begin(), mPagesInLoading.end(), pageID);
			if(it==mPagesInLoading.end())
			{
				mPagesInLoading.push_back(pageID);
				mHasRunningTasks = true;
			}

			// no running tasks, start the new one
			if(mPagesInLoading.size()==1)
			{
				Root::getSingleton().getWorkQueue()->addRequest(
					mWorkQueueChannel, WORKQUEUE_LOAD_TERRAIN_PAGE_REQUEST,
					Any(), 0, forceSynchronous);
			}
		}

		PagedWorldSection::loadPage(pageID, forceSynchronous);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::unloadPage(PageID pageID, bool forceSynchronous)
	{
		if (!mParent->getManager()->getPagingOperationsEnabled())
			return;

		PagedWorldSection::unloadPage(pageID, forceSynchronous);

		std::list<PageID>::iterator it = find( mPagesInLoading.begin(), mPagesInLoading.end(), pageID);
		// hasn't been loaded, just remove from the queue
		if(it!=mPagesInLoading.end())
		{
			mPagesInLoading.erase(it);
		}
		else
		{
			// trigger terrain unload
			long x, y;
			// pageID is the same as a packed index
			mTerrainGroup->unpackIndex(pageID, &x, &y);
			mTerrainGroup->unloadTerrain(x, y);
		}
	}
	//---------------------------------------------------------------------
	WorkQueue::Response* TerrainPagedWorldSection::handleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ)
	{
		return OGRE_NEW WorkQueue::Response(req, true, Any());
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ)
	{
		if(mPagesInLoading.empty())
		{
			mHasRunningTasks = false;
			return;
		}

		unsigned long currentTime = Root::getSingletonPtr()->getTimer()->getMilliseconds();
		if(currentTime>mNextLoadingTime)
		{
			PageID pageID = mPagesInLoading.front();

			// trigger terrain load
			long x, y;
			// pageID is the same as a packed index
			mTerrainGroup->unpackIndex(pageID, &x, &y);

			if(!mTerrainDefiner)
				mTerrainDefiner = OGRE_NEW TerrainDefiner();
			mTerrainDefiner->define(mTerrainGroup,x,y);

			mTerrainGroup->loadTerrain(x, y, false);

			mPagesInLoading.pop_front();
			mNextLoadingTime = currentTime + LOADING_TERRAIN_PAGE_INTERVAL_MS;
		}

		if(!mPagesInLoading.empty())
		{
			Root::getSingleton().getWorkQueue()->addRequest(
				mWorkQueueChannel, WORKQUEUE_LOAD_TERRAIN_PAGE_REQUEST,
				Any(), 0, false);
		}
		else
			mHasRunningTasks = false;
	}
}

<commit_msg>Terrain-Paging: Call TerrainDefiner from background thread<commit_after>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2013 Torus Knot Software Ltd

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 "OgreTerrainPagedWorldSection.h"
#include "OgreTerrainGroup.h"
#include "OgreGrid2DPageStrategy.h"
#include "OgrePagedWorld.h"
#include "OgrePageManager.h"
#include "OgreRoot.h"

namespace Ogre
{
	const uint16 TerrainPagedWorldSection::WORKQUEUE_LOAD_TERRAIN_PAGE_REQUEST = 1;
	const uint64 TerrainPagedWorldSection::LOADING_TERRAIN_PAGE_INTERVAL_MS = 900;

	//---------------------------------------------------------------------
	TerrainPagedWorldSection::TerrainPagedWorldSection(const String& name, PagedWorld* parent, SceneManager* sm)
		: PagedWorldSection(name, parent, sm)
		, mTerrainGroup(0)
		, mTerrainDefiner(0)
		, mHasRunningTasks(false)
	{
		// we always use a grid strategy
		setStrategy(parent->getManager()->getStrategy("Grid2D"));

		WorkQueue* wq = Root::getSingleton().getWorkQueue();
		mWorkQueueChannel = wq->getChannel("Ogre/TerrainPagedWorldSection");
		wq->addRequestHandler(mWorkQueueChannel, this);
		wq->addResponseHandler(mWorkQueueChannel, this);

		mNextLoadingTime = Root::getSingletonPtr()->getTimer()->getMilliseconds();
	}
	//---------------------------------------------------------------------
	TerrainPagedWorldSection::~TerrainPagedWorldSection()
	{
		//remove the pending tasks, but keep the front one, as it may have been in running
		if(!mPagesInLoading.empty())
			mPagesInLoading.erase( ++mPagesInLoading.begin(), mPagesInLoading.end() );

		while(!mPagesInLoading.empty())
		{
			OGRE_THREAD_SLEEP(50);
			Root::getSingleton().getWorkQueue()->processResponses();
		}

		WorkQueue* wq = Root::getSingleton().getWorkQueue();
		wq->removeRequestHandler(mWorkQueueChannel, this);
		wq->removeResponseHandler(mWorkQueueChannel, this);

		OGRE_DELETE mTerrainGroup;
		if(mTerrainDefiner)
			OGRE_DELETE mTerrainDefiner;
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::init(TerrainGroup* grp)
	{
		if (mTerrainGroup == grp)
			return;

		if (mTerrainGroup)
			OGRE_DELETE mTerrainGroup;

		mTerrainGroup = grp;
		syncSettings();

		// Unload all existing terrain pages, because we want the paging system
		// to be in charge of this
		mTerrainGroup->removeAllTerrains();
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::syncSettings()
	{

		// Base grid on terrain settings
		Grid2DPageStrategyData* gridData = getGridStrategyData();
		switch (mTerrainGroup->getAlignment())
		{
		case Terrain::ALIGN_X_Y:
			gridData->setMode(G2D_X_Y);
			break;
		case Terrain::ALIGN_X_Z:
			gridData->setMode(G2D_X_Z);
			break;
		case Terrain::ALIGN_Y_Z:
			gridData->setMode(G2D_Y_Z);
			break;
		}
		gridData->setOrigin(mTerrainGroup->getOrigin());

		gridData->setCellSize(mTerrainGroup->getTerrainWorldSize());

	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setLoadRadius(Real sz)
	{
		getGridStrategyData()->setLoadRadius(sz);
	}
	//---------------------------------------------------------------------
	Real TerrainPagedWorldSection::getLoadRadius() const
	{
		return getGridStrategyData()->getLoadRadius();
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setHoldRadius(Real sz)
	{
		getGridStrategyData()->setHoldRadius(sz);
	}
	//---------------------------------------------------------------------
	Real TerrainPagedWorldSection::getHoldRadius()
	{
		return getGridStrategyData()->getHoldRadius();
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRange(int32 minX, int32 minY, int32 maxX, int32 maxY)
	{
		getGridStrategyData()->setCellRange(minX, minY, maxX, maxY);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRangeMinX(int32 minX)
	{
		getGridStrategyData()->setCellRangeMinX(minX);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRangeMinY(int32 minY)
	{
		getGridStrategyData()->setCellRangeMinY(minY);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRangeMaxX(int32 maxX)
	{
		getGridStrategyData()->setCellRangeMaxX(maxX);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::setPageRangeMaxY(int32 maxY)
	{
		getGridStrategyData()->setCellRangeMaxY(maxY);
	}
	//---------------------------------------------------------------------
	int32 TerrainPagedWorldSection::getPageRangeMinX() const
	{
		return getGridStrategyData()->getCellRangeMinX();
	}
	//---------------------------------------------------------------------
	int32 TerrainPagedWorldSection::getPageRangeMinY() const
	{
		return getGridStrategyData()->getCellRangeMinY();
	}
	//---------------------------------------------------------------------
	int32 TerrainPagedWorldSection::getPageRangeMaxX() const
	{
		return getGridStrategyData()->getCellRangeMaxX();
	}
	//---------------------------------------------------------------------
	int32 TerrainPagedWorldSection::getPageRangeMaxY() const
	{
		return getGridStrategyData()->getCellRangeMaxY();
	}
	//---------------------------------------------------------------------
	Grid2DPageStrategy* TerrainPagedWorldSection::getGridStrategy() const
	{
		return static_cast<Grid2DPageStrategy*>(this->getStrategy());
	}
	//---------------------------------------------------------------------
	Grid2DPageStrategyData* TerrainPagedWorldSection::getGridStrategyData() const
	{
		return static_cast<Grid2DPageStrategyData*>(mStrategyData);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::loadSubtypeData(StreamSerialiser& ser)
	{
		// we load the TerrainGroup information from here
		if (!mTerrainGroup)
			mTerrainGroup = OGRE_NEW TerrainGroup(getSceneManager());

		mTerrainGroup->loadGroupDefinition(ser);

		// params that are in the Grid2DStrategyData will have already been loaded
		// as part of the main load() routine

		syncSettings();
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::saveSubtypeData(StreamSerialiser& ser)
	{
		mTerrainGroup->saveGroupDefinition(ser);

		// params that are in the Grid2DStrategyData will have already been saved
		// as part of the main save() routine

	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::loadPage(PageID pageID, bool forceSynchronous)
	{
		if (!mParent->getManager()->getPagingOperationsEnabled())
			return;

		PageMap::iterator i = mPages.find(pageID);
		if (i == mPages.end())
		{
			std::list<PageID>::iterator it = find( mPagesInLoading.begin(), mPagesInLoading.end(), pageID);
			if(it==mPagesInLoading.end())
			{
				mPagesInLoading.push_back(pageID);
				mHasRunningTasks = true;
			}

			// no running tasks, start the new one
			if(mPagesInLoading.size()==1)
			{
				Root::getSingleton().getWorkQueue()->addRequest(
					mWorkQueueChannel, WORKQUEUE_LOAD_TERRAIN_PAGE_REQUEST,
					Any(), 0, forceSynchronous);
			}
		}

		PagedWorldSection::loadPage(pageID, forceSynchronous);
	}
	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::unloadPage(PageID pageID, bool forceSynchronous)
	{
		if (!mParent->getManager()->getPagingOperationsEnabled())
			return;

		PagedWorldSection::unloadPage(pageID, forceSynchronous);

		std::list<PageID>::iterator it = find( mPagesInLoading.begin(), mPagesInLoading.end(), pageID);
		// hasn't been loaded, just remove from the queue
		if(it!=mPagesInLoading.end())
		{
			mPagesInLoading.erase(it);
		}
		else
		{
			// trigger terrain unload
			long x, y;
			// pageID is the same as a packed index
			mTerrainGroup->unpackIndex(pageID, &x, &y);
			mTerrainGroup->unloadTerrain(x, y);
		}
	}
	//---------------------------------------------------------------------
	WorkQueue::Response* TerrainPagedWorldSection::handleRequest(const WorkQueue::Request* req, const WorkQueue* srcQ)
	{
		if(mPagesInLoading.empty())
		{
			mHasRunningTasks = false;
			req->abortRequest();
			return OGRE_NEW WorkQueue::Response(req, true, Any());
		}

		unsigned long currentTime = Root::getSingletonPtr()->getTimer()->getMilliseconds();
		if(currentTime < mNextLoadingTime)
		{
			// Wait until the next page is to be loaded -> we are in background thread here
			OGRE_THREAD_SLEEP(mNextLoadingTime - currentTime);
		}

		PageID pageID = mPagesInLoading.front();

		// call the TerrainDefiner from the background thread
		long x, y;
		// pageID is the same as a packed index
		mTerrainGroup->unpackIndex(pageID, &x, &y);

		if(!mTerrainDefiner)
			mTerrainDefiner = OGRE_NEW TerrainDefiner();
		mTerrainDefiner->define(mTerrainGroup, x, y);

		// continue loading in main thread
		return OGRE_NEW WorkQueue::Response(req, true, Any());
	}

	//---------------------------------------------------------------------
	void TerrainPagedWorldSection::handleResponse(const WorkQueue::Response* res, const WorkQueue* srcQ)
	{
		PageID pageID = mPagesInLoading.front();

		// trigger terrain load
		long x, y;
		// pageID is the same as a packed index
		mTerrainGroup->unpackIndex(pageID, &x, &y);
		mTerrainGroup->loadTerrain(x, y, false);
		mPagesInLoading.pop_front();

		unsigned long currentTime = Root::getSingletonPtr()->getTimer()->getMilliseconds();
		mNextLoadingTime = currentTime + LOADING_TERRAIN_PAGE_INTERVAL_MS;

		if(!mPagesInLoading.empty())
		{
			// Continue loading other pages
			Root::getSingleton().getWorkQueue()->addRequest(
					mWorkQueueChannel, WORKQUEUE_LOAD_TERRAIN_PAGE_REQUEST,
					Any(), 0, false);
		}
		else
			mHasRunningTasks = false;
	}
}

<|endoftext|>


<commit_before>#include <thread>
#include <mutex>
#include <chrono>
#include <set>
#include <condition_variable>
#include <gtest/gtest.h>
#include "cppkafka/producer.h"
#include "cppkafka/consumer.h"
#include "cppkafka/utils/buffered_producer.h"
#include "test_utils.h"

using std::string;
using std::to_string;
using std::set;
using std::tie;
using std::move;
using std::thread;
using std::mutex;
using std::unique_lock;
using std::lock_guard;
using std::condition_variable;

using std::chrono::system_clock;
using std::chrono::seconds;
using std::chrono::milliseconds;

using namespace cppkafka;

class ProducerTest : public testing::Test {
public:
    static const string KAFKA_TOPIC;

    Configuration make_producer_config() {
        Configuration config = {
            { "metadata.broker.list", KAFKA_TEST_INSTANCE },
            { "queue.buffering.max.ms", 0 }
        };
        return config;
    }

    Configuration make_consumer_config() {
        Configuration config = {
            { "metadata.broker.list", KAFKA_TEST_INSTANCE },
            { "enable.auto.commit", false },
            { "group.id", "producer_test" }
        };
        return config;
    }
};

const string ProducerTest::KAFKA_TOPIC = "cppkafka_test1";

TEST_F(ProducerTest, OneMessageOnFixedPartition) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 1, 1);

    // Now create a producer and produce a message
    Producer producer(make_producer_config());
    string payload = "Hello world! 1";
    producer.produce(MessageBuilder(KAFKA_TOPIC).partition(partition).payload(payload));
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(1, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(Buffer(payload), message.get_payload());
    EXPECT_FALSE(message.get_key());
    EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_FALSE(message.get_error());

    int64_t low;
    int64_t high;
    tie(low, high) = producer.query_offsets({ KAFKA_TOPIC, partition });
    EXPECT_GT(high, low);
}

TEST_F(ProducerTest, OneMessageUsingKey) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 1, 1);

    // Now create a producer and produce a message
    Producer producer(make_producer_config());
    string payload = "Hello world! 2";
    string key = "such key";
    producer.produce(MessageBuilder(KAFKA_TOPIC).partition(partition).key(key).payload(payload));
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(1, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(Buffer(payload), message.get_payload());
    EXPECT_EQ(Buffer(key), message.get_key());
    EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_FALSE(message.get_error());
    // NOTE: if this line fails, then you're using kafka 0.10+ and that's okay
    EXPECT_FALSE(message.get_timestamp());
}

TEST_F(ProducerTest, MultipleMessagesUnassignedPartitions) {
    size_t message_count = 10;
    int partitions = 3;
    set<string> payloads;

    // Create a consumer and subscribe to this topic
    Consumer consumer(make_consumer_config());
    consumer.subscribe({ KAFKA_TOPIC });
    ConsumerRunner runner(consumer, message_count, partitions);

    // Now create a producer and produce a message
    Producer producer(make_producer_config());
    string payload_base = "Hello world ";
    for (size_t i = 0; i < message_count; ++i) {
        string payload = payload_base + to_string(i);
        payloads.insert(payload);
        producer.produce(MessageBuilder(KAFKA_TOPIC).payload(payload));
    }
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(message_count, messages.size());
    for (const auto& message : messages) {
        EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
        EXPECT_EQ(1, payloads.erase(message.get_payload()));
        EXPECT_FALSE(message.get_error());
        EXPECT_FALSE(message.get_key());
        EXPECT_GE(message.get_partition(), 0);
        EXPECT_LT(message.get_partition(), 3);
    }
}

TEST_F(ProducerTest, Callbacks) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 1, 1);

    // Now create a producer and produce a message
    string payload = "Hello world! 3";
    string key = "hehe";
    bool delivery_report_called = false;
    Configuration config = make_producer_config();
    config.set_delivery_report_callback([&](Producer&, const Message& msg) {
        EXPECT_EQ(Buffer(payload), msg.get_payload());
        delivery_report_called = true;
    });

    TopicConfiguration topic_config;
    topic_config.set_partitioner_callback([&](const Topic& topic, const Buffer& msg_key,
                                              int32_t partition_count) {
        EXPECT_EQ(Buffer(key), msg_key);
        EXPECT_EQ(3, partition_count);
        EXPECT_EQ(KAFKA_TOPIC, topic.get_name());
        return 0;
    });
    config.set_default_topic_configuration(topic_config);

    Producer producer(move(config));
    producer.produce(MessageBuilder(KAFKA_TOPIC).key(key).payload(payload));
    producer.poll();
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(1, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(Buffer(payload), message.get_payload());
    EXPECT_EQ(Buffer(key), message.get_key());
    EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_FALSE(message.get_error());
    EXPECT_TRUE(delivery_report_called);
}

TEST_F(ProducerTest, PartitionerCallbackOnDefaultTopicConfig) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 1, 1);

    // Now create a producer and produce a message
    string payload = "Hello world! 4";
    string key = "hehe";
    bool callback_called = false;

    Configuration config = make_producer_config();
    TopicConfiguration topic_config;
    topic_config.set_partitioner_callback([&](const Topic& topic, const Buffer& msg_key,
                                              int32_t partition_count) {
        EXPECT_EQ(Buffer(key), msg_key);
        EXPECT_EQ(3, partition_count);
        EXPECT_EQ(KAFKA_TOPIC, topic.get_name());
        callback_called = true;
        return 0;
    });
    config.set_default_topic_configuration(topic_config);

    Producer producer(move(config));
    producer.produce(MessageBuilder(KAFKA_TOPIC).key(key).payload(payload));
    producer.poll();
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(1, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_TRUE(callback_called);
}

TEST_F(ProducerTest, BufferedProducer) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 3, 1);

    // Now create a buffered producer and produce two messages
    BufferedProducer<string> producer(make_producer_config());
    string payload = "Hello world! 2";
    string key = "such key";
    producer.add_message(MessageBuilder(KAFKA_TOPIC).partition(partition)
                                                    .key(key)
                                                    .payload(payload));
    producer.add_message(producer.make_builder(KAFKA_TOPIC).partition(partition).payload(payload));
    producer.flush();
    producer.produce(MessageBuilder(KAFKA_TOPIC).partition(partition).payload(payload));
    producer.wait_for_acks();
    // Add another one but then clear it
    producer.add_message(producer.make_builder(KAFKA_TOPIC).partition(partition).payload(payload));
    producer.clear();
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(3, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(Buffer(key), message.get_key());
    EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_FALSE(message.get_error());

    EXPECT_FALSE(messages[1].get_key());
    EXPECT_FALSE(messages[2].get_key());
    for (const auto& message : messages) {
        EXPECT_EQ(Buffer(payload), message.get_payload());
    }
}
<commit_msg>Assume testing kafka cluster is >= 0.10<commit_after>#include <thread>
#include <mutex>
#include <chrono>
#include <set>
#include <condition_variable>
#include <gtest/gtest.h>
#include "cppkafka/producer.h"
#include "cppkafka/consumer.h"
#include "cppkafka/utils/buffered_producer.h"
#include "test_utils.h"

using std::string;
using std::to_string;
using std::set;
using std::tie;
using std::move;
using std::thread;
using std::mutex;
using std::unique_lock;
using std::lock_guard;
using std::condition_variable;

using std::chrono::system_clock;
using std::chrono::seconds;
using std::chrono::milliseconds;

using namespace cppkafka;

class ProducerTest : public testing::Test {
public:
    static const string KAFKA_TOPIC;

    Configuration make_producer_config() {
        Configuration config = {
            { "metadata.broker.list", KAFKA_TEST_INSTANCE },
            { "queue.buffering.max.ms", 0 },
            { "api.version.request", true },
            { "queue.buffering.max.ms", 50 }
        };
        return config;
    }

    Configuration make_consumer_config() {
        Configuration config = {
            { "metadata.broker.list", KAFKA_TEST_INSTANCE },
            { "enable.auto.commit", false },
            { "group.id", "producer_test" },
            { "api.version.request", true }
        };
        return config;
    }
};

const string ProducerTest::KAFKA_TOPIC = "cppkafka_test1";

TEST_F(ProducerTest, OneMessageOnFixedPartition) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 1, 1);

    // Now create a producer and produce a message
    Producer producer(make_producer_config());
    string payload = "Hello world! 1";
    producer.produce(MessageBuilder(KAFKA_TOPIC).partition(partition).payload(payload));
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(1, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(Buffer(payload), message.get_payload());
    EXPECT_FALSE(message.get_key());
    EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_FALSE(message.get_error());

    int64_t low;
    int64_t high;
    tie(low, high) = producer.query_offsets({ KAFKA_TOPIC, partition });
    EXPECT_GT(high, low);
}

TEST_F(ProducerTest, OneMessageUsingKey) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 1, 1);

    // Now create a producer and produce a message
    Producer producer(make_producer_config());
    string payload = "Hello world! 2";
    string key = "such key";
    const milliseconds timestamp{15};
    producer.produce(MessageBuilder(KAFKA_TOPIC).partition(partition)
                                                 .key(key)
                                                 .payload(payload)
                                                 .timestamp(timestamp));
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(1, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(Buffer(payload), message.get_payload());
    EXPECT_EQ(Buffer(key), message.get_key());
    EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_FALSE(message.get_error());
    EXPECT_TRUE(message.get_timestamp());
    EXPECT_EQ(timestamp, message.get_timestamp()->get_timestamp());
}

TEST_F(ProducerTest, MultipleMessagesUnassignedPartitions) {
    size_t message_count = 10;
    int partitions = 3;
    set<string> payloads;

    // Create a consumer and subscribe to this topic
    Consumer consumer(make_consumer_config());
    consumer.subscribe({ KAFKA_TOPIC });
    ConsumerRunner runner(consumer, message_count, partitions);

    // Now create a producer and produce a message
    Producer producer(make_producer_config());
    string payload_base = "Hello world ";
    for (size_t i = 0; i < message_count; ++i) {
        string payload = payload_base + to_string(i);
        payloads.insert(payload);
        producer.produce(MessageBuilder(KAFKA_TOPIC).payload(payload));
    }
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(message_count, messages.size());
    for (const auto& message : messages) {
        EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
        EXPECT_EQ(1, payloads.erase(message.get_payload()));
        EXPECT_FALSE(message.get_error());
        EXPECT_FALSE(message.get_key());
        EXPECT_GE(message.get_partition(), 0);
        EXPECT_LT(message.get_partition(), 3);
    }
}

TEST_F(ProducerTest, Callbacks) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 1, 1);

    // Now create a producer and produce a message
    string payload = "Hello world! 3";
    string key = "hehe";
    bool delivery_report_called = false;
    Configuration config = make_producer_config();
    config.set_delivery_report_callback([&](Producer&, const Message& msg) {
        EXPECT_EQ(Buffer(payload), msg.get_payload());
        delivery_report_called = true;
    });

    TopicConfiguration topic_config;
    topic_config.set_partitioner_callback([&](const Topic& topic, const Buffer& msg_key,
                                              int32_t partition_count) {
        EXPECT_EQ(Buffer(key), msg_key);
        EXPECT_EQ(3, partition_count);
        EXPECT_EQ(KAFKA_TOPIC, topic.get_name());
        return 0;
    });
    config.set_default_topic_configuration(topic_config);

    Producer producer(move(config));
    producer.produce(MessageBuilder(KAFKA_TOPIC).key(key).payload(payload));
    while (producer.get_out_queue_length() > 0) {
        producer.poll();
    }
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(1, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(Buffer(payload), message.get_payload());
    EXPECT_EQ(Buffer(key), message.get_key());
    EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_FALSE(message.get_error());
    EXPECT_TRUE(delivery_report_called);
}

TEST_F(ProducerTest, PartitionerCallbackOnDefaultTopicConfig) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 1, 1);

    // Now create a producer and produce a message
    string payload = "Hello world! 4";
    string key = "hehe";
    bool callback_called = false;

    Configuration config = make_producer_config();
    TopicConfiguration topic_config;
    topic_config.set_partitioner_callback([&](const Topic& topic, const Buffer& msg_key,
                                              int32_t partition_count) {
        EXPECT_EQ(Buffer(key), msg_key);
        EXPECT_EQ(3, partition_count);
        EXPECT_EQ(KAFKA_TOPIC, topic.get_name());
        callback_called = true;
        return 0;
    });
    config.set_default_topic_configuration(topic_config);

    Producer producer(move(config));
    producer.produce(MessageBuilder(KAFKA_TOPIC).key(key).payload(payload));
    producer.poll();
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(1, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_TRUE(callback_called);
}

TEST_F(ProducerTest, BufferedProducer) {
    int partition = 0;

    // Create a consumer and assign this topic/partition
    Consumer consumer(make_consumer_config());
    consumer.assign({ TopicPartition(KAFKA_TOPIC, partition) });
    ConsumerRunner runner(consumer, 3, 1);

    // Now create a buffered producer and produce two messages
    BufferedProducer<string> producer(make_producer_config());
    string payload = "Hello world! 2";
    string key = "such key";
    producer.add_message(MessageBuilder(KAFKA_TOPIC).partition(partition)
                                                    .key(key)
                                                    .payload(payload));
    producer.add_message(producer.make_builder(KAFKA_TOPIC).partition(partition).payload(payload));
    producer.flush();
    producer.produce(MessageBuilder(KAFKA_TOPIC).partition(partition).payload(payload));
    producer.wait_for_acks();
    // Add another one but then clear it
    producer.add_message(producer.make_builder(KAFKA_TOPIC).partition(partition).payload(payload));
    producer.clear();
    runner.try_join();

    const auto& messages = runner.get_messages();
    ASSERT_EQ(3, messages.size());
    const auto& message = messages[0];
    EXPECT_EQ(Buffer(key), message.get_key());
    EXPECT_EQ(KAFKA_TOPIC, message.get_topic());
    EXPECT_EQ(partition, message.get_partition());
    EXPECT_FALSE(message.get_error());

    EXPECT_FALSE(messages[1].get_key());
    EXPECT_FALSE(messages[2].get_key());
    for (const auto& message : messages) {
        EXPECT_EQ(Buffer(payload), message.get_payload());
    }
}
<|endoftext|>


<commit_before>///
/// @file  P2.cpp
/// @brief 2nd partial sieve function.
///        P2(x, y) counts the numbers <= x that have exactly 2 prime
///        factors each exceeding the a-th prime.
///
/// Copyright (C) 2014 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <primesieve.hpp>
#include <aligned_vector.hpp>
#include <BitSieve.hpp>
#include <generate.hpp>
#include <pmath.hpp>
#include <inttypes.hpp>

#include <stdint.h>
#include <algorithm>
#include <iostream>
#include <vector>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;
using namespace primecount;

namespace {
namespace P2 {

class ReversePrimeIterator
{
public:
  ReversePrimeIterator(int64_t stop, int64_t start) :
    iter_(stop, start),
    prime_(stop)
  { }
  int64_t previous_prime()
  {
    int64_t NO_PREV_PRIME = -1;
    if (prime_ <= 2)
      return NO_PREV_PRIME;
    prime_ = iter_.previous_prime();
    return prime_;
  }
private:
  primesieve::iterator iter_;
  int64_t prime_;
};

/// For each prime calculate its first multiple >= low
vector<int64_t> generate_next_multiples(int64_t low, int64_t size, vector<int32_t>& primes)
{
  vector<int64_t> next;

  next.reserve(size);
  next.push_back(0);

  for (int64_t b = 1; b < size; b++)
  {
    int64_t prime = primes[b];
    int64_t next_multiple = ceil_div(low, prime) * prime;
    next_multiple += prime * (~next_multiple & 1);
    next_multiple = max(prime * prime, next_multiple);
    next.push_back(next_multiple);
  }

  return next;
}

template <typename T>
T P2_thread(T x,
            int64_t y,
            int64_t segment_size,
            int64_t segments_per_thread,
            int64_t thread_num,
            int64_t low,
            int64_t limit,
            int64_t& pix,
            int64_t& pix_count,
            vector<int32_t>& primes)
{
  pix = 0;
  pix_count = 0;
  low += thread_num * segments_per_thread * segment_size;
  limit = min(low + segments_per_thread * segment_size, limit);
  int64_t size = pi_bsearch(primes, isqrt(limit)) + 1;
  int64_t start = (int64_t) max(x / limit + 1, y);
  int64_t stop  = (int64_t) min(x / low, isqrt(x));
  T P2_thread = 0;

  // P2_thread = \sum_{i=pi[start]}^{pi[stop]} pi(x / primes[i]) - pi(low - 1)
  // We use a reverse prime iterator to calculate P2_thread
  ReversePrimeIterator prime_iter(stop + 1, start);
  int64_t prime = prime_iter.previous_prime();
  int64_t xp = (int64_t) (x / prime);

  vector<int64_t> next = generate_next_multiples(low, size, primes);
  BitSieve sieve(segment_size);

  // segmented sieve of Eratosthenes
  for (; low < limit; low += segment_size)
  {
    // current segment = interval [low, high[
    int64_t high = min(low + segment_size, limit);
    int64_t sqrt = isqrt(high - 1);
    int64_t j = 0;

    sieve.fill(low, high);

    // cross-off multiples
    for (int64_t i = 2; i < size && primes[i] <= sqrt; i++)
    {
      int64_t k;
      int64_t p2 = primes[i] * 2;
      for (k = next[i]; k < high; k += p2)
        sieve.unset(k - low);
      next[i] = k;
    }

    while (prime >= start &&
           xp < high)
    {
      pix += sieve.count(j, xp - low);
      j = xp - low + 1;
      pix_count++;
      P2_thread += pix;
      prime = prime_iter.previous_prime();
      xp = (int64_t) (x / prime);
    }

    pix += sieve.count(j, (high - 1) - low);
  }

  return P2_thread;
}

void balanceLoad(int64_t* segments_per_thread, double seconds1, double time1)
{
  double time2 = get_wtime();
  double seconds = time2 - seconds1;
  double time = time2 - time1;
  double increase_threshold = in_between(0.5, time / 10, 20);

  if (seconds < increase_threshold)
    *segments_per_thread += *segments_per_thread * 3;
  else if (*segments_per_thread >= 4)
    *segments_per_thread -= *segments_per_thread / 4;
}

/// P2(x, y) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime, a = pi(y).
/// Space complexity: O((x / y)^(1/2)).
///
template <typename T>
T P2(T x, int64_t y, int threads)
{
  int64_t a = pi_legendre(y, 1);
  int64_t b = pi_legendre((int64_t) isqrt(x), 1);

  if (x < 4 || a >= b)
    return 0;

  if (print_status())
  {
    cout << endl;
    cout << "=== P2(x, y) ===" << endl;
    cout << "Computation of the 2nd partial sieve function" << endl;
  }

  double time = get_wtime();
  int64_t low = 2;
  int64_t limit = (int64_t)(x / max(y, 1));
  int64_t segment_size = max(isqrt(limit), 1 << 12);
  int64_t segments_per_thread = 1;
  threads = validate_threads(threads, limit);

  vector<int32_t> primes = generate_primes(isqrt(limit));
  aligned_vector<int64_t> pix(threads);
  aligned_vector<int64_t> pix_counts(threads);

  // \sum_{i=a+1}^{b} pi(x / primes[i]) - (i - 1)
  T sum = 0;
  T pix_total = 0;

  // \sum_{i=a+1}^{b} pi(x / primes[i])
  while (low < limit)
  {
    int64_t segments = ceil_div(limit - low, segment_size);
    threads = in_between(1, threads, segments);
    segments_per_thread = in_between(1, segments_per_thread, ceil_div(segments, threads));
    double seconds = get_wtime();

    #pragma omp parallel for \
        num_threads(threads) reduction(+: sum)
    for (int i = 0; i < threads; i++)
      sum += P2_thread(x, y, segment_size, segments_per_thread, i,
         low, limit, pix[i], pix_counts[i], primes);

    low += segments_per_thread * threads * segment_size;
    balanceLoad(&segments_per_thread, seconds, time);

    // Add missing sum contributions in order
    for (int i = 0; i < threads; i++)
    {
      sum += pix_total * pix_counts[i];
      pix_total += pix[i];
    }

    if (print_status())
      cout << "\rStatus: " << get_percent(low, limit) << '%' << flush;
  }

  // \sum_{i=a+1}^{b} - (i - 1)
  sum -= (b - 2) * (b + 1) / 2 - (a - 2) * (a + 1) / 2;

  if (print_status())
    print_result("P2", sum, time);

  return sum;
}

} // namespace P2
} // namespace

namespace primecount {

int64_t P2(int64_t x, int64_t y, int threads)
{
  return P2::P2((intfast64_t) x, y, threads);
}

#ifdef HAVE_INT128_T

int128_t P2(int128_t x, int64_t y, int threads)
{
  return P2::P2((intfast128_t) x, y, threads);
}

#endif

/// P2_lehmer(x, a) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime. This implementation is
/// optimized for small values of a < pi(x^(1/3)) which requires
/// sieving up to a large limit (x / primes[a]). Sieving is done in
/// parallel using primesieve (segmented sieve of Eratosthenes).
/// Space complexity: O(pi(sqrt(x))).
///
int64_t P2_lehmer(int64_t x, int64_t a, int threads)
{
  if (print_status())
  {
    cout << endl;
    cout << "=== P2_lehmer(x, a) ===" << endl;
    cout << "Computation of the 2nd partial sieve function" << endl;
  }

  double time = get_wtime();
  vector<int32_t> primes = generate_primes(isqrt(x));
  vector<int64_t> counts(primes.size());

  int64_t b = pi_bsearch(primes, isqrt(x));
  int64_t sum = 0;
  int64_t pix = 0;

  #pragma omp parallel for schedule(dynamic) \
      num_threads(validate_threads(threads, b, 1000))
  for (int64_t i = b; i > a; i--)
  {
    int64_t prev = (i == b) ? 0 : x / primes[i + 1] + 1;
    int64_t xi = x / primes[i];
    counts[i] = primesieve::count_primes(prev, xi);
  }

  for (int64_t i = b; i > a; i--)
  {
    pix += counts[i];
    sum += pix - (i - 1);
  }

  if (print_status())
    print_result("P2", sum, time);

  return sum;
}

} // namespace primecount
<commit_msg>Refactoring<commit_after>///
/// @file  P2.cpp
/// @brief 2nd partial sieve function.
///        P2(x, y) counts the numbers <= x that have exactly 2 prime
///        factors each exceeding the a-th prime.
///
/// Copyright (C) 2014 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <primesieve.hpp>
#include <aligned_vector.hpp>
#include <BitSieve.hpp>
#include <generate.hpp>
#include <min_max.hpp>
#include <pmath.hpp>
#include <int128.hpp>

#include <stdint.h>
#include <algorithm>
#include <iostream>
#include <vector>

#ifdef _OPENMP
  #include <omp.h>
#endif

using namespace std;
using namespace primecount;

namespace {
namespace P2 {

class ReversePrimeIterator
{
public:
  ReversePrimeIterator(int64_t stop, int64_t start) :
    iter_(stop, start),
    prime_(stop)
  { }
  int64_t previous_prime()
  {
    if (prime_ <= 2)
      return -1;
    prime_ = iter_.previous_prime();
    return prime_;
  }
private:
  primesieve::iterator iter_;
  int64_t prime_;
};

/// For each prime calculate its first multiple >= low
vector<int64_t> generate_next_multiples(int64_t low, int64_t size, vector<int32_t>& primes)
{
  vector<int64_t> next;

  next.reserve(size);
  next.push_back(0);

  for (int64_t b = 1; b < size; b++)
  {
    int64_t prime = primes[b];
    int64_t next_multiple = ceil_div(low, prime) * prime;
    next_multiple += prime * (~next_multiple & 1);
    next_multiple = max(prime * prime, next_multiple);
    next.push_back(next_multiple);
  }

  return next;
}

template <typename T>
T P2_thread(T x,
            int64_t y,
            int64_t segment_size,
            int64_t segments_per_thread,
            int64_t thread_num,
            int64_t low,
            int64_t limit,
            int64_t& pix,
            int64_t& pix_count,
            vector<int32_t>& primes)
{
  pix = 0;
  pix_count = 0;
  low += thread_num * segments_per_thread * segment_size;
  limit = min(low + segments_per_thread * segment_size, limit);
  int64_t size = pi_bsearch(primes, isqrt(limit)) + 1;
  int64_t start = (int64_t) max(x / limit + 1, y);
  int64_t stop  = (int64_t) min(x / low, isqrt(x));
  T P2_thread = 0;

  // P2_thread = \sum_{i=pi[start]}^{pi[stop]} pi(x / primes[i]) - pi(low - 1)
  // We use a reverse prime iterator to calculate P2_thread
  ReversePrimeIterator prime_iter(stop + 1, start);
  int64_t prime = prime_iter.previous_prime();
  int64_t xp = (int64_t) (x / prime);

  vector<int64_t> next = generate_next_multiples(low, size, primes);
  BitSieve sieve(segment_size);

  // segmented sieve of Eratosthenes
  for (; low < limit; low += segment_size)
  {
    // current segment = interval [low, high[
    int64_t high = min(low + segment_size, limit);
    int64_t sqrt = isqrt(high - 1);
    int64_t j = 0;

    sieve.fill(low, high);

    // cross-off multiples
    for (int64_t i = 2; i < size && primes[i] <= sqrt; i++)
    {
      int64_t k;
      int64_t p2 = primes[i] * 2;
      for (k = next[i]; k < high; k += p2)
        sieve.unset(k - low);
      next[i] = k;
    }

    while (prime >= start &&
           xp < high)
    {
      pix += sieve.count(j, xp - low);
      j = xp - low + 1;
      pix_count++;
      P2_thread += pix;
      prime = prime_iter.previous_prime();
      xp = (int64_t) (x / prime);
    }

    pix += sieve.count(j, (high - 1) - low);
  }

  return P2_thread;
}

void balanceLoad(int64_t* segments_per_thread, double seconds1, double time1)
{
  double time2 = get_wtime();
  double seconds = time2 - seconds1;
  double time = time2 - time1;
  double increase_threshold = in_between(0.5, time / 10, 20);

  if (seconds < increase_threshold)
    *segments_per_thread += *segments_per_thread * 3;
  else if (*segments_per_thread >= 4)
    *segments_per_thread -= *segments_per_thread / 4;
}

/// P2(x, y) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime, a = pi(y).
/// Space complexity: O((x / y)^(1/2)).
///
template <typename T>
T P2(T x, int64_t y, int threads)
{
  int64_t a = pi_legendre(y, 1);
  int64_t b = pi_legendre((int64_t) isqrt(x), 1);

  if (x < 4 || a >= b)
    return 0;

  if (print_status())
  {
    cout << endl;
    cout << "=== P2(x, y) ===" << endl;
    cout << "Computation of the 2nd partial sieve function" << endl;
  }

  double time = get_wtime();
  int64_t low = 2;
  int64_t limit = (int64_t)(x / max(y, 1));
  int64_t segment_size = max(isqrt(limit), 1 << 12);
  int64_t segments_per_thread = 1;
  threads = validate_threads(threads, limit);

  vector<int32_t> primes = generate_primes(isqrt(limit));
  aligned_vector<int64_t> pix(threads);
  aligned_vector<int64_t> pix_counts(threads);

  // \sum_{i=a+1}^{b} pi(x / primes[i]) - (i - 1)
  T sum = 0;
  T pix_total = 0;

  // \sum_{i=a+1}^{b} pi(x / primes[i])
  while (low < limit)
  {
    int64_t segments = ceil_div(limit - low, segment_size);
    threads = in_between(1, threads, segments);
    segments_per_thread = in_between(1, segments_per_thread, ceil_div(segments, threads));
    double seconds = get_wtime();

    #pragma omp parallel for \
        num_threads(threads) reduction(+: sum)
    for (int i = 0; i < threads; i++)
      sum += P2_thread(x, y, segment_size, segments_per_thread, i,
         low, limit, pix[i], pix_counts[i], primes);

    low += segments_per_thread * threads * segment_size;
    balanceLoad(&segments_per_thread, seconds, time);

    // Add missing sum contributions in order
    for (int i = 0; i < threads; i++)
    {
      sum += pix_total * pix_counts[i];
      pix_total += pix[i];
    }

    if (print_status())
      cout << "\rStatus: " << get_percent(low, limit) << '%' << flush;
  }

  // \sum_{i=a+1}^{b} - (i - 1)
  sum -= (b - 2) * (b + 1) / 2 - (a - 2) * (a + 1) / 2;

  if (print_status())
    print_result("P2", sum, time);

  return sum;
}

} // namespace P2
} // namespace

namespace primecount {

int64_t P2(int64_t x, int64_t y, int threads)
{
  return P2::P2((intfast64_t) x, y, threads);
}

#ifdef HAVE_INT128_T

int128_t P2(int128_t x, int64_t y, int threads)
{
  return P2::P2((intfast128_t) x, y, threads);
}

#endif

/// P2_lehmer(x, a) counts the numbers <= x that have exactly 2 prime
/// factors each exceeding the a-th prime. This implementation is
/// optimized for small values of a < pi(x^(1/3)) which requires
/// sieving up to a large limit (x / primes[a]). Sieving is done in
/// parallel using primesieve (segmented sieve of Eratosthenes).
/// Space complexity: O(pi(sqrt(x))).
///
int64_t P2_lehmer(int64_t x, int64_t a, int threads)
{
  if (print_status())
  {
    cout << endl;
    cout << "=== P2_lehmer(x, a) ===" << endl;
    cout << "Computation of the 2nd partial sieve function" << endl;
  }

  double time = get_wtime();
  vector<int32_t> primes = generate_primes(isqrt(x));
  vector<int64_t> counts(primes.size());

  int64_t b = pi_bsearch(primes, isqrt(x));
  int64_t sum = 0;
  int64_t pix = 0;

  #pragma omp parallel for schedule(dynamic) \
      num_threads(validate_threads(threads, b, 1000))
  for (int64_t i = b; i > a; i--)
  {
    int64_t prev = (i == b) ? 0 : x / primes[i + 1] + 1;
    int64_t xi = x / primes[i];
    counts[i] = primesieve::count_primes(prev, xi);
  }

  for (int64_t i = b; i > a; i--)
  {
    pix += counts[i];
    sum += pix - (i - 1);
  }

  if (print_status())
    print_result("P2", sum, time);

  return sum;
}

} // namespace primecount
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include "utTerm.h"
#include "utAtom.h"
#include "utNumber.h"
#include "utVariable.h"
#include "utStruct.h"
//#include "utList.h"

int main( int argc , char **argv )
{
    testing :: InitGoogleTest( &argc , argv ) ;
    return RUN_ALL_TESTS( ) ;
}<commit_msg>update hw<commit_after>#include <gtest/gtest.h>
//#include "utTerm.h"
//#include "utAtom.h"
//#include "utNumber.h"
//#include "utVariable.h"
//#include "utStruct.h"
#include "utList.h"

int main( int argc , char **argv )
{
    testing :: InitGoogleTest( &argc , argv ) ;
    return RUN_ALL_TESTS( ) ;
}<|endoftext|>


<commit_before>#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <cmath>
#include <memory>
#include <limits>

#include "scene.hpp"
#include "light.hpp"

#define MAX_LEVEL 5

using std::vector;
using std::auto_ptr;

/*
 * In the case of an empty constructor, Scene will generate a predefined
 * scene for rendering.
 *
 * @param width : The width of the scene
 * @param height : The height of the scene
 */
Scene::Scene(int width, int height) : shapes(vector<Shape*>()),
                 lights(vector<LightSource*>()),
                 ambient(Color(0.25, 0.25, 0.25)),
                 camera(Camera::lookAt(Point(0,2,-5), Vector(0, 1, 0),
                             Point(0, 0, 0), degrees(60),
                             width, height))
{
    // floor
    shapes.push_back(new Plane(Point(0, -3, 0), Vector(0, 1, 0), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    // back wall
    shapes.push_back(new Plane(Point(0, 0, 7), Vector(0, 0, -1), Color(1, 1, 0), 1, 0.5, 0.5, 0.5, 0, 0));

    // left side wall
    shapes.push_back(new Plane(Point(-3, 0, 0), Vector(1, 0, 0), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    // ceiling
    shapes.push_back(new Plane(Point(0, 10, 0), Vector(0, -1, 0), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    // wall behind camera
    shapes.push_back(new Plane(Point(0, 0, -10), Vector(0, 0, 1), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    // right wall
    shapes.push_back(new Plane(Point(15, 0, 0), Vector(-1, 0, 0), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    //teal sphere (transparent)
    shapes.push_back(new Sphere(Point(4, .5, 2), 2,
                Color(0, 1, 1), 1.1, .3, .5, 0, 0, .9));

    //red sphere in the middle
    shapes.push_back(new Sphere(Point(4, .5, 2), .75,
                Color(1, 0, 0), 1, .3, .5, .5, 0, 0));

    // red sphere (shiny)
    shapes.push_back(new Sphere(Point(3, -1, 5), 2,
                Color(1, 0, 0), 1, .3, 0, 1, 1, 0));
    // blue sphere
    shapes.push_back(new Sphere(Point(-1, 2, 5), 1,
                Color(0, 0, 1), 1, 0.3, 0.5, 0.5, 0, 0));

    //green sphere
        shapes.push_back(new Sphere(Point(-4, 0, 4), 2,
                Color(0, 1, 0), 1, 0.3, 0.5, 0.5, 0, 0));

    // light source 1 (blueish)
    lights.push_back(new PointLight(Color(.5, .5, 1), Point(0, 0, 2)));

    // light source 2 (redish)
    lights.push_back(new PointLight(Color(1, .5, .5), Point(0, 5, 0)));

}

Scene::Scene(std::string fileName, int width, int height): shapes(vector<Shape*>()),
                                    lights(vector<LightSource*>()),
                                    ambient(Color(0.25, 0.25, 0.25)),
                                    camera(Camera(width, height)) {

    std::ifstream input_scene (fileName.c_str());

    // Read the file and add specified shapes.
    // We need to figure out what our syntax/grammar is first though.
}

Color Scene::raytrace(const Ray& camera_ray, int level) const {
    if(level>MAX_LEVEL) {
       /**
        * I'm not really sure if this is the correct return for this case
        * but in theory, this shouldn't trigger anyway.
        * We should always check the current level before attempting
        * to enter a new level of raytracing.
        */
        return Color(1,1,1);
    }

    const Shape *nearest_shape = NULL;
    float nearest_distance2 = std::numeric_limits<float>::max();
    Ray nearest_hit;
    for (vector<Shape*>::const_iterator it = shapes.begin(); it != shapes.end(); it++) {
        const Shape *shape = *it;
        auto_ptr<Ray> intersection(shape->getIntersection(camera_ray));
        if (intersection.get() == NULL) // Didn't hit this shape
            continue;
        float int_distance2 = Ray::makeRay(camera_ray.getOrigin(),
                intersection->getOrigin()).getDir().magnitude2();
        if (int_distance2 < nearest_distance2) {
            // Found a closer hit
            nearest_shape = shape;
            nearest_distance2 = int_distance2;
            nearest_hit = *intersection;
        }
    }

    if (nearest_shape == NULL) {
        // Didn't hit anything
        return Color(1, 0, 1); // Magenta
    } else {
        return shade(nearest_shape, nearest_hit, camera_ray, level);
    }
}

Color Scene::shade(const Shape *obj, Ray hit, const Ray& camera_ray, int level) const {
    Color result = Color(ambient) * obj->getAmbientCoefficient();
    for(vector<LightSource*>::const_iterator it = lights.begin(); it != lights.end(); it++) {
        LightSource *light = *it;
        Ray shadow = Ray::makeRay(hit.getOrigin(), light->getPoint());
        float lightAmount = 1;
        // Detect collisions, for now do nothing if there is a collision
        for(vector<Shape*>::const_iterator sh = shapes.begin(); sh != shapes.end(); sh++) {
            Shape *shape = *sh;
            if (shape != obj) {
                auto_ptr<Ray> hit(shape->getIntersection(shadow));
                if (hit.get() != NULL && hit->getOrigin() != shadow.getOrigin()) {
                    Vector toHit(hit->getOrigin().x-shadow.getOrigin().x,
                        hit->getOrigin().y-shadow.getOrigin().y,
                        hit->getOrigin().z-shadow.getOrigin().z);
                    if(fabs(toHit.i) < fabs(shadow.getDir().i) &&
                        fabs(toHit.j) < fabs(shadow.getDir().j) &&
                        fabs(toHit.k) < fabs(shadow.getDir().k)){
                        if(shape->getTransparencyCoefficient()==0){
                            //if object is opaque then full shadow
                            lightAmount=0;
                            break;
                        }
                        //if object is transparent, add a percent of the shadow
                        lightAmount *= shape->getTransparencyCoefficient();
                    }
                }
            }
        }
        if(lightAmount>0) {

            // Normalize the direction vectors before calculations
            shadow.normalize();
            hit.normalize();
            //shade the lightColor appropriately
            Color lightColor = light->getColor() * lightAmount;

            //diffuse lighting
            if(obj->getDiffuseCoefficient()>0) {
                float cos_theta = (shadow.getDir()).dotProduct(hit.getDir());
                if(cos_theta > 0){
                    Color diffuse = cos_theta * obj->getDiffuseCoefficient()
                        * lightColor * obj->getColor();
                    result = result + diffuse;
                }
            }

            //specular lighting
            if(obj->getSpecularCoefficient()>0) {
                Ray lightReflection(hit.getOrigin(), shadow.getDir()-
                    hit.getDir()*2*shadow.getDir().dotProduct(hit.getDir()));
                float cos_theta =
                    camera_ray.getDir().dotProduct(lightReflection.getDir());
                if(cos_theta > 0) {
                    Color spec = powf( cos_theta, 20 ) *
                        obj->getSpecularCoefficient() *
                        lightColor;
                    result = result + spec;
                }
            }

        }
    }

    //reflection
    if(obj->getReflectionCoefficient()>0 && level < MAX_LEVEL){
        Ray reflection(hit.getOrigin(), camera_ray.getDir()-
                    hit.getDir()*2*camera_ray.getDir().dotProduct(hit.getDir()));
        Color reflColor = raytrace(reflection, level+1) *
            obj->getReflectionCoefficient() * obj->getColor();
        result = result + reflColor;
    }

    //refraction
    if(obj->getTransparencyCoefficient()>0 && level < MAX_LEVEL){
        float rIndex = obj->getIndexOfRefraction();
        float n = 1/rIndex;
        Vector N = hit.getDir();
        Vector negNorm(-hit.getDir().i,-hit.getDir().j,-hit.getDir().k);
        float cosT = camera_ray.getDir().dotProduct(negNorm);

        if(cosT<0){//hitting the edge from inside of the object
            N = N*-1;
        }
        float cosI = -(N.dotProduct(camera_ray.getDir()));
        float cosT2 = 1-n*n*(1-cosI*cosI);
        if(cosT2>0){
            Vector T = (camera_ray.getDir() * n) + N * (n * cosI - sqrtf(cosT2));
            Ray refraction(hit.getOrigin(), T);
            Color rcol = raytrace(refraction, level+1) *
                obj->getTransparencyCoefficient();
            result = result + rcol;

        }

    }
    result = result.clamp();
    return result;
}

Scene::~Scene() {
    for(vector<Shape*>::iterator it = shapes.begin(); it != shapes.end(); it++) {
        delete *it;
    }
    for(vector<LightSource*>::iterator it = lights.begin(); it != lights.end(); it++) {
        delete *it;
    }
}

Ray Scene::get_camera_ray(float x, float y) const {
    return camera.get_ray(x, -y);
}
<commit_msg>Tilted the shiny new camera<commit_after>#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <cmath>
#include <memory>
#include <limits>

#include "scene.hpp"
#include "light.hpp"

#define MAX_LEVEL 5

using std::vector;
using std::auto_ptr;

/*
 * In the case of an empty constructor, Scene will generate a predefined
 * scene for rendering.
 *
 * @param width : The width of the scene
 * @param height : The height of the scene
 */
Scene::Scene(int width, int height) : shapes(vector<Shape*>()),
                 lights(vector<LightSource*>()),
                 ambient(Color(0.25, 0.25, 0.25)),
                 camera(Camera::lookAt(Point(0,2,-5), Vector(0.1, 1, 0),
                             Point(0, 1, 0), degrees(60),
                             width, height))
{
    // floor
    shapes.push_back(new Plane(Point(0, -3, 0), Vector(0, 1, 0), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    // back wall
    shapes.push_back(new Plane(Point(0, 0, 7), Vector(0, 0, -1), Color(1, 1, 0), 1, 0.5, 0.5, 0.5, 0, 0));

    // left side wall
    shapes.push_back(new Plane(Point(-3, 0, 0), Vector(1, 0, 0), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    // ceiling
    shapes.push_back(new Plane(Point(0, 10, 0), Vector(0, -1, 0), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    // wall behind camera
    shapes.push_back(new Plane(Point(0, 0, -10), Vector(0, 0, 1), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    // right wall
    shapes.push_back(new Plane(Point(15, 0, 0), Vector(-1, 0, 0), Color(1, 1, 1), 1, 0.5, 0.5, 0.5, 0, 0));

    //teal sphere (transparent)
    shapes.push_back(new Sphere(Point(4, .5, 2), 2,
                Color(0, 1, 1), 1.1, .3, .5, 0, 0, .9));

    //red sphere in the middle
    shapes.push_back(new Sphere(Point(4, .5, 2), .75,
                Color(1, 0, 0), 1, .3, .5, .5, 0, 0));

    // red sphere (shiny)
    shapes.push_back(new Sphere(Point(3, -1, 5), 2,
                Color(1, 0, 0), 1, .3, 0, 1, 1, 0));
    // blue sphere
    shapes.push_back(new Sphere(Point(-1, 2, 5), 1,
                Color(0, 0, 1), 1, 0.3, 0.5, 0.5, 0, 0));

    //green sphere
        shapes.push_back(new Sphere(Point(-4, 0, 4), 2,
                Color(0, 1, 0), 1, 0.3, 0.5, 0.5, 0, 0));

    // light source 1 (blueish)
    lights.push_back(new PointLight(Color(.5, .5, 1), Point(0, 0, 2)));

    // light source 2 (redish)
    lights.push_back(new PointLight(Color(1, .5, .5), Point(0, 5, 0)));

}

Scene::Scene(std::string fileName, int width, int height): shapes(vector<Shape*>()),
                                    lights(vector<LightSource*>()),
                                    ambient(Color(0.25, 0.25, 0.25)),
                                    camera(Camera(width, height)) {

    std::ifstream input_scene (fileName.c_str());

    // Read the file and add specified shapes.
    // We need to figure out what our syntax/grammar is first though.
}

Color Scene::raytrace(const Ray& camera_ray, int level) const {
    if(level>MAX_LEVEL) {
       /**
        * I'm not really sure if this is the correct return for this case
        * but in theory, this shouldn't trigger anyway.
        * We should always check the current level before attempting
        * to enter a new level of raytracing.
        */
        return Color(1,1,1);
    }

    const Shape *nearest_shape = NULL;
    float nearest_distance2 = std::numeric_limits<float>::max();
    Ray nearest_hit;
    for (vector<Shape*>::const_iterator it = shapes.begin(); it != shapes.end(); it++) {
        const Shape *shape = *it;
        auto_ptr<Ray> intersection(shape->getIntersection(camera_ray));
        if (intersection.get() == NULL) // Didn't hit this shape
            continue;
        float int_distance2 = Ray::makeRay(camera_ray.getOrigin(),
                intersection->getOrigin()).getDir().magnitude2();
        if (int_distance2 < nearest_distance2) {
            // Found a closer hit
            nearest_shape = shape;
            nearest_distance2 = int_distance2;
            nearest_hit = *intersection;
        }
    }

    if (nearest_shape == NULL) {
        // Didn't hit anything
        return Color(1, 0, 1); // Magenta
    } else {
        return shade(nearest_shape, nearest_hit, camera_ray, level);
    }
}

Color Scene::shade(const Shape *obj, Ray hit, const Ray& camera_ray, int level) const {
    Color result = Color(ambient) * obj->getAmbientCoefficient();
    for(vector<LightSource*>::const_iterator it = lights.begin(); it != lights.end(); it++) {
        LightSource *light = *it;
        Ray shadow = Ray::makeRay(hit.getOrigin(), light->getPoint());
        float lightAmount = 1;
        // Detect collisions, for now do nothing if there is a collision
        for(vector<Shape*>::const_iterator sh = shapes.begin(); sh != shapes.end(); sh++) {
            Shape *shape = *sh;
            if (shape != obj) {
                auto_ptr<Ray> hit(shape->getIntersection(shadow));
                if (hit.get() != NULL && hit->getOrigin() != shadow.getOrigin()) {
                    Vector toHit(hit->getOrigin().x-shadow.getOrigin().x,
                        hit->getOrigin().y-shadow.getOrigin().y,
                        hit->getOrigin().z-shadow.getOrigin().z);
                    if(fabs(toHit.i) < fabs(shadow.getDir().i) &&
                        fabs(toHit.j) < fabs(shadow.getDir().j) &&
                        fabs(toHit.k) < fabs(shadow.getDir().k)){
                        if(shape->getTransparencyCoefficient()==0){
                            //if object is opaque then full shadow
                            lightAmount=0;
                            break;
                        }
                        //if object is transparent, add a percent of the shadow
                        lightAmount *= shape->getTransparencyCoefficient();
                    }
                }
            }
        }
        if(lightAmount>0) {

            // Normalize the direction vectors before calculations
            shadow.normalize();
            hit.normalize();
            //shade the lightColor appropriately
            Color lightColor = light->getColor() * lightAmount;

            //diffuse lighting
            if(obj->getDiffuseCoefficient()>0) {
                float cos_theta = (shadow.getDir()).dotProduct(hit.getDir());
                if(cos_theta > 0){
                    Color diffuse = cos_theta * obj->getDiffuseCoefficient()
                        * lightColor * obj->getColor();
                    result = result + diffuse;
                }
            }

            //specular lighting
            if(obj->getSpecularCoefficient()>0) {
                Ray lightReflection(hit.getOrigin(), shadow.getDir()-
                    hit.getDir()*2*shadow.getDir().dotProduct(hit.getDir()));
                float cos_theta =
                    camera_ray.getDir().dotProduct(lightReflection.getDir());
                if(cos_theta > 0) {
                    Color spec = powf( cos_theta, 20 ) *
                        obj->getSpecularCoefficient() *
                        lightColor;
                    result = result + spec;
                }
            }

        }
    }

    //reflection
    if(obj->getReflectionCoefficient()>0 && level < MAX_LEVEL){
        Ray reflection(hit.getOrigin(), camera_ray.getDir()-
                    hit.getDir()*2*camera_ray.getDir().dotProduct(hit.getDir()));
        Color reflColor = raytrace(reflection, level+1) *
            obj->getReflectionCoefficient() * obj->getColor();
        result = result + reflColor;
    }

    //refraction
    if(obj->getTransparencyCoefficient()>0 && level < MAX_LEVEL){
        float rIndex = obj->getIndexOfRefraction();
        float n = 1/rIndex;
        Vector N = hit.getDir();
        Vector negNorm(-hit.getDir().i,-hit.getDir().j,-hit.getDir().k);
        float cosT = camera_ray.getDir().dotProduct(negNorm);

        if(cosT<0){//hitting the edge from inside of the object
            N = N*-1;
        }
        float cosI = -(N.dotProduct(camera_ray.getDir()));
        float cosT2 = 1-n*n*(1-cosI*cosI);
        if(cosT2>0){
            Vector T = (camera_ray.getDir() * n) + N * (n * cosI - sqrtf(cosT2));
            Ray refraction(hit.getOrigin(), T);
            Color rcol = raytrace(refraction, level+1) *
                obj->getTransparencyCoefficient();
            result = result + rcol;

        }

    }
    result = result.clamp();
    return result;
}

Scene::~Scene() {
    for(vector<Shape*>::iterator it = shapes.begin(); it != shapes.end(); it++) {
        delete *it;
    }
    for(vector<LightSource*>::iterator it = lights.begin(); it != lights.end(); it++) {
        delete *it;
    }
}

Ray Scene::get_camera_ray(float x, float y) const {
    return camera.get_ray(x, -y);
}
<|endoftext|>


<commit_before>#include "gtest/gtest.h"

#include "../src/accel.h"

const void * void_null = NULL;

accel_state *test_fabricate_state(int dimensions) {
    accel_state *state = NULL;
    int result = accel_generate_state(&state, dimensions, 1);
    EXPECT_EQ(0, result);
    EXPECT_NE(void_null, state);
    return state;
}

accel_state *test_fabricate_1d_state() {
    return test_fabricate_state(1);
}

accel_state *test_fabricate_3d_state() {
    return test_fabricate_state(3);
}

void test_burn_state(accel_state ** state) {
    int result = accel_destroy_state(state);
    EXPECT_EQ(0, result);
    EXPECT_EQ(void_null, *state);
}

TEST(AccelFuzzTest, generate_state_null_state) {
    int result = accel_generate_state(NULL, 3, 1);
    EXPECT_EQ(result, ACCEL_PARAM_ERROR);
}

TEST(AccelFuzzTest, generate_state_negative_or_zero_dimensions) {
    accel_state *state = NULL;
    // 0 dimensions must fail
    int result = accel_generate_state(&state, 0, 1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // -1 dimension must fail
    result = accel_generate_state(&state, -1, 1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // 1 dimension must succeed.
    result = accel_generate_state(&state, 1, 1);
    EXPECT_EQ(0, result);
    // TODO: result's memory is leaked :s
}

TEST(AccelFuzzTest, generate_state_invalid_window_size) {
    accel_state *state = NULL;
    int result = 0;

    // Size 0 must fail
    result = accel_generate_state(&state, 1, 0);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Size -1 must fail
    result = accel_generate_state(&state, 1, -1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Size 1 must succeed
    result = accel_generate_state(&state, 1, 1);
    EXPECT_EQ(0, result);
    EXPECT_NE(void_null, state);
}

TEST(AccelFuzzTest, accel_destroy_state_invalid_input) {
    int result = 0;

    // Destroying x (x = NULL) will fail.
    result = accel_destroy_state(NULL);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Destroying x (*x = NULL) will fail.
    accel_state *state = NULL;
    result = accel_destroy_state(&state);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Destroying x (*x != NULL) should succeed.
    state = test_fabricate_1d_state();

    // Destroy the state
    result = accel_destroy_state(&state);
    EXPECT_EQ(0, result);
    EXPECT_EQ(void_null, state);
}

TEST(AccelFuzzTest, accel_start_record_gesture_invalid_input) {
    int result = 0;
    int gesture_id = 0;
    result = accel_start_record_gesture(NULL, &gesture_id);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    accel_state *state = test_fabricate_1d_state();

    result = accel_start_record_gesture(state, NULL);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    test_burn_state(&state);
}

TEST(AccelFuzzTest, accel_end_record_gesture_invalid_input) {
    int result = 0;
    int gesture_id = 0;
    accel_state *state = NULL;

    // Null state:
    result = accel_end_record_gesture(NULL, 1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Negative index:
    state = test_fabricate_1d_state();
    result = accel_end_record_gesture(state, -1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);

    // Unused index:
    state = test_fabricate_1d_state();
    result = accel_end_record_gesture(state, 1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);

    // Verify it works for valid indexes.
    state = test_fabricate_1d_state();
    int gesture = 123;
    result = accel_start_record_gesture(state, &gesture);
    EXPECT_NE(123, gesture);
    EXPECT_EQ(0, result);
    result = accel_end_record_gesture(state, gesture);
    EXPECT_EQ(0, result)  << "gesture " << gesture << " couldn't be recorded correctly" << std::endl;
    test_burn_state(&state);
}

TEST(AccelFuzzTest, accel_process_timer_tick_invalid_input) {
    int result = 0;
    int accel_data = 0;
    accel_state *state = NULL;

    // Null state value.
    result = accel_process_timer_tick(NULL, &accel_data);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Null array input.
    state = test_fabricate_1d_state();
    result = accel_process_timer_tick(state, NULL);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);
}

TEST(AccelFuzzTest, accel_find_most_likely_gesture_invalid_input) {
    int result = 0;
    int gesture_id = 0;
    int affinity = 0;
    accel_state *state = NULL;

    // Null state:
    result = accel_find_most_likely_gesture(NULL, &gesture_id, &affinity);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Null gesture id is passed in.
    state = test_fabricate_1d_state();
    result = accel_find_most_likely_gesture(state, NULL, &affinity);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);

    // Null affinity is passed in.
    state = test_fabricate_1d_state();
    result = accel_find_most_likely_gesture(state, &gesture_id, NULL);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);

    // No tests exist, but otherwise valid parameters.
    state = test_fabricate_1d_state();
    result = accel_find_most_likely_gesture(state, &gesture_id, &affinity);
    EXPECT_EQ(ACCEL_NO_VALID_GESTURE, gesture_id);
    EXPECT_EQ(ACCEL_NO_VALID_GESTURE, affinity);
    EXPECT_EQ(ACCEL_NO_VALID_GESTURE, result);
}

TEST(AccelTest, accel_generate_and_destroy) {
    int result = 0;
    accel_state *state = NULL;
    for (int i=1; i<10; ++i) {
        EXPECT_EQ(void_null, state) << "i = " << i;
        EXPECT_EQ(0, accel_generate_state(&state, 2*i, i)) << "i = " << i;
        EXPECT_EQ(0, accel_destroy_state(&state)) << "i = " << i;
        EXPECT_EQ(void_null, state) << "i = " << i;
    }
}

TEST(AccelTest, start_recording_and_close_many_gestures) {
    int result = 0;
    accel_state *state = NULL;
    state = test_fabricate_1d_state();

    for (int i=0; i<10; ++i) {
        int gesture = 0;
        EXPECT_EQ(0, accel_start_record_gesture(state, &gesture));
        EXPECT_EQ(i, gesture);
    }
    for (int i=0; i<10; ++i) {
        EXPECT_EQ(0, accel_end_record_gesture(state, i));
    }
    test_burn_state(&state);
}

TEST(AccelTest, record_incredibly_long_sequence) {
    int result = 0;
    accel_state *state = NULL;
    state = test_fabricate_1d_state();

    int gesture = 0;
    EXPECT_EQ(0, accel_start_record_gesture(state, &gesture));
    EXPECT_EQ(0, gesture);

    int data[] = {1};
    for (int i=0; i<10000; ++i) {
        EXPECT_EQ(0, accel_process_timer_tick(state, data));
    }

    EXPECT_EQ(0, accel_end_record_gesture(state, gesture));
    test_burn_state(&state);
}

TEST(AccelTest, end_to_end_test_single_recording) {
    int result = 0;
    accel_state *state = NULL;
    state = test_fabricate_1d_state();

    int gesture = 0;
    EXPECT_EQ(0, accel_start_record_gesture(state, &gesture));
    EXPECT_EQ(0, gesture);

    int data[] = {1};
    for (int i=0; i<10; ++i) {
        data[0] = i;
        EXPECT_EQ(0, accel_process_timer_tick(state, data));
    }

    EXPECT_EQ(0, accel_end_record_gesture(state, gesture));

    int prev_affinity = 0;
    for (int i=0; i<10; ++i) {
        data[0] = i;
        int gesture_found = 1;
        int affinity_of_gesture = 1;
        ASSERT_EQ(0, accel_process_timer_tick(state, data));
        ASSERT_EQ(0, accel_find_most_likely_gesture(state, &gesture_found, &affinity_of_gesture));
        ASSERT_EQ(gesture, gesture_found);
        if (i != 0) {
            ASSERT_LT(affinity_of_gesture, prev_affinity) << "i=" << i;
        }
        prev_affinity = affinity_of_gesture;
    }

    test_burn_state(&state);
}

int main (int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);

    int returnValue;

    //Do whatever setup here you will need for your tests here
    //
    //

    returnValue =  RUN_ALL_TESTS();

    //Do Your teardown here if required
    //
    //

    return returnValue;
}
<commit_msg>This algorithm works within some constant bounds<commit_after>#include "gtest/gtest.h"

#include "../src/accel.h"

const void * void_null = NULL;

accel_state *test_fabricate_state(int dimensions) {
    accel_state *state = NULL;
    int result = accel_generate_state(&state, dimensions, 1);
    EXPECT_EQ(0, result);
    EXPECT_NE(void_null, state);
    return state;
}

accel_state *test_fabricate_1d_state() {
    return test_fabricate_state(1);
}

accel_state *test_fabricate_3d_state() {
    return test_fabricate_state(3);
}

void test_burn_state(accel_state ** state) {
    int result = accel_destroy_state(state);
    EXPECT_EQ(0, result);
    EXPECT_EQ(void_null, *state);
}

TEST(AccelFuzzTest, generate_state_null_state) {
    int result = accel_generate_state(NULL, 3, 1);
    EXPECT_EQ(result, ACCEL_PARAM_ERROR);
}

TEST(AccelFuzzTest, generate_state_negative_or_zero_dimensions) {
    accel_state *state = NULL;
    // 0 dimensions must fail
    int result = accel_generate_state(&state, 0, 1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // -1 dimension must fail
    result = accel_generate_state(&state, -1, 1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // 1 dimension must succeed.
    result = accel_generate_state(&state, 1, 1);
    EXPECT_EQ(0, result);
    // TODO: result's memory is leaked :s
}

TEST(AccelFuzzTest, generate_state_invalid_window_size) {
    accel_state *state = NULL;
    int result = 0;

    // Size 0 must fail
    result = accel_generate_state(&state, 1, 0);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Size -1 must fail
    result = accel_generate_state(&state, 1, -1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Size 1 must succeed
    result = accel_generate_state(&state, 1, 1);
    EXPECT_EQ(0, result);
    EXPECT_NE(void_null, state);
}

TEST(AccelFuzzTest, accel_destroy_state_invalid_input) {
    int result = 0;

    // Destroying x (x = NULL) will fail.
    result = accel_destroy_state(NULL);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Destroying x (*x = NULL) will fail.
    accel_state *state = NULL;
    result = accel_destroy_state(&state);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Destroying x (*x != NULL) should succeed.
    state = test_fabricate_1d_state();

    // Destroy the state
    result = accel_destroy_state(&state);
    EXPECT_EQ(0, result);
    EXPECT_EQ(void_null, state);
}

TEST(AccelFuzzTest, accel_start_record_gesture_invalid_input) {
    int result = 0;
    int gesture_id = 0;
    result = accel_start_record_gesture(NULL, &gesture_id);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    accel_state *state = test_fabricate_1d_state();

    result = accel_start_record_gesture(state, NULL);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    test_burn_state(&state);
}

TEST(AccelFuzzTest, accel_end_record_gesture_invalid_input) {
    int result = 0;
    int gesture_id = 0;
    accel_state *state = NULL;

    // Null state:
    result = accel_end_record_gesture(NULL, 1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Negative index:
    state = test_fabricate_1d_state();
    result = accel_end_record_gesture(state, -1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);

    // Unused index:
    state = test_fabricate_1d_state();
    result = accel_end_record_gesture(state, 1);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);

    // Verify it works for valid indexes.
    state = test_fabricate_1d_state();
    int gesture = 123;
    result = accel_start_record_gesture(state, &gesture);
    EXPECT_NE(123, gesture);
    EXPECT_EQ(0, result);
    result = accel_end_record_gesture(state, gesture);
    EXPECT_EQ(0, result)  << "gesture " << gesture << " couldn't be recorded correctly" << std::endl;
    test_burn_state(&state);
}

TEST(AccelFuzzTest, accel_process_timer_tick_invalid_input) {
    int result = 0;
    int accel_data = 0;
    accel_state *state = NULL;

    // Null state value.
    result = accel_process_timer_tick(NULL, &accel_data);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Null array input.
    state = test_fabricate_1d_state();
    result = accel_process_timer_tick(state, NULL);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);
}

TEST(AccelFuzzTest, accel_find_most_likely_gesture_invalid_input) {
    int result = 0;
    int gesture_id = 0;
    int affinity = 0;
    accel_state *state = NULL;

    // Null state:
    result = accel_find_most_likely_gesture(NULL, &gesture_id, &affinity);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);

    // Null gesture id is passed in.
    state = test_fabricate_1d_state();
    result = accel_find_most_likely_gesture(state, NULL, &affinity);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);

    // Null affinity is passed in.
    state = test_fabricate_1d_state();
    result = accel_find_most_likely_gesture(state, &gesture_id, NULL);
    EXPECT_EQ(ACCEL_PARAM_ERROR, result);
    test_burn_state(&state);

    // No tests exist, but otherwise valid parameters.
    state = test_fabricate_1d_state();
    result = accel_find_most_likely_gesture(state, &gesture_id, &affinity);
    EXPECT_EQ(ACCEL_NO_VALID_GESTURE, gesture_id);
    EXPECT_EQ(ACCEL_NO_VALID_GESTURE, affinity);
    EXPECT_EQ(ACCEL_NO_VALID_GESTURE, result);
}

TEST(AccelTest, accel_generate_and_destroy) {
    int result = 0;
    accel_state *state = NULL;
    for (int i=1; i<10; ++i) {
        EXPECT_EQ(void_null, state) << "i = " << i;
        EXPECT_EQ(0, accel_generate_state(&state, 2*i, i)) << "i = " << i;
        EXPECT_EQ(0, accel_destroy_state(&state)) << "i = " << i;
        EXPECT_EQ(void_null, state) << "i = " << i;
    }
}

TEST(AccelTest, start_recording_and_close_many_gestures) {
    int result = 0;
    accel_state *state = NULL;
    state = test_fabricate_1d_state();

    for (int i=0; i<10; ++i) {
        int gesture = 0;
        EXPECT_EQ(0, accel_start_record_gesture(state, &gesture));
        EXPECT_EQ(i, gesture);
    }
    for (int i=0; i<10; ++i) {
        EXPECT_EQ(0, accel_end_record_gesture(state, i));
    }
    test_burn_state(&state);
}

TEST(AccelTest, record_incredibly_long_sequence) {
    int result = 0;
    accel_state *state = NULL;
    state = test_fabricate_1d_state();

    int gesture = 0;
    EXPECT_EQ(0, accel_start_record_gesture(state, &gesture));
    EXPECT_EQ(0, gesture);

    int data[] = {1};
    for (int i=0; i<10000; ++i) {
        EXPECT_EQ(0, accel_process_timer_tick(state, data));
    }

    EXPECT_EQ(0, accel_end_record_gesture(state, gesture));
    test_burn_state(&state);
}

TEST(AccelTest, end_to_end_test_single_recording) {
    int result = 0;
    accel_state *state = NULL;
    state = test_fabricate_1d_state();

    int gesture = 0;
    EXPECT_EQ(0, accel_start_record_gesture(state, &gesture));
    EXPECT_EQ(0, gesture);

    int data[] = {1};
    for (int i=0; i<10; ++i) {
        data[0] = i;
        EXPECT_EQ(0, accel_process_timer_tick(state, data));
    }

    EXPECT_EQ(0, accel_end_record_gesture(state, gesture));

    int prev_affinity = 0;
    for (int i=0; i<10; ++i) {
        data[0] = i;
        int gesture_found = 1;
        int affinity_of_gesture = 1;
        ASSERT_EQ(0, accel_process_timer_tick(state, data));
        ASSERT_EQ(0, accel_find_most_likely_gesture(state, &gesture_found, &affinity_of_gesture));
        ASSERT_EQ(gesture, gesture_found);
        if (i != 0) {
            ASSERT_LT(affinity_of_gesture, prev_affinity) << "i=" << i;
        }
        prev_affinity = affinity_of_gesture;
    }

    test_burn_state(&state);
}

TEST(AccelTest, end_to_end_test_multiple_recordings) {
    // g_1(x) = x, g_2(x) = x. Sample data is f(x) = 2x, we want to ensure that g_1 is chosen.
    int result = 0;
    accel_state *state = NULL;
    state = test_fabricate_1d_state();

    int first_gesture = 0;
    EXPECT_EQ(0, accel_start_record_gesture(state, &first_gesture));
    EXPECT_EQ(0, first_gesture);

    int data[] = {1};
    for (int i=0; i<10; ++i) {
        data[0] = i;
        EXPECT_EQ(0, accel_process_timer_tick(state, data));
    }

    EXPECT_EQ(0, accel_end_record_gesture(state, first_gesture));

    int second_gesture = 0;
    EXPECT_EQ(0, accel_start_record_gesture(state, &second_gesture));
    EXPECT_NE(first_gesture, second_gesture);

    for (int i=0; i<10; ++i) {
        data[0] = i*i;
        EXPECT_EQ(0, accel_process_timer_tick(state, data));
    }

    EXPECT_EQ(0, accel_end_record_gesture(state, second_gesture));

    int prev_affinity = 0;
    for (int i=0; i<10; ++i) {
        data[0] = i*2;
        int gesture_found = 1;
        int affinity_of_gesture = 1;
        ASSERT_EQ(0, accel_process_timer_tick(state, data));
        ASSERT_EQ(0, accel_find_most_likely_gesture(state, &gesture_found, &affinity_of_gesture));
        ASSERT_EQ(first_gesture, gesture_found);
        prev_affinity = affinity_of_gesture;
    }

    test_burn_state(&state);
}

int main (int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);

    int returnValue;

    //Do whatever setup here you will need for your tests here
    //
    //

    returnValue =  RUN_ALL_TESTS();

    //Do Your teardown here if required
    //
    //

    return returnValue;
}
<|endoftext|>