<commit_before>#include <atomic>
#include <string>
#include <gtest/gtest.h>
#include <teelogging/teelogging.h>
#include "../shell.h"
#include "../coroutine.h"
#include "../scheduler.h"
#include "../semaphore.h"
#include "../channel.h"

class CoroTest : testing::Test { };

using namespace cu;

TEST(CoroTest, Test_find)
{
	cu::scheduler sch;

	LOGI("---------- begin find test --------");
	cu::channel<std::string> c1(sch, 20);
	c1.pipeline(	  find()
			, grep("*.h")
			, cat()
			, replace("class", "object")
			, log() );
	c1("../..");
	LOGI("---------- end find test --------");
}

TEST(CoroTest, Test_run_ls_strip_quote_grep)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  run()
			, strip()
			, quote()
			, grep("shell_*")
			, assert_count(1)
			, assert_string("\"shell_exe\"")
			, log()
	);
	c1("ls .");

	/////////////////////////////////

	cu::channel<std::string> c2(sch, 100);
	c2.pipeline(
			  ls()
			, strip()
			, quote()
			, grep("shell_*")
			, assert_count(1)
			, assert_string("\"shell_exe\"")
			, log()
	);
	c2(".");
}

TEST(CoroTest, Test_run_ls_sort_grep_uniq_join)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	std::string out_subproces;
	c1.pipeline(run(), strip(), sort(), grep("*fes*"), uniq(), join(), log(), out(out_subproces));
	c1("ls .");
	//
	cu::channel<std::string> c2(sch, 100);
	std::string out_ls;
	c2.pipeline(ls(), sort(), grep("*fes*"), uniq(), join(), log(), out(out_ls));
	c2(".");
	//
	ASSERT_STREQ(out_subproces.c_str(), out_ls.c_str());
}

TEST(CoroTest, TestCut)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(0)
			, assert_count(1)
			, assert_string("hello")
	);
	c1("hello big world");

	cu::channel<std::string> c2(sch, 100);
	c2.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(1)
			, assert_count(1)
			, assert_string("big")
	);
	c2("hello big world");

	cu::channel<std::string> c3(sch, 100);
	c3.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(2)
			, assert_count(1)
			, assert_string("world")
	);
	c3("hello big world");
}

TEST(CoroTest, TestGrep)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  split("\n")
			, assert_count(3)
			, grep("line2")
			, assert_count(1)
			, assert_string("line2")
	);
	c1("line1\nline2\nline3");
}

TEST(CoroTest, TestGrep2)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  split("\n")
			, assert_count(4)
	);
	c1("line1\nline2\nline3\n");
}

// TEST(CoroTest, TestCount)
// {
// 	cu::scheduler sch;
//
// 	cu::channel<std::string> c1(sch, 100);
// 	int result;
// 	c1.pipeline(
// 			  split("\n")
// 			, count()
// 			, out(result)
// 	);
// 	c1("line1\nline2\nline3");
// 	ASSERT_EQ(result, 3) << "maybe count() is not working well";
// }

TEST(CoroTest, TestUpper)
{
	cu::scheduler sch;
	cu::channel<std::string> c1(sch, 10);
	c1.pipeline( replace("mundo", "gente"), toupper() );
	sch.spawn([&](auto& yield) {
		for(int x=1; x<=1; ++x)
		{
			LOGI("sending hola mundo");
			c1(yield, "hola mundo");
		}
		c1.close(yield);
	});
	sch.spawn([&](auto& yield) {
		LOGI("begin");
		for(auto& r : cu::range(yield, c1))
		{
			LOGI("recv %s", r.c_str());
			// ASSERT_STREQ("HOLA GENTE", r.c_str());
		}
		LOGI("end");
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler2)
{
	cu::scheduler sch;

	cu::channel<int> c1(sch, 10);
	cu::channel<int> c2(sch, 10);
	cu::channel<int> c3(sch, 10);

	c1.pipeline(
		[]() -> cu::channel<int>::link
		{
			return [](auto&& source, auto&& yield)
			{
				for (auto& s : source)
				{
					if(s)
						yield(*s);
					else
						yield(s);
				}
			};
		}()
	);
	c2.pipeline(
		[]() -> cu::channel<int>::link
		{
			return [](auto&& source, auto&& yield)
			{
				for (auto& s : source)
				{
					if(s)
						yield(*s);
					else
						yield(s);
				}
			};
		}()
	);
	c3.pipeline(
		[]() -> cu::channel<int>::link
		{
			return [](auto&& source, auto&& yield)
			{
				int total = 0;
				int count = 0;
				for (auto& s : source)
				{
					if(s)
					{
						total += *s;
						++count;
					}
					else
						yield(s);
				}
				LOGI("---> media es %f", float(total) / count);
				yield( int(total / count) );
			};
		}()
	);

	sch.spawn([&](auto& yield)
	{
		for(int x=1; x<=106; ++x)
		{
			LOGI("1. send %d", x);
			c1(yield, x);
		}
		c1.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		for(int y=1; y<=106; ++y)
		{
			LOGI("2. send %d", y);
			c2(yield, y);
		}
		c2.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		int a, b;
		for(auto& t : cu::range(yield, c1, c2))
		{
			std::tie(a, b) = t;
			LOGI("3. recv and resend %d", a+b);
			c3(yield, a + b);
		}
		c3.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		for(auto& r : cu::range(yield, c3))
		{
			LOGI("4. result = %d", r);
		}
	});
	sch.run_until_complete();
}
<commit_msg>Update test_shell.cpp<commit_after>#include <atomic>
#include <string>
#include <gtest/gtest.h>
#include <teelogging/teelogging.h>
#include "../shell.h"
#include "../coroutine.h"
#include "../scheduler.h"
#include "../semaphore.h"
#include "../channel.h"

class CoroTest : testing::Test { };

using namespace cu;

TEST(CoroTest, Test_find)
{
	cu::scheduler sch;

	LOGI("---------- begin find test --------");
	cu::channel<std::string> c1(sch, 20);
	c1.pipeline(	  find()
			, grep("*.h")
			, cat()
			, replace("class", "object")
			, log() );
	c1("../..");
	LOGI("---------- end find test --------");
}

TEST(CoroTest, Test_run_ls_strip_quote_grep)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  run()
			, strip()
			, quote()
			, grep("shell_*")
			, assert_count(1)
			, assert_string("\"shell_exe\"")
			, log()
	);
	c1("ls .");

	/////////////////////////////////

	cu::channel<std::string> c2(sch, 100);
	c2.pipeline(
			  ls()
			, strip()
			, quote()
			, grep("shell_*")
			, assert_count(1)
			, assert_string("\"shell_exe\"")
			, log()
	);
	c2(".");
}

TEST(CoroTest, Test_run_ls_sort_grep_uniq_join)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	std::string out_subproces;
	c1.pipeline(run(), strip(), sort(), grep("*fes*"), uniq(), join(), log(), out(out_subproces));
	c1("ls .");
	//
	cu::channel<std::string> c2(sch, 100);
	std::string out_ls;
	c2.pipeline(ls(), sort(), grep("*fes*"), uniq(), join(), log(), out(out_ls));
	c2(".");
	//
	ASSERT_STREQ(out_subproces.c_str(), out_ls.c_str());
}

TEST(CoroTest, TestCut)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(0)
			, assert_count(1)
			, assert_string("hello")
	);
	c1("hello big world");

	cu::channel<std::string> c2(sch, 100);
	c2.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(1)
			, assert_count(1)
			, assert_string("big")
	);
	c2("hello big world");

	cu::channel<std::string> c3(sch, 100);
	c3.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(2)
			, assert_count(1)
			, assert_string("world")
	);
	c3("hello big world");
}

TEST(CoroTest, TestGrep)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  split("\n")
			, assert_count(3)
			, grep("line2")
			, assert_count(1)
			, assert_string("line2")
	);
	c1("line1\nline2\nline3");
}

TEST(CoroTest, TestGrep2)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  split("\n")
			, assert_count(4)
	);
	c1("line1\nline2\nline3\n");
}

// TEST(CoroTest, TestCount)
// {
// 	cu::scheduler sch;
//
// 	cu::channel<std::string> c1(sch, 100);
// 	int result;
// 	c1.pipeline(
// 			  split("\n")
// 			, count()
// 			, out(result)
// 	);
// 	c1("line1\nline2\nline3");
// 	ASSERT_EQ(result, 3) << "maybe count() is not working well";
// }

TEST(CoroTest, TestUpper)
{
	cu::scheduler sch;
	cu::channel<std::string> c1(sch, 10);
	c1.pipeline( replace("mundo", "gente"), toupper() );
	sch.spawn([&](auto& yield) {
		for(int x=1; x<=1; ++x)
		{
			LOGI("sending hola mundo");
			c1(yield, "hola mundo");
		}
		c1.close(yield);
	});
	sch.spawn([&](auto& yield) {
		LOGI("begin");
		for(auto& r : cu::range(yield, c1))
		{
			LOGI("recv %s", r.c_str());
			// ASSERT_STREQ("HOLA GENTE", r.c_str());
		}
		LOGI("end");
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler2)
{
	cu::scheduler sch;

	cu::channel<int> c1(sch, 10);
	cu::channel<int> c2(sch, 10);
	cu::channel<int> c3(sch, 4);

	c1.pipeline(
		[]() -> cu::channel<int>::link
		{
			return [](auto&& source, auto&& yield)
			{
				for (auto& s : source)
				{
					if(s)
						yield(*s);
					else
						yield(s);
				}
			};
		}()
	);
	c2.pipeline(
		[]() -> cu::channel<int>::link
		{
			return [](auto&& source, auto&& yield)
			{
				for (auto& s : source)
				{
					if(s)
						yield(*s);
					else
						yield(s);
				}
			};
		}()
	);
	c3.pipeline(
		[]() -> cu::channel<int>::link
		{
			return [](auto&& source, auto&& yield)
			{
				int total = 0;
				int count = 0;
				for (auto& s : source)
				{
					if(s)
					{
						total += *s;
						++count;
						yield(*s);
					}
					else
						yield(s);
				}
				LOGI("---> media es %f", float(total) / count);
				yield( int(total / count) );
			};
		}()
	);

	sch.spawn([&](auto& yield)
	{
		for(int x=1; x<=106; ++x)
		{
			LOGI("1. send %d", x);
			c1(yield, x);
		}
		c1.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		for(int y=1; y<=106; ++y)
		{
			LOGI("2. send %d", y);
			c2(yield, y);
		}
		c2.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		int a, b;
		for(auto& t : cu::range(yield, c1, c2))
		{
			std::tie(a, b) = t;
			LOGI("3. recv and resend %d", a+b);
			c3(yield, a + b);
		}
		c3.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		for(auto& r : cu::range(yield, c3))
		{
			LOGI("4. result = %d", r);
		}
	});
	sch.run_until_complete();
}
<|endoftext|>


<commit_before>/*
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <map>
#include <gtest/gtest.h>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <qi/application.hpp>
#include <qitype/genericobject.hpp>
#include <qitype/genericobjectbuilder.hpp>

using namespace qi;
qiLogCategory("test");

TEST(Value, Ref)
{
  std::string s("foo");
  GenericValueRef r(s);
  r = "bar";
  ASSERT_EQ("bar", s);
  ASSERT_EQ("bar", r.toString());
  ASSERT_ANY_THROW(r = 5);
  double d = 12;
  GenericValueRef rd(d);
  rd = 15;
  ASSERT_EQ(d, 15);
  GenericValuePtr p(&d);
  GenericValueRef vr(p);
  vr = 16;
  ASSERT_EQ(d, 16);
}

TEST(Value, InPlaceSet)
{
  std::string s("foo");
  GenericValuePtr v(&s);
  v.setString("bar");
  ASSERT_EQ("bar", s);
  v.setString("pifpouf");
  ASSERT_EQ("pifpouf", s);
  double d = 12;
  v = GenericValuePtr(&d);
  v.setDouble(15.5);
  ASSERT_EQ(15.5, d);
  v.setInt(16);
  ASSERT_EQ(16.0, d);
  int i = 14;
  v = GenericValuePtr(&i);
  v.setInt(13);
  ASSERT_EQ(13, i);
}

TEST(Value, Update)
{
  std::string s("foo");
  GenericValuePtr v(&s);
  v.update(GenericValueRef("bar"));
  ASSERT_EQ("bar", s);
  v.update(GenericValueRef(std::string("baz")));
  ASSERT_EQ("baz", s);
  ASSERT_ANY_THROW(v.update(GenericValueRef(42)));
  double d = 5.0;
  v = GenericValuePtr(&d);
  v.update(GenericValueRef(42));
  ASSERT_EQ(42, d);
  v.update(GenericValueRef(42.42));
  ASSERT_DOUBLE_EQ(42.42, d);
  ASSERT_ANY_THROW(v.update(GenericValueRef("bar")));
}

TEST(Value, As)
{
  std::string s("foo");
  GenericValuePtr v(&s);
  ASSERT_EQ(&v.asString(), &s);
  ASSERT_ANY_THROW(v.asDouble());
  double d = 1.5;
  v = GenericValuePtr(&d);
  ASSERT_EQ(&d, &v.asDouble());
  ASSERT_ANY_THROW(v.asInt32());
  qi::uint32_t ui = 2; // vcxx uint32_t unqualified is ambiguous.
  v = GenericValuePtr(&ui);
  ASSERT_EQ(&ui, &v.asUInt32());
  ASSERT_ANY_THROW(v.asInt32());
  ASSERT_ANY_THROW(v.asInt16());
}

TEST(Value, Basic)
{
  GenericValuePtr v;
  int twelve = 12;
  v = GenericValueRef(twelve);
  ASSERT_TRUE(v.type);
  ASSERT_TRUE(v.value);
  ASSERT_EQ(v.toInt(), 12);
  ASSERT_EQ(v.toFloat(), 12.0f);
  ASSERT_EQ(v.toDouble(), 12.0);
  double five = 5.0;
  v = GenericValueRef(five);
  ASSERT_EQ(v.toInt(), 5);
  ASSERT_EQ(v.toFloat(), 5.0f);
  ASSERT_EQ(v.toDouble(), 5.0);
  v = GenericValueRef("foo");
  ASSERT_EQ("foo", v.toString());
}

TEST(Value, Map)
{
  std::map<std::string, double> map;
  map["foo"] = 1;
  map["bar"] = 2;
  map["baz"] = 3;
  GenericValuePtr v(&map);
  ASSERT_EQ(3u, v.size());

  ASSERT_EQ(v["foo"].toInt(), 1);
  ASSERT_EQ(v[std::string("bar")].toInt(), 2);

  v["baz"].setInt(4);
  ASSERT_EQ(v["baz"].toInt(), 4);

  // write to ref
  v["baz"] = 5;
  ASSERT_EQ(v["baz"].toInt(), 5);

  // Create a new element
  qiLogDebug() << "Insert bimm";
  ASSERT_ANY_THROW(v[GenericValue::from("bimm")].setString("foo"));
  v["bimm"].setInt(42);
  qiLogDebug() << "Check bimm";
  ASSERT_EQ(v["bimm"].toInt(), 42);
  ASSERT_EQ(v[std::string("bimm")].toInt(), 42);

  // Create a new element of an existing string length
  qiLogDebug() << "Insert pif";
  v[GenericValue::from("pif")].setInt(43);
  qiLogDebug() << "Check pif";
  ASSERT_EQ(v["pif"].toInt(), 43);
  ASSERT_EQ(v[std::string("pif")].toInt(), 43);

  // insert existing
  v.insert("pif", 63);
  ASSERT_EQ(v["pif"].toInt(), 63);

  // insert new
  v.insert("pouf", 65);
  ASSERT_EQ(v["pouf"].toInt(), 65);

  ASSERT_EQ(6u, v.size());
  ASSERT_TRUE(!v.find("nokey").type);
  // wrong keytype
  ASSERT_ANY_THROW(v.find(42));
  // append on map
  ASSERT_ANY_THROW(v.append("foo"));
}

static bool triggered = false;
static void nothing(GenericObject*) {triggered = true;}

TEST(Value, ObjectPtr)
{
  {
    ObjectPtr o((GenericObject*)1, &nothing);
    ASSERT_TRUE(o);
    ASSERT_TRUE(o.get());
    GenericValuePtr v = GenericValueRef(o);
    qi::ObjectPtr out = v.to<ObjectPtr>();
    ASSERT_TRUE(out);
    ASSERT_EQ(o.get(), out.get());
    out = v.toObject();
    ASSERT_TRUE(out);
    ASSERT_EQ(o.get(), out.get());
  }
  ASSERT_TRUE(triggered);
}

TEST(Value, list)
{
  int one = 1;
  GenericValuePtr v = GenericValueRef(one);
  v.set(5);
  ASSERT_ANY_THROW(v.set("foo"));
  ASSERT_ANY_THROW(v.set(std::vector<int>()));
  std::vector<int> vint;
  vint.push_back(12);
  v = GenericValuePtr(&vint);
  v.append(7);
  ASSERT_EQ(7, v[1].toInt());
  ASSERT_EQ(7, v[1].toFloat());
  ASSERT_EQ(7, v.element<int>(1));
  v[1].setInt(8);
  ASSERT_EQ(8, v[1].toInt());
  v.element<int>(1) = 9;
  ASSERT_EQ(9, v[1].toInt());
  ASSERT_ANY_THROW(v.element<double>(1)); // wrong type
  ASSERT_ANY_THROW(v.element<int>(17));   // out of bound
  EXPECT_EQ(v.as<std::vector<int> >().size(), v.size());
}

struct TStruct
{
  double d;
  std::string s;
  bool operator ==(const TStruct& b) const { return d == b.d && s == b.s;}
};
struct Point
{
  int x,y;
  bool operator ==(const Point& b) const { return x==b.x && y == b.y;}
};

QI_TYPE_STRUCT(TStruct, d, s);
QI_TYPE_STRUCT(Point, x, y);

TEST(Value, Tuple)
{
  // Create a Dynamic tuple from vector
  std::vector<GenericValue> v;
  GenericValuePtr gv(&v);
  gv.append(GenericValue::from(12.0));
  gv.append(GenericValue::from("foo")); // cstring not std::string
  GenericValue gtuple = gv.toTuple(false);
  TStruct t;
  t.d = 12.0;
  t.s = "foo";
  TStruct gtupleconv = gtuple.to<TStruct>();
  ASSERT_EQ(t, gtupleconv);
  gtuple[0].setDouble(15);
  ASSERT_EQ(15, gtuple.to<TStruct>().d);

  // create a static tuple
  std::vector<double> vd;
  vd.push_back(1);
  gv = GenericValuePtr(&vd);
  gv.append(2);
  gtuple = gv.toTuple(true);
  Point p;
  p.x = 1;
  p.y = 2;
  p == p;
  ASSERT_EQ(p , gtuple.to<Point>());
  p.x = 3;
  gtuple[0].setDouble(gtuple[0].toDouble() + 2);
  ASSERT_EQ(p, gtuple.to<Point>());
}

TEST(Value, DefaultMap)
{ // this one has tricky code and deserves a test)
  Type* dmt = Type::fromSignature("{si}");
  GenericValue val = GenericValue(GenericValuePtr(dmt), false, true);
  ASSERT_EQ(0u, val.size());
  val["foo"] = 12;
  ASSERT_EQ(1u, val.size());
  ASSERT_EQ(12, val.find("foo").toInt());
  ASSERT_EQ(0, val.find("bar").type);
  val.insert(std::string("bar"), 13);
  ASSERT_EQ(13, val.element<int>("bar"));
  val.element<int>("foo") = 10;
  ASSERT_EQ(10, val.find("foo").toInt());
  GenericIterator b = val.begin();
  GenericIterator end = val.end();
  int sum = 0;
  while (b != end)
  {
    GenericValueRef deref = *b;
    sum += deref[1].toInt();
    ++b;
  }
  ASSERT_EQ(23, sum);

  GenericValuePtr valCopy = val.clone();
  ASSERT_EQ(13, valCopy.element<int>("bar"));
  ASSERT_EQ(2u, valCopy.size());
  // reset val, checks valCopy still works
  val.reset();
  val = GenericValue::from(5);
  ASSERT_EQ(13, valCopy.element<int>("bar"));
  ASSERT_EQ(2u, valCopy.size());
  valCopy.destroy();
}

TEST(Value, STL)
{
  std::vector<int> v;
  GenericValuePtr gv(&v);
  gv.append(1);
  gv.append(3);
  gv.append(2);
  std::vector<int> w;
  // seems there are overloads for push_back, need to explicitly cast to signature
  std::for_each(gv.begin(), gv.end(),
    boost::lambda::bind((void (std::vector<int>::*)(const int&))&std::vector<int>::push_back,
      boost::ref(w),
      boost::lambda::bind(&GenericValueRef::toInt, boost::lambda::_1)));
  ASSERT_EQ(3u, w.size());
  ASSERT_EQ(v, w);
  GenericIterator mine = std::min_element(gv.begin(), gv.end());
  ASSERT_EQ(1, (*mine).toInt());
  mine = std::find_if(gv.begin(), gv.end(),
    boost::lambda::bind(&GenericValueRef::toInt, boost::lambda::_1) == 3);
  ASSERT_EQ(3, (*mine).toInt());
  (*mine).setInt(4);
  ASSERT_EQ(4, v[1]);
  mine = std::find_if(gv.begin(), gv.end(),
    boost::lambda::bind(&GenericValueRef::toInt, boost::lambda::_1) == 42);
  ASSERT_EQ(mine, gv.end());

  std::vector<int> v2;
  v2.push_back(10);
  v2.push_back(1);
  v2.push_back(100);
  // v has correct size
  std::copy(v2.begin(), v2.end(), gv.begin());
  ASSERT_EQ(v2, v);
  // copy other-way-round requires cast from GenericValueRef to int

  std::vector<GenericValueRef> vg;
  vg.insert(vg.end(), v.begin(), v.end());
  std::sort(vg.begin(), vg.end());
  ASSERT_EQ(321, vg[0].toInt() + vg[1].toInt()*2 + vg[2].toInt() * 3);
}

TEST(Value, Overflow)
{
  {
    long long test = 0xFFFF11223344LL; // paint the stack with nonzero
  }
  long twelve = 12;
  GenericValueRef(12).to<char>();
  ASSERT_EQ(12,GenericValueRef(twelve).to<int64_t>());
  GenericValueRef(127).to<char>();
  ASSERT_ANY_THROW(GenericValueRef(128).to<char>());
  GenericValueRef(-128).to<char>();
  ASSERT_ANY_THROW(GenericValueRef(254).to<char>());
  GenericValueRef(255).to<unsigned char>();
  GenericValueRef(0xFF11223344).to<qi::int64_t>();
  GenericValueRef(0xFF11223344).to<qi::uint64_t>();
  ASSERT_ANY_THROW(GenericValueRef(256).to<unsigned char>());
  ASSERT_ANY_THROW(GenericValueRef(-1).to<unsigned char>());
  // check correct sign propagation
  ASSERT_EQ(-120, GenericValueRef((char)-120).to<int>());
  GenericValueRef(12).to<double>();
  GenericValueRef(13).to<float>();
  ASSERT_ANY_THROW(GenericValueRef(-5.0).to<unsigned int>());
  ASSERT_ANY_THROW(GenericValueRef(-5.0f).to<unsigned int>());
  ASSERT_ANY_THROW(GenericValueRef(-256.0).to<char>());
  ASSERT_ANY_THROW(GenericValueRef(256.0).to<unsigned char>());
  ASSERT_ANY_THROW(GenericValueRef(1.0e80).to<qi::uint64_t>());
  ASSERT_TRUE(GenericValueRef((qi::uint32_t)0xFF223344).to<double>() > 0);
  ASSERT_TRUE(GenericValueRef((qi::int32_t)0xFF223344).to<double>() < 0);
  ASSERT_TRUE(GenericValueRef(0xFF22334455667788ULL).to<double>() > 0);
  ASSERT_TRUE(GenericValueRef((qi::int64_t)0xFF22334455667788).to<double>() < 0);
  // check other access path
  ASSERT_ANY_THROW(GenericValue::make<char>().setInt(128));
  ASSERT_ANY_THROW(GenericValue::make<char>().update(GenericValueRef(128)));
}

int main(int argc, char **argv) {
  qi::Application app(argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>win32 fix: ambiguous unqualified int64_t in test_value.<commit_after>/*
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <map>
#include <gtest/gtest.h>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <qi/application.hpp>
#include <qitype/genericobject.hpp>
#include <qitype/genericobjectbuilder.hpp>

using namespace qi;
qiLogCategory("test");

TEST(Value, Ref)
{
  std::string s("foo");
  GenericValueRef r(s);
  r = "bar";
  ASSERT_EQ("bar", s);
  ASSERT_EQ("bar", r.toString());
  ASSERT_ANY_THROW(r = 5);
  double d = 12;
  GenericValueRef rd(d);
  rd = 15;
  ASSERT_EQ(d, 15);
  GenericValuePtr p(&d);
  GenericValueRef vr(p);
  vr = 16;
  ASSERT_EQ(d, 16);
}

TEST(Value, InPlaceSet)
{
  std::string s("foo");
  GenericValuePtr v(&s);
  v.setString("bar");
  ASSERT_EQ("bar", s);
  v.setString("pifpouf");
  ASSERT_EQ("pifpouf", s);
  double d = 12;
  v = GenericValuePtr(&d);
  v.setDouble(15.5);
  ASSERT_EQ(15.5, d);
  v.setInt(16);
  ASSERT_EQ(16.0, d);
  int i = 14;
  v = GenericValuePtr(&i);
  v.setInt(13);
  ASSERT_EQ(13, i);
}

TEST(Value, Update)
{
  std::string s("foo");
  GenericValuePtr v(&s);
  v.update(GenericValueRef("bar"));
  ASSERT_EQ("bar", s);
  v.update(GenericValueRef(std::string("baz")));
  ASSERT_EQ("baz", s);
  ASSERT_ANY_THROW(v.update(GenericValueRef(42)));
  double d = 5.0;
  v = GenericValuePtr(&d);
  v.update(GenericValueRef(42));
  ASSERT_EQ(42, d);
  v.update(GenericValueRef(42.42));
  ASSERT_DOUBLE_EQ(42.42, d);
  ASSERT_ANY_THROW(v.update(GenericValueRef("bar")));
}

TEST(Value, As)
{
  std::string s("foo");
  GenericValuePtr v(&s);
  ASSERT_EQ(&v.asString(), &s);
  ASSERT_ANY_THROW(v.asDouble());
  double d = 1.5;
  v = GenericValuePtr(&d);
  ASSERT_EQ(&d, &v.asDouble());
  ASSERT_ANY_THROW(v.asInt32());
  qi::uint32_t ui = 2; // vcxx uint32_t unqualified is ambiguous.
  v = GenericValuePtr(&ui);
  ASSERT_EQ(&ui, &v.asUInt32());
  ASSERT_ANY_THROW(v.asInt32());
  ASSERT_ANY_THROW(v.asInt16());
}

TEST(Value, Basic)
{
  GenericValuePtr v;
  int twelve = 12;
  v = GenericValueRef(twelve);
  ASSERT_TRUE(v.type);
  ASSERT_TRUE(v.value);
  ASSERT_EQ(v.toInt(), 12);
  ASSERT_EQ(v.toFloat(), 12.0f);
  ASSERT_EQ(v.toDouble(), 12.0);
  double five = 5.0;
  v = GenericValueRef(five);
  ASSERT_EQ(v.toInt(), 5);
  ASSERT_EQ(v.toFloat(), 5.0f);
  ASSERT_EQ(v.toDouble(), 5.0);
  v = GenericValueRef("foo");
  ASSERT_EQ("foo", v.toString());
}

TEST(Value, Map)
{
  std::map<std::string, double> map;
  map["foo"] = 1;
  map["bar"] = 2;
  map["baz"] = 3;
  GenericValuePtr v(&map);
  ASSERT_EQ(3u, v.size());

  ASSERT_EQ(v["foo"].toInt(), 1);
  ASSERT_EQ(v[std::string("bar")].toInt(), 2);

  v["baz"].setInt(4);
  ASSERT_EQ(v["baz"].toInt(), 4);

  // write to ref
  v["baz"] = 5;
  ASSERT_EQ(v["baz"].toInt(), 5);

  // Create a new element
  qiLogDebug() << "Insert bimm";
  ASSERT_ANY_THROW(v[GenericValue::from("bimm")].setString("foo"));
  v["bimm"].setInt(42);
  qiLogDebug() << "Check bimm";
  ASSERT_EQ(v["bimm"].toInt(), 42);
  ASSERT_EQ(v[std::string("bimm")].toInt(), 42);

  // Create a new element of an existing string length
  qiLogDebug() << "Insert pif";
  v[GenericValue::from("pif")].setInt(43);
  qiLogDebug() << "Check pif";
  ASSERT_EQ(v["pif"].toInt(), 43);
  ASSERT_EQ(v[std::string("pif")].toInt(), 43);

  // insert existing
  v.insert("pif", 63);
  ASSERT_EQ(v["pif"].toInt(), 63);

  // insert new
  v.insert("pouf", 65);
  ASSERT_EQ(v["pouf"].toInt(), 65);

  ASSERT_EQ(6u, v.size());
  ASSERT_TRUE(!v.find("nokey").type);
  // wrong keytype
  ASSERT_ANY_THROW(v.find(42));
  // append on map
  ASSERT_ANY_THROW(v.append("foo"));
}

static bool triggered = false;
static void nothing(GenericObject*) {triggered = true;}

TEST(Value, ObjectPtr)
{
  {
    ObjectPtr o((GenericObject*)1, &nothing);
    ASSERT_TRUE(o);
    ASSERT_TRUE(o.get());
    GenericValuePtr v = GenericValueRef(o);
    qi::ObjectPtr out = v.to<ObjectPtr>();
    ASSERT_TRUE(out);
    ASSERT_EQ(o.get(), out.get());
    out = v.toObject();
    ASSERT_TRUE(out);
    ASSERT_EQ(o.get(), out.get());
  }
  ASSERT_TRUE(triggered);
}

TEST(Value, list)
{
  int one = 1;
  GenericValuePtr v = GenericValueRef(one);
  v.set(5);
  ASSERT_ANY_THROW(v.set("foo"));
  ASSERT_ANY_THROW(v.set(std::vector<int>()));
  std::vector<int> vint;
  vint.push_back(12);
  v = GenericValuePtr(&vint);
  v.append(7);
  ASSERT_EQ(7, v[1].toInt());
  ASSERT_EQ(7, v[1].toFloat());
  ASSERT_EQ(7, v.element<int>(1));
  v[1].setInt(8);
  ASSERT_EQ(8, v[1].toInt());
  v.element<int>(1) = 9;
  ASSERT_EQ(9, v[1].toInt());
  ASSERT_ANY_THROW(v.element<double>(1)); // wrong type
  ASSERT_ANY_THROW(v.element<int>(17));   // out of bound
  EXPECT_EQ(v.as<std::vector<int> >().size(), v.size());
}

struct TStruct
{
  double d;
  std::string s;
  bool operator ==(const TStruct& b) const { return d == b.d && s == b.s;}
};
struct Point
{
  int x,y;
  bool operator ==(const Point& b) const { return x==b.x && y == b.y;}
};

QI_TYPE_STRUCT(TStruct, d, s);
QI_TYPE_STRUCT(Point, x, y);

TEST(Value, Tuple)
{
  // Create a Dynamic tuple from vector
  std::vector<GenericValue> v;
  GenericValuePtr gv(&v);
  gv.append(GenericValue::from(12.0));
  gv.append(GenericValue::from("foo")); // cstring not std::string
  GenericValue gtuple = gv.toTuple(false);
  TStruct t;
  t.d = 12.0;
  t.s = "foo";
  TStruct gtupleconv = gtuple.to<TStruct>();
  ASSERT_EQ(t, gtupleconv);
  gtuple[0].setDouble(15);
  ASSERT_EQ(15, gtuple.to<TStruct>().d);

  // create a static tuple
  std::vector<double> vd;
  vd.push_back(1);
  gv = GenericValuePtr(&vd);
  gv.append(2);
  gtuple = gv.toTuple(true);
  Point p;
  p.x = 1;
  p.y = 2;
  p == p;
  ASSERT_EQ(p , gtuple.to<Point>());
  p.x = 3;
  gtuple[0].setDouble(gtuple[0].toDouble() + 2);
  ASSERT_EQ(p, gtuple.to<Point>());
}

TEST(Value, DefaultMap)
{ // this one has tricky code and deserves a test)
  Type* dmt = Type::fromSignature("{si}");
  GenericValue val = GenericValue(GenericValuePtr(dmt), false, true);
  ASSERT_EQ(0u, val.size());
  val["foo"] = 12;
  ASSERT_EQ(1u, val.size());
  ASSERT_EQ(12, val.find("foo").toInt());
  ASSERT_EQ(0, val.find("bar").type);
  val.insert(std::string("bar"), 13);
  ASSERT_EQ(13, val.element<int>("bar"));
  val.element<int>("foo") = 10;
  ASSERT_EQ(10, val.find("foo").toInt());
  GenericIterator b = val.begin();
  GenericIterator end = val.end();
  int sum = 0;
  while (b != end)
  {
    GenericValueRef deref = *b;
    sum += deref[1].toInt();
    ++b;
  }
  ASSERT_EQ(23, sum);

  GenericValuePtr valCopy = val.clone();
  ASSERT_EQ(13, valCopy.element<int>("bar"));
  ASSERT_EQ(2u, valCopy.size());
  // reset val, checks valCopy still works
  val.reset();
  val = GenericValue::from(5);
  ASSERT_EQ(13, valCopy.element<int>("bar"));
  ASSERT_EQ(2u, valCopy.size());
  valCopy.destroy();
}

TEST(Value, STL)
{
  std::vector<int> v;
  GenericValuePtr gv(&v);
  gv.append(1);
  gv.append(3);
  gv.append(2);
  std::vector<int> w;
  // seems there are overloads for push_back, need to explicitly cast to signature
  std::for_each(gv.begin(), gv.end(),
    boost::lambda::bind((void (std::vector<int>::*)(const int&))&std::vector<int>::push_back,
      boost::ref(w),
      boost::lambda::bind(&GenericValueRef::toInt, boost::lambda::_1)));
  ASSERT_EQ(3u, w.size());
  ASSERT_EQ(v, w);
  GenericIterator mine = std::min_element(gv.begin(), gv.end());
  ASSERT_EQ(1, (*mine).toInt());
  mine = std::find_if(gv.begin(), gv.end(),
    boost::lambda::bind(&GenericValueRef::toInt, boost::lambda::_1) == 3);
  ASSERT_EQ(3, (*mine).toInt());
  (*mine).setInt(4);
  ASSERT_EQ(4, v[1]);
  mine = std::find_if(gv.begin(), gv.end(),
    boost::lambda::bind(&GenericValueRef::toInt, boost::lambda::_1) == 42);
  ASSERT_EQ(mine, gv.end());

  std::vector<int> v2;
  v2.push_back(10);
  v2.push_back(1);
  v2.push_back(100);
  // v has correct size
  std::copy(v2.begin(), v2.end(), gv.begin());
  ASSERT_EQ(v2, v);
  // copy other-way-round requires cast from GenericValueRef to int

  std::vector<GenericValueRef> vg;
  vg.insert(vg.end(), v.begin(), v.end());
  std::sort(vg.begin(), vg.end());
  ASSERT_EQ(321, vg[0].toInt() + vg[1].toInt()*2 + vg[2].toInt() * 3);
}

TEST(Value, Overflow)
{
  {
    long long test = 0xFFFF11223344LL; // paint the stack with nonzero
  }
  long twelve = 12;
  GenericValueRef(12).to<char>();
  ASSERT_EQ(12,GenericValueRef(twelve).to<qi::int64_t>());
  GenericValueRef(127).to<char>();
  ASSERT_ANY_THROW(GenericValueRef(128).to<char>());
  GenericValueRef(-128).to<char>();
  ASSERT_ANY_THROW(GenericValueRef(254).to<char>());
  GenericValueRef(255).to<unsigned char>();
  GenericValueRef(0xFF11223344).to<qi::int64_t>();
  GenericValueRef(0xFF11223344).to<qi::uint64_t>();
  ASSERT_ANY_THROW(GenericValueRef(256).to<unsigned char>());
  ASSERT_ANY_THROW(GenericValueRef(-1).to<unsigned char>());
  // check correct sign propagation
  ASSERT_EQ(-120, GenericValueRef((char)-120).to<int>());
  GenericValueRef(12).to<double>();
  GenericValueRef(13).to<float>();
  ASSERT_ANY_THROW(GenericValueRef(-5.0).to<unsigned int>());
  ASSERT_ANY_THROW(GenericValueRef(-5.0f).to<unsigned int>());
  ASSERT_ANY_THROW(GenericValueRef(-256.0).to<char>());
  ASSERT_ANY_THROW(GenericValueRef(256.0).to<unsigned char>());
  ASSERT_ANY_THROW(GenericValueRef(1.0e80).to<qi::uint64_t>());
  ASSERT_TRUE(GenericValueRef((qi::uint32_t)0xFF223344).to<double>() > 0);
  ASSERT_TRUE(GenericValueRef((qi::int32_t)0xFF223344).to<double>() < 0);
  ASSERT_TRUE(GenericValueRef(0xFF22334455667788ULL).to<double>() > 0);
  ASSERT_TRUE(GenericValueRef((qi::int64_t)0xFF22334455667788).to<double>() < 0);
  // check other access path
  ASSERT_ANY_THROW(GenericValue::make<char>().setInt(128));
  ASSERT_ANY_THROW(GenericValue::make<char>().update(GenericValueRef(128)));
}

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


<commit_before>/*Santiago Zubieta*/
#include <iostream>
#include <numeric>
#include <fstream>
#include <climits>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <list>
#include <map>
#include <set>
#include <stack>
#include <deque>
#include <vector>
#include <string>
#include <cstdlib>
#include <cassert>
#include <sstream>
#include <iterator>
#include <algorithm>

using namespace std;

map<string, int> nameToIndex;
// Convert from an authors name to an index between 0 and n - 1 (# of authors)
map<int, string> indexToName;
// Convert from an index between 0 and n - 1 (# of authors) to an authors name

vector< set<int> > authors;
// A graph for keeping track of authors relationships in papers. Lets use a set
// to avoid repeated relationships since there may be more than 1 paper where
// two authors may meet so this allows us to have unique pairings.
vector<bool> visited;
// A visited list for keeping track of already visited authors in graph traver-
// sal. We will do a BFS which will allow to know the shortest distance (if any
// at all) between two authors, specifically, between any author and Paul Erdos
// to find the so called 'Erdos Distance'.
set<string> authorsSet;
// A set containing all the possible authors, to know their unique occurrences
// and give them an index for easy identification and graph traversal. Be care-
// ful that maybe the program may be queried for an author that isn't in any
// paper, therefore it doesn't have an index to search in the graph, so first
// check in this set, and if it isn't here, skip doing the graph search.

string substring(string s, int l, int r) {
// substring of a string s, from index l (inclusive) to index r (exclusive)
    string res = "";
    while(l < r) {
        res += s[l];
        l++;
    }
    return res;
}

int bfs(int start, int end) {
    queue< pair<int, int> > q;
    // The queue will contain several pairs, each one has:
    // first: the current position
    // second: the cost until current position
    q.push(make_pair(start, 0));
    // BFS will start at the original given position, with 0 cost
    while(q.size()) {
        pair<int, int> now = q.front();
        q.pop();
        int    actual = now.first;
        int weight = now.second;
        if(visited[actual]) {
        // This node has been previously reach, ignore it
            continue;
        }
        if(actual == end) {
        // If this node is the target node, return the weight up to this point.
        // The BFS ensures that this weight is the minimal distance possible
        // between the start node and end node.
            return weight;
        }
        visited[actual] = true;
        set<int>::iterator it;
        for(it = authors[actual].begin(); it != authors[actual].end(); it++) {
            int next = *it;
            q.push(make_pair(next, weight + 1));
        }
    }
    return -1;
}

void resetVisited() {
// Every instance of the BFS requires the list of visited nodes to be resetted
    for(int k = 0; k < visited.size(); k++) {
        visited[k] = false;
    }
}

void initGraph() {
// Lets initialize the graph of authors with empty positions for every author
// available, and a visited list also as false, for easy graph traversal later.
    for(int k = 0; k < authorsSet.size(); k++) {
        set<int> coauthors;
        authors.push_back(coauthors);
        visited.push_back(false);
    }
}

string readName() {
// This function reads a single name, until a second comma is found, the line
// breaks, or a ':' is found. Whitespaces are ignored.
    char c;
    string name = "";
    bool firstComma = true;
    // A name has only one comma the second one means there is at least another
    // author ahead in the list of authors of the paper
    while((c = getchar()) != EOF) {
        // Read the character
        if(c != ' ') {
        // White spaces are ignored
            if(c == ',') {
                // If a comma is found
                if(firstComma) {
                // If its the first, add it to the name and then mark that it
                // was found so the second one isn't added
                    name += ", ";
                    firstComma = false;
                }
                else {
                // If its the second, it means the name ends here
                    return name;
                }
            }
            else {
            // If its not a comma
                if(c == ':') {
                // If its a colon add it to the name, it will tell the external
                // routine that this is the last name, there won't be any more.
                    name += c;
                    return name;
                }
                else if(c == '\n'){
                // This is useful when reading just one name in a line, it will
                // end with the \n character, or line break.
                    return name;
                }
                else {
                // If it isn't a space, nor a :, nor a \n, nor a comma, add it.
                    name += c;
                }
            }
        }
    }
}

vector<string> readNames() {
// This will read a list of names in a given paper, terminating when a name co-
// mes up with a ':' at the end, marking the end of the authors and the begin-
// ning of the paper's title. Remove that ':' and store all names in a list.
    vector<string> res;
    while(true) {
        string name = readName();
        if(name[name.length() - 1] == ':') {
        // If this is the last name down the road
            name = substring(name, 0, name.length() - 1);
            res.push_back(name);
            break;
        }
        res.push_back(name);
    }
    while('\n' != getchar());
    return res;
}

int main() {
    int s;
    // The number of scenarios
    int p, n;
    // p: the number of papers
    // n: the number of authors to query for their Erdos Number
    cin >> s;
    for(int z = 0; z < s; z++) {
        authors.clear();
        visited.clear();
        authorsSet.clear();
        cin >> p >> n;
        vector< vector<string> > allPapers;
        // A list of the lists of authors contained in each paper, this is like
        // a graph in string form, which will be converted into a graph in num-
        // erical form for easy and fast traversal.
        getchar();
        for(int k = 0; k < p; k++) {
            // Each read line will be a paper, first with the authors then the
            // paper's title, which will really be irrelevant for this program.
            vector<string> paperAuthors = readNames();
            for(int l = 0; l < paperAuthors.size(); l++) {
                string name = paperAuthors[l];
                authorsSet.insert(name);
            }
            allPapers.push_back(paperAuthors);
        }
        // Now that we have the list of authors in every paper, and a set that
        // contains the unique occurrences, lets iterate over the set to assign
        // each author an iterator so its easy to make a graph with them.
        set<string>::iterator it;
        int index = 0;
        for(it = authorsSet.begin(); it != authorsSet.end(); it++) {
        // Starting from 0, lets assign every unique author its own number, and
        // also keep a record of this in two maps, one that lets retrieve the
        // author's name from the number, and other that lets retrieve the num-
        // ber by using the author's name, helpful for making a graph using on-
        // ly numbers as indices and then getting the names back again
            string name = *it;
            nameToIndex[name] = index;
            indexToName[index] = name;
            index++;
        }
        initGraph();
         for(int i = 0; i < allPapers.size(); i++) {
         // After this, the graph using only indices will be completed
            vector<string> paperAuthors = allPapers[i];
            for(int j = 0; j < paperAuthors.size() - 1; j++) {
                string name1 = paperAuthors[j];
                int index1 = nameToIndex[name1];
                for(int k = j + 1; k < paperAuthors.size(); k++) {
                    string name2 = paperAuthors[k];
                    int index2 = nameToIndex[name2];
                    authors[index1].insert(index2);
                    authors[index2].insert(index1);
                }
            }
        }
        // Now, proceed to read a list of authors to inquire for their Erdos
        // Distance, and then do a BFS to see what is their shortest distance
        // to Paul Erdos (Erdos, P.) if any (infinity in case of none found).
        cout << "Scenario " << z + 1 << endl;
        for(int k = 0; k < n; k++) {
            resetVisited();
            string completeName = readName();
            int res;
            if(authorsSet.find(completeName) != authorsSet.end()) {
            // This is VERY important. Since the idea of making a graph invol-
            // ves giving an index to all the authors, present in the papers,
            // it may be possible that afterwards, there's a query for an au-
            // thor's Erdos Number and that author isn't in any of the availa-
            // ble paper, which would result in a nasty error since that aut-
            // hor isn't available in the map, possibly returning a bogus in-
            // dex. If the author isn't among the authors we know to be pres-
            // ent in the previously read papers, immediately print that its
            // Erdos Distance is infinity.
                int start = nameToIndex[completeName];
                int end = nameToIndex["Erdos, P."];
                res = bfs(start, end);
            }
            else {
            // The queried author isn't even in any of the given papers so the
            // immediate response is that it has infinity as its Erdos Distance
                res = -1;
            }
            if(res == -1) {
                cout << completeName << " infinity" << endl;
            }
            else {
                cout << completeName << " " << res << endl;
            }
        }
    }
}<commit_msg>Added 10044_Erdos_Numbers<commit_after>/*Santiago Zubieta*/
#include <iostream>
#include <numeric>
#include <fstream>
#include <climits>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <list>
#include <map>
#include <set>
#include <stack>
#include <deque>
#include <vector>
#include <string>
#include <cstdlib>
#include <cassert>
#include <sstream>
#include <iterator>
#include <algorithm>

using namespace std;

map<string, int> nameToIndex;
// Convert from an authors name to an index between 0 and n - 1 (# of authors)
map<int, string> indexToName;
// Convert from an index between 0 and n - 1 (# of authors) to an authors name

vector< set<int> > authors;
// A graph for keeping track of authors relationships in papers. Lets use a set
// to avoid repeated relationships since there may be more than 1 paper where
// two authors may meet so this allows us to have unique pairings.
vector<bool> visited;
// A visited list for keeping track of already visited authors in graph traver-
// sal. We will do a BFS which will allow to know the shortest distance (if any
// at all) between two authors, specifically, between any author and Paul Erdos
// to find the so called 'Erdos Distance'.
set<string> authorsSet;
// A set containing all the possible authors, to know their unique occurrences
// and give them an index for easy identification and graph traversal. Be care-
// ful that maybe the program may be queried for an author that isn't in any
// paper, therefore it doesn't have an index to search in the graph, so first
// check in this set, and if it isn't here, skip doing the graph search.

string substring(string s, int l, int r) {
// substring of a string s, from index l (inclusive) to index r (exclusive)
    string res = "";
    while(l < r) {
        res += s[l];
        l++;
    }
    return res;
}

int bfs(int start, int end) {
    queue< pair<int, int> > q;
    // The queue will contain several pairs, each one has:
    // first: the current position
    // second: the cost until current position
    q.push(make_pair(start, 0));
    // BFS will start at the original given position, with 0 cost
    while(q.size()) {
        pair<int, int> now = q.front();
        q.pop();
        int actual = now.first;
        int weight = now.second;
        if(visited[actual]) {
        // This node has been previously reach, ignore it
            continue;
        }
        if(actual == end) {
        // If this node is the target node, return the weight up to this point.
        // The BFS ensures that this weight is the minimal distance possible
        // between the start node and end node.
            return weight;
        }
        visited[actual] = true;
        set<int>::iterator it;
        for(it = authors[actual].begin(); it != authors[actual].end(); it++) {
            int next = *it;
            q.push(make_pair(next, weight + 1));
        }
    }
    return -1;
}

void resetVisited() {
// Every instance of the BFS requires the list of visited nodes to be resetted
    for(int k = 0; k < visited.size(); k++) {
        visited[k] = false;
    }
}

void initGraph() {
// Lets initialize the graph of authors with empty positions for every author
// available, and a visited list also as false, for easy graph traversal later.
    for(int k = 0; k < authorsSet.size(); k++) {
        set<int> coauthors;
        authors.push_back(coauthors);
        visited.push_back(false);
    }
}

string readName() {
// This function reads a single name, until a second comma is found, the line
// breaks, or a ':' is found. Whitespaces are ignored.
    char c;
    string name = "";
    bool firstComma = true;
    // A name has only one comma the second one means there is at least another
    // author ahead in the list of authors of the paper
    while((c = getchar()) != EOF) {
        // Read the character
        if(c != ' ') {
        // White spaces are ignored
            if(c == ',') {
                // If a comma is found
                if(firstComma) {
                // If its the first, add it to the name and then mark that it
                // was found so the second one isn't added
                    name += ", ";
                    firstComma = false;
                }
                else {
                // If its the second, it means the name ends here
                    return name;
                }
            }
            else {
            // If its not a comma
                if(c == ':') {
                // If its a colon add it to the name, it will tell the external
                // routine that this is the last name, there won't be any more.
                    name += c;
                    return name;
                }
                else if(c == '\n'){
                // This is useful when reading just one name in a line, it will
                // end with the \n character, or line break.
                    return name;
                }
                else {
                // If it isn't a space, nor a :, nor a \n, nor a comma, add it.
                    name += c;
                }
            }
        }
    }
}

vector<string> readNames() {
// This will read a list of names in a given paper, terminating when a name co-
// mes up with a ':' at the end, marking the end of the authors and the begin-
// ning of the paper's title. Remove that ':' and store all names in a list.
    vector<string> res;
    while(true) {
        string name = readName();
        if(name[name.length() - 1] == ':') {
        // If this is the last name down the road
            name = substring(name, 0, name.length() - 1);
            res.push_back(name);
            break;
        }
        res.push_back(name);
    }
    while('\n' != getchar());
    return res;
}

int main() {
    int s;
    // The number of scenarios
    int p, n;
    // p: the number of papers
    // n: the number of authors to query for their Erdos Number
    cin >> s;
    for(int z = 0; z < s; z++) {
        authors.clear();
        visited.clear();
        authorsSet.clear();
        cin >> p >> n;
        vector< vector<string> > allPapers;
        // A list of the lists of authors contained in each paper, this is like
        // a graph in string form, which will be converted into a graph in num-
        // erical form for easy and fast traversal.
        getchar();
        for(int k = 0; k < p; k++) {
            // Each read line will be a paper, first with the authors then the
            // paper's title, which will really be irrelevant for this program.
            vector<string> paperAuthors = readNames();
            for(int l = 0; l < paperAuthors.size(); l++) {
                string name = paperAuthors[l];
                authorsSet.insert(name);
            }
            allPapers.push_back(paperAuthors);
        }
        // Now that we have the list of authors in every paper, and a set that
        // contains the unique occurrences, lets iterate over the set to assign
        // each author an iterator so its easy to make a graph with them.
        set<string>::iterator it;
        int index = 0;
        for(it = authorsSet.begin(); it != authorsSet.end(); it++) {
        // Starting from 0, lets assign every unique author its own number, and
        // also keep a record of this in two maps, one that lets retrieve the
        // author's name from the number, and other that lets retrieve the num-
        // ber by using the author's name, helpful for making a graph using on-
        // ly numbers as indices and then getting the names back again
            string name = *it;
            nameToIndex[name] = index;
            indexToName[index] = name;
            index++;
        }
        initGraph();
         for(int i = 0; i < allPapers.size(); i++) {
         // After this, the graph using only indices will be completed
            vector<string> paperAuthors = allPapers[i];
            for(int j = 0; j < paperAuthors.size() - 1; j++) {
                string name1 = paperAuthors[j];
                int index1 = nameToIndex[name1];
                for(int k = j + 1; k < paperAuthors.size(); k++) {
                    string name2 = paperAuthors[k];
                    int index2 = nameToIndex[name2];
                    authors[index1].insert(index2);
                    authors[index2].insert(index1);
                }
            }
        }
        // Now, proceed to read a list of authors to inquire for their Erdos
        // Distance, and then do a BFS to see what is their shortest distance
        // to Paul Erdos (Erdos, P.) if any (infinity in case of none found).
        cout << "Scenario " << z + 1 << endl;
        for(int k = 0; k < n; k++) {
            resetVisited();
            string completeName = readName();
            int res;
            if(authorsSet.find(completeName) != authorsSet.end()) {
            // This is VERY important. Since the idea of making a graph invol-
            // ves giving an index to all the authors, present in the papers,
            // it may be possible that afterwards, there's a query for an au-
            // thor's Erdos Number and that author isn't in any of the availa-
            // ble paper, which would result in a nasty error since that aut-
            // hor isn't available in the map, possibly returning a bogus in-
            // dex. If the author isn't among the authors we know to be pres-
            // ent in the previously read papers, immediately print that its
            // Erdos Distance is infinity.
                int start = nameToIndex[completeName];
                int end = nameToIndex["Erdos, P."];
                res = bfs(start, end);
            }
            else {
            // The queried author isn't even in any of the given papers so the
            // immediate response is that it has infinity as its Erdos Distance
                res = -1;
            }
            if(res == -1) {
                cout << completeName << " infinity" << endl;
            }
            else {
                cout << completeName << " " << res << endl;
            }
        }
    }
}<|endoftext|>


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

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

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

=========================================================================*/

#include "QmitkMovieMaker.h"
#include "QmitkMovieMakerControls.h"
#include "QmitkTreeNodeSelector.h"
#include "QmitkStepperAdapter.h"
#include "QmitkStdMultiWidget.h"

#include "mitkOpenGLRenderer.h"
#include "mitkMovieGenerator.h"
#include "mitkGlobalInteraction.h"

#include "icon.xpm"

#include <iostream>

#include <vtkRenderer.h>
#include <vtkCamera.h>

#include <qaction.h>
#include <qfiledialog.h>
#include <qtimer.h>
#include <qdatetime.h>
#include <qspinbox.h>
#include <qcombobox.h>

QmitkMovieMaker::QmitkMovieMaker( QObject *parent, const char *name,
  QmitkStdMultiWidget *mitkStdMultiWidget, mitk::DataTreeIteratorBase* it )

: QmitkFunctionality(parent, name, it),
  m_Controls(NULL), m_StepperAdapter(NULL), m_MultiWidget(mitkStdMultiWidget),
  m_Looping(true), m_Direction(0), m_Aspect(0)
{
  this->SetAvailability(true);
  m_Timer = new QTimer(this);
  m_Time = new QTime();

  m_FocusManagerCallback = MemberCommand::New();
  m_FocusManagerCallback->SetCallbackFunction( this, &QmitkMovieMaker::FocusChange );
  mitk::GlobalInteraction::GetInstance()->GetFocusManager()->AddObserver(
    mitk::FocusEvent(), m_FocusManagerCallback
  );
}

QmitkMovieMaker::~QmitkMovieMaker()
{
  delete m_StepperAdapter;
  delete m_Timer;
  delete m_Time;
}

mitk::BaseController* QmitkMovieMaker::GetSpatialController()
{
  mitk::BaseRenderer* focusedRenderer =
    mitk::GlobalInteraction::GetInstance()->GetFocus();

  if ( m_MultiWidget->GetRenderWindow1()->GetRenderer() == focusedRenderer )
  {
    return m_MultiWidget->GetRenderWindow1()->GetController();
  }
  else if ( m_MultiWidget->GetRenderWindow2()->GetRenderer() == focusedRenderer )
  {
    return m_MultiWidget->GetRenderWindow2()->GetController();
  }
  else if ( m_MultiWidget->GetRenderWindow3()->GetRenderer() == focusedRenderer )
  {
    return m_MultiWidget->GetRenderWindow3()->GetController();
  }
  else if ( m_MultiWidget->GetRenderWindow4()->GetRenderer() == focusedRenderer )
  {
    return m_MultiWidget->GetRenderWindow4()->GetController();
  }

  return m_MultiWidget->GetRenderWindow4()->GetController();
}

mitk::BaseController* QmitkMovieMaker::GetTemporalController()
{
  return m_MultiWidget->GetTimeNavigationController();
}

QWidget* QmitkMovieMaker::CreateMainWidget(QWidget *parent)
{
  return NULL;
}

QWidget* QmitkMovieMaker::CreateControlWidget(QWidget *parent)
{
  if (m_Controls == NULL)
  {
    m_Controls = new QmitkMovieMakerControls(parent);

    m_StepperAdapter = new QmitkStepperAdapter(
      (QObject*) m_Controls->slidAngle, this->GetSpatialController()->GetSlice(),
      "AngleStepperToMovieMakerFunctionality"
    );

    // Initialize "Selected Window" combo box
    const mitk::RenderWindow::RenderWindowSet rws =
      mitk::RenderWindow::GetInstances();

    mitk::RenderWindow::RenderWindowSet::const_iterator iter;
    for (iter = rws.begin(); iter != rws.end(); ++iter)
    {
      m_Controls->cmbSelectedWindow->insertItem((*iter)->GetName(), -1);
    }
  }
  return m_Controls;
}

void QmitkMovieMaker::CreateConnections()
{
  if ( m_Controls )
  {
    // start / pause / stop playing
    connect( (QObject*) m_Controls, SIGNAL(StartPlaying()),
      this, SLOT(StartPlaying()) );

    connect( (QObject*) m_Controls, SIGNAL(PausePlaying()),
      this, SLOT(PausePlaying()) );

    connect( (QObject*) m_Controls, SIGNAL(StopPlaying()),
      this, SLOT(StopPlaying()) );

    // radio button group: forward, backward, ping-pong
    connect( (QObject*) m_Controls, SIGNAL(SwitchDirection(int)),
      this, SLOT(SetDirection(int)) );

    // radio button group: spatial, temporal
    connect( (QObject*) m_Controls, SIGNAL(SwitchAspect(int)),
      this, SLOT(SetAspect(int)) );

    // window selection
    connect( (QObject*) m_Controls, SIGNAL(SwitchSelectedWindow(int)),
      this, SLOT(SetWindow(int)) );

    // camera rotation
    // every timer tick
    connect( (QObject*) m_Timer, SIGNAL(timeout()),
      this, SLOT(RotateCamera()) );

    // movie generation
    // when the movie button is clickethe movie button is clicked
    connect( (QObject*) m_Controls->btnMovie, SIGNAL(clicked()),
      this, SLOT(GenerateMovie()) );

    // blocking of ui elements during movie generation
    connect( (QObject*) this, SIGNAL(StartBlockControls()),
      (QObject*) m_Controls , SLOT(BlockControls()) );

    connect( (QObject*) this, SIGNAL(EndBlockControls()),
      (QObject*) m_Controls , SLOT(UnBlockControls()) );
  }
}

QAction* QmitkMovieMaker::CreateAction(QActionGroup *parent)
{
  QAction* action;
  action = new QAction( tr( "MovieMaker" ), QPixmap((const char**)icon_xpm),
    tr( "Camera Path" ), CTRL + Key_G, parent, "MovieMaker"
  );

  return action;
}

void QmitkMovieMaker::Activated()
{
  QmitkFunctionality::Activated();

  // Initialize steppers etc.
  this->FocusChange();
}

void QmitkMovieMaker::FocusChange()
{
  mitk::Stepper *stepper = this->GetAspectStepper();
  m_StepperAdapter->SetStepper( stepper );

  // Make the stepper movement non-inverted
  stepper->InverseDirectionOff();

  // Set stepping direction and aspect (spatial / temporal) for new stepper
  this->SwitchLooping( m_Looping );
  this->SwitchDirection( m_Direction );
  this->SwitchAspect( m_Aspect );

  // Set newly focused window as active in "Selected Window" combo box
  const mitk::RenderWindow::RenderWindowSet rws =
    mitk::RenderWindow::GetInstances();

  int i;
  mitk::RenderWindow::RenderWindowSet::const_iterator iter;
  for (iter = rws.begin(), i = 0; iter != rws.end(); ++iter, ++i)
  {
    mitk::BaseRenderer* focusedRenderer =
      mitk::GlobalInteraction::GetInstance()->GetFocusManager()->GetFocused();

    if ( focusedRenderer == (*iter)->GetRenderer() )
    {
      m_Controls->cmbSelectedWindow->setCurrentItem( i );
      break;
    }
  }
}

void QmitkMovieMaker::RotateCamera()
{
  // This method is called when a timer timeout occurs. It increases the
  // stepper value according to the elapsed time and camera rotation inteval.
  // Note that a screen refresh is not forced, but merely requested, and may
  // occur only after more calls to RotateCamera().

  mitk::Stepper* stepper = this->GetAspectStepper();

  m_StepperAdapter->SetStepper( stepper );

  int elapsedTime = m_Time->elapsed();
  m_Time->restart();

  static double increment = 0.0;
  increment = increment - static_cast< int >( increment );
  increment += elapsedTime * stepper->GetSteps()
    / ( m_Controls->spnDuration->value() * 1000.0 );

  int i, n = static_cast< int >( increment );
  for ( i = 0; i < n; ++i )
  {
    stepper->Next();
  }
}

void QmitkMovieMaker::StartPlaying()
{
  // Restart timer with 5 msec interval - this should be fine-grained enough
  // even for high display refresh frequencies
  m_Timer->start( 5 );

  m_Time->restart();
}

void QmitkMovieMaker::PausePlaying()
{
  m_Timer->stop();
}

void QmitkMovieMaker::StopPlaying()
{
  m_Timer->stop();
  switch ( m_Direction )
  {
  case 0:
  case 2:
    this->GetAspectStepper()->First();
    break;

  case 1:
    this->GetAspectStepper()->Last();
    break;
  }

  // Reposition slider GUI element
  m_StepperAdapter->SetStepper( this->GetAspectStepper() );
}

void QmitkMovieMaker::SetLooping( bool looping )
{
  m_Looping = looping;
  this->SwitchLooping( m_Looping );
}

void QmitkMovieMaker::SetDirection( int direction )
{
  m_Direction = direction;
  this->SwitchDirection( m_Direction );
}

void QmitkMovieMaker::SetAspect( int aspect )
{
  m_Aspect = aspect;
  this->SwitchAspect( m_Aspect );
}

void QmitkMovieMaker::SetWindow( int window )
{
  // Set newly selected window / renderer as focused
  const mitk::RenderWindow::RenderWindowSet rws =
    mitk::RenderWindow::GetInstances();

  int i;
  mitk::RenderWindow::RenderWindowSet::const_iterator iter;
  for (iter = rws.begin(), i = 0; iter != rws.end(); ++iter, ++i)
  {
    if ( i == window )
    {
      mitk::GlobalInteraction::GetInstance()->GetFocusManager()
        ->SetFocused( (*iter)->GetRenderer() );
      break;
    }
  }
}

void QmitkMovieMaker::SwitchLooping( bool looping )
{
 this->GetAspectStepper()->SetAutoRepeat( looping );
}

void QmitkMovieMaker::SwitchDirection( int direction )
{
  mitk::Stepper* stepper = this->GetAspectStepper();

  switch ( direction )
  {
  case 0:
    stepper->InverseDirectionOff();
    stepper->PingPongOff();
    break;

  case 1:
    stepper->InverseDirectionOn();
    stepper->PingPongOff();
    break;

  case 2:
    stepper->PingPongOn();
    break;
  }
}

void QmitkMovieMaker::SwitchAspect( int aspect )
{
  // Initialize stepper direction and looping
  this->SwitchDirection( m_Direction );
  this->SwitchLooping( m_Looping );
}

mitk::Stepper* QmitkMovieMaker::GetAspectStepper()
{
  if (m_Aspect == 0)
  {
    m_Stepper = NULL;
    return this->GetSpatialController()->GetSlice();
  }
  else if (m_Aspect == 1)
  {
    m_Stepper = NULL;
    return this->GetTemporalController()->GetTime();
  } else if (m_Aspect == 2) {
    if (m_Stepper.IsNull()) {
      int rel = m_Controls->spatialTimeRelation->value();
      int timeRepeat =1 ;
      int sliceRepeat = 1;
      if (rel < 0 ) {
        sliceRepeat = -rel;
      } else if (rel > 0) {
        timeRepeat = rel;
      }
      m_Stepper = mitk::MultiStepper::New();
      m_Stepper->AddStepper(this->GetSpatialController()->GetSlice(),sliceRepeat);
      m_Stepper->AddStepper(this->GetTemporalController()->GetTime(),timeRepeat);
    }
    return m_Stepper.GetPointer();
  }
}

void QmitkMovieMaker::GenerateMovie()
{
  emit StartBlockControls();

  // provide the movie generator with the stepper and rotate the camera each step
  mitk::MovieGenerator::Pointer movieGenerator = mitk::MovieGenerator::New();

  if ( movieGenerator.IsNotNull() )
  {
    movieGenerator->SetStepper( this->GetAspectStepper() );
    movieGenerator->SetRenderer(
      mitk::GlobalInteraction::GetInstance()->GetFocus()
    );

    QString movieFileName = QFileDialog::getSaveFileName(
      QString::null, "Movie (*.avi)", 0, "movie file dialog", "Choose a file name"
    );

    if(movieFileName.isEmpty() == false)
    {
      movieGenerator->SetFileName( movieFileName.ascii() );
      movieGenerator->WriteMovie();
    }
  }
  else
  {
    std::cerr << "Either mitk::MovieGenerator is not implemented for your";
    std::cerr << " platform or an error occurred during";
    std::cerr << " mitk::MovieGenerator::New()" << std::endl;
  }
  emit EndBlockControls();
}
<commit_msg>FIX: warning<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

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

=========================================================================*/

#include "QmitkMovieMaker.h"
#include "QmitkMovieMakerControls.h"
#include "QmitkTreeNodeSelector.h"
#include "QmitkStepperAdapter.h"
#include "QmitkStdMultiWidget.h"

#include "mitkOpenGLRenderer.h"
#include "mitkMovieGenerator.h"
#include "mitkGlobalInteraction.h"

#include "icon.xpm"

#include <iostream>

#include <vtkRenderer.h>
#include <vtkCamera.h>

#include <qaction.h>
#include <qfiledialog.h>
#include <qtimer.h>
#include <qdatetime.h>
#include <qspinbox.h>
#include <qcombobox.h>

QmitkMovieMaker::QmitkMovieMaker( QObject *parent, const char *name,
  QmitkStdMultiWidget *mitkStdMultiWidget, mitk::DataTreeIteratorBase* it )

: QmitkFunctionality(parent, name, it),
  m_Controls(NULL), m_StepperAdapter(NULL), m_MultiWidget(mitkStdMultiWidget),
  m_Looping(true), m_Direction(0), m_Aspect(0)
{
  this->SetAvailability(true);
  m_Timer = new QTimer(this);
  m_Time = new QTime();

  m_FocusManagerCallback = MemberCommand::New();
  m_FocusManagerCallback->SetCallbackFunction( this, &QmitkMovieMaker::FocusChange );
  mitk::GlobalInteraction::GetInstance()->GetFocusManager()->AddObserver(
    mitk::FocusEvent(), m_FocusManagerCallback
  );
}

QmitkMovieMaker::~QmitkMovieMaker()
{
  delete m_StepperAdapter;
  delete m_Timer;
  delete m_Time;
}

mitk::BaseController* QmitkMovieMaker::GetSpatialController()
{
  mitk::BaseRenderer* focusedRenderer =
    mitk::GlobalInteraction::GetInstance()->GetFocus();

  if ( m_MultiWidget->GetRenderWindow1()->GetRenderer() == focusedRenderer )
  {
    return m_MultiWidget->GetRenderWindow1()->GetController();
  }
  else if ( m_MultiWidget->GetRenderWindow2()->GetRenderer() == focusedRenderer )
  {
    return m_MultiWidget->GetRenderWindow2()->GetController();
  }
  else if ( m_MultiWidget->GetRenderWindow3()->GetRenderer() == focusedRenderer )
  {
    return m_MultiWidget->GetRenderWindow3()->GetController();
  }
  else if ( m_MultiWidget->GetRenderWindow4()->GetRenderer() == focusedRenderer )
  {
    return m_MultiWidget->GetRenderWindow4()->GetController();
  }

  return m_MultiWidget->GetRenderWindow4()->GetController();
}

mitk::BaseController* QmitkMovieMaker::GetTemporalController()
{
  return m_MultiWidget->GetTimeNavigationController();
}

QWidget* QmitkMovieMaker::CreateMainWidget(QWidget *parent)
{
  return NULL;
}

QWidget* QmitkMovieMaker::CreateControlWidget(QWidget *parent)
{
  if (m_Controls == NULL)
  {
    m_Controls = new QmitkMovieMakerControls(parent);

    m_StepperAdapter = new QmitkStepperAdapter(
      (QObject*) m_Controls->slidAngle, this->GetSpatialController()->GetSlice(),
      "AngleStepperToMovieMakerFunctionality"
    );

    // Initialize "Selected Window" combo box
    const mitk::RenderWindow::RenderWindowSet rws =
      mitk::RenderWindow::GetInstances();

    mitk::RenderWindow::RenderWindowSet::const_iterator iter;
    for (iter = rws.begin(); iter != rws.end(); ++iter)
    {
      m_Controls->cmbSelectedWindow->insertItem((*iter)->GetName(), -1);
    }
  }
  return m_Controls;
}

void QmitkMovieMaker::CreateConnections()
{
  if ( m_Controls )
  {
    // start / pause / stop playing
    connect( (QObject*) m_Controls, SIGNAL(StartPlaying()),
      this, SLOT(StartPlaying()) );

    connect( (QObject*) m_Controls, SIGNAL(PausePlaying()),
      this, SLOT(PausePlaying()) );

    connect( (QObject*) m_Controls, SIGNAL(StopPlaying()),
      this, SLOT(StopPlaying()) );

    // radio button group: forward, backward, ping-pong
    connect( (QObject*) m_Controls, SIGNAL(SwitchDirection(int)),
      this, SLOT(SetDirection(int)) );

    // radio button group: spatial, temporal
    connect( (QObject*) m_Controls, SIGNAL(SwitchAspect(int)),
      this, SLOT(SetAspect(int)) );

    // window selection
    connect( (QObject*) m_Controls, SIGNAL(SwitchSelectedWindow(int)),
      this, SLOT(SetWindow(int)) );

    // camera rotation
    // every timer tick
    connect( (QObject*) m_Timer, SIGNAL(timeout()),
      this, SLOT(RotateCamera()) );

    // movie generation
    // when the movie button is clickethe movie button is clicked
    connect( (QObject*) m_Controls->btnMovie, SIGNAL(clicked()),
      this, SLOT(GenerateMovie()) );

    // blocking of ui elements during movie generation
    connect( (QObject*) this, SIGNAL(StartBlockControls()),
      (QObject*) m_Controls , SLOT(BlockControls()) );

    connect( (QObject*) this, SIGNAL(EndBlockControls()),
      (QObject*) m_Controls , SLOT(UnBlockControls()) );
  }
}

QAction* QmitkMovieMaker::CreateAction(QActionGroup *parent)
{
  QAction* action;
  action = new QAction( tr( "MovieMaker" ), QPixmap((const char**)icon_xpm),
    tr( "Camera Path" ), CTRL + Key_G, parent, "MovieMaker"
  );

  return action;
}

void QmitkMovieMaker::Activated()
{
  QmitkFunctionality::Activated();

  // Initialize steppers etc.
  this->FocusChange();
}

void QmitkMovieMaker::FocusChange()
{
  mitk::Stepper *stepper = this->GetAspectStepper();
  m_StepperAdapter->SetStepper( stepper );

  // Make the stepper movement non-inverted
  stepper->InverseDirectionOff();

  // Set stepping direction and aspect (spatial / temporal) for new stepper
  this->SwitchLooping( m_Looping );
  this->SwitchDirection( m_Direction );
  this->SwitchAspect( m_Aspect );

  // Set newly focused window as active in "Selected Window" combo box
  const mitk::RenderWindow::RenderWindowSet rws =
    mitk::RenderWindow::GetInstances();

  int i;
  mitk::RenderWindow::RenderWindowSet::const_iterator iter;
  for (iter = rws.begin(), i = 0; iter != rws.end(); ++iter, ++i)
  {
    mitk::BaseRenderer* focusedRenderer =
      mitk::GlobalInteraction::GetInstance()->GetFocusManager()->GetFocused();

    if ( focusedRenderer == (*iter)->GetRenderer() )
    {
      m_Controls->cmbSelectedWindow->setCurrentItem( i );
      break;
    }
  }
}

void QmitkMovieMaker::RotateCamera()
{
  // This method is called when a timer timeout occurs. It increases the
  // stepper value according to the elapsed time and camera rotation inteval.
  // Note that a screen refresh is not forced, but merely requested, and may
  // occur only after more calls to RotateCamera().

  mitk::Stepper* stepper = this->GetAspectStepper();

  m_StepperAdapter->SetStepper( stepper );

  int elapsedTime = m_Time->elapsed();
  m_Time->restart();

  static double increment = 0.0;
  increment = increment - static_cast< int >( increment );
  increment += elapsedTime * stepper->GetSteps()
    / ( m_Controls->spnDuration->value() * 1000.0 );

  int i, n = static_cast< int >( increment );
  for ( i = 0; i < n; ++i )
  {
    stepper->Next();
  }
}

void QmitkMovieMaker::StartPlaying()
{
  // Restart timer with 5 msec interval - this should be fine-grained enough
  // even for high display refresh frequencies
  m_Timer->start( 5 );

  m_Time->restart();
}

void QmitkMovieMaker::PausePlaying()
{
  m_Timer->stop();
}

void QmitkMovieMaker::StopPlaying()
{
  m_Timer->stop();
  switch ( m_Direction )
  {
  case 0:
  case 2:
    this->GetAspectStepper()->First();
    break;

  case 1:
    this->GetAspectStepper()->Last();
    break;
  }

  // Reposition slider GUI element
  m_StepperAdapter->SetStepper( this->GetAspectStepper() );
}

void QmitkMovieMaker::SetLooping( bool looping )
{
  m_Looping = looping;
  this->SwitchLooping( m_Looping );
}

void QmitkMovieMaker::SetDirection( int direction )
{
  m_Direction = direction;
  this->SwitchDirection( m_Direction );
}

void QmitkMovieMaker::SetAspect( int aspect )
{
  m_Aspect = aspect;
  this->SwitchAspect( m_Aspect );
}

void QmitkMovieMaker::SetWindow( int window )
{
  // Set newly selected window / renderer as focused
  const mitk::RenderWindow::RenderWindowSet rws =
    mitk::RenderWindow::GetInstances();

  int i;
  mitk::RenderWindow::RenderWindowSet::const_iterator iter;
  for (iter = rws.begin(), i = 0; iter != rws.end(); ++iter, ++i)
  {
    if ( i == window )
    {
      mitk::GlobalInteraction::GetInstance()->GetFocusManager()
        ->SetFocused( (*iter)->GetRenderer() );
      break;
    }
  }
}

void QmitkMovieMaker::SwitchLooping( bool looping )
{
 this->GetAspectStepper()->SetAutoRepeat( looping );
}

void QmitkMovieMaker::SwitchDirection( int direction )
{
  mitk::Stepper* stepper = this->GetAspectStepper();

  switch ( direction )
  {
  case 0:
    stepper->InverseDirectionOff();
    stepper->PingPongOff();
    break;

  case 1:
    stepper->InverseDirectionOn();
    stepper->PingPongOff();
    break;

  case 2:
    stepper->PingPongOn();
    break;
  }
}

void QmitkMovieMaker::SwitchAspect( int aspect )
{
  // Initialize stepper direction and looping
  this->SwitchDirection( m_Direction );
  this->SwitchLooping( m_Looping );
}

mitk::Stepper* QmitkMovieMaker::GetAspectStepper()
{
  if (m_Aspect == 0)
  {
    m_Stepper = NULL;
    return this->GetSpatialController()->GetSlice();
  }
  else if (m_Aspect == 1)
  {
    m_Stepper = NULL;
    return this->GetTemporalController()->GetTime();
  } else if (m_Aspect == 2) {
    if (m_Stepper.IsNull()) {
      int rel = m_Controls->spatialTimeRelation->value();
      int timeRepeat =1 ;
      int sliceRepeat = 1;
      if (rel < 0 ) {
        sliceRepeat = -rel;
      } else if (rel > 0) {
        timeRepeat = rel;
      }
      m_Stepper = mitk::MultiStepper::New();
      m_Stepper->AddStepper(this->GetSpatialController()->GetSlice(),sliceRepeat);
      m_Stepper->AddStepper(this->GetTemporalController()->GetTime(),timeRepeat);
    }
    return m_Stepper.GetPointer();
  } else {
    // should never get here
	return 0;
  }
}

void QmitkMovieMaker::GenerateMovie()
{
  emit StartBlockControls();

  // provide the movie generator with the stepper and rotate the camera each step
  mitk::MovieGenerator::Pointer movieGenerator = mitk::MovieGenerator::New();

  if ( movieGenerator.IsNotNull() )
  {
    movieGenerator->SetStepper( this->GetAspectStepper() );
    movieGenerator->SetRenderer(
      mitk::GlobalInteraction::GetInstance()->GetFocus()
    );

    QString movieFileName = QFileDialog::getSaveFileName(
      QString::null, "Movie (*.avi)", 0, "movie file dialog", "Choose a file name"
    );

    if(movieFileName.isEmpty() == false)
    {
      movieGenerator->SetFileName( movieFileName.ascii() );
      movieGenerator->WriteMovie();
    }
  }
  else
  {
    std::cerr << "Either mitk::MovieGenerator is not implemented for your";
    std::cerr << " platform or an error occurred during";
    std::cerr << " mitk::MovieGenerator::New()" << std::endl;
  }
  emit EndBlockControls();
}
<|endoftext|>


<commit_before>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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.

// https://github.com/pillarjs/path-to-regexp/blob/master/index.js

#ifndef PATH_TO_REGEX_HPP
#define PATH_TO_REGEX_HPP

#include <regex>
#include <string>
#include <vector>
#include <map>

namespace route {

struct Token {
	std::string name;	// can be a string or an int (index)
	std::string prefix;
	std::string delimiter;
	std::string pattern;
  bool        optional;
  bool        repeat;
  bool        partial;
  bool        asterisk;
	bool        is_string;	// If it is a string we only put/have a string in the name-attribute (path in parse-method)
	               	// So if this is true, we can ignore all attributes except name

  void set_string_token(const std::string& name_) {
    name = name_;
    prefix = "";
    delimiter = "";
    optional = false;
    repeat = false;
    partial = false;
    asterisk = false;
    pattern = "";
    is_string = true;
  }
};	// < struct Token

class PathToRegex {

public:

  /**
   *  Creates a path-regex from string input (path)
   *  Updates keys-vector (empty input parameter)
   *
   *  Calls parse-method and then tokens_to_regex-method based on the tokens returned from parse-method
   *  Puts the tokens that are keys (not string-tokens) into keys-vector
   *
   *  std::vector<Token> keys (empty)
   *    Is created outside the class and sent in as parameter
   *    One Token-object in the keys-vector per parameter found in the path
   *
   *  std::map<std::string, bool> options (optional)
   *    Can contain bool-values for the keys "sensitive", "strict" and/or "end"
   *    Default:
   *      strict = false
   *      sensitive = false
   *      end = true
   */
	static std::regex path_to_regex(const std::string& path, std::vector<Token>& keys,
		const std::map<std::string, bool>& options = std::map<std::string, bool>());

  /**
   *  Creates a path-regex from string input (path)
   *
   *  Calls parse-method and then tokens_to_regex-method based on the tokens returned from parse-method
   *
   *  std::map<std::string, bool> options (optional)
   *    Can contain bool-values for the keys "sensitive", "strict" and/or "end"
   *    Default:
   *      strict = false
   *      sensitive = false
   *      end = true
   */
  static std::regex path_to_regex(const std::string& path,
    const std::map<std::string, bool>& options = std::map<std::string, bool>());

  /**
   *  Creates vector of tokens based on the given string (this vector of tokens can be sent as
   *  input to tokens_to_regex-method and includes tokens that are strings, not only tokens
   *  that are parameters in str)
   */
  static std::vector<Token> parse(const std::string& str);

  /**
   *  Creates a regex based on the tokens and options (optional) given
   */
  static std::regex tokens_to_regex(const std::vector<Token>& tokens,
    const std::map<std::string, bool>& options = std::map<std::string, bool>());

  /**
   *  Goes through the tokens-vector and push all tokens that are not string-tokens
   *  onto keys-vector
   */
  static void tokens_to_keys(const std::vector<Token>& tokens, std::vector<Token>& keys);

private:
	static const std::regex PATH_REGEXP;

};  // < class PathToRegex

};	// < namespace route

#endif	// < PATH_TO_REGEX_HPP
<commit_msg>route: Refactored Token::set_string_token<commit_after>// This file is a part of the IncludeOS unikernel - www.includeos.org
//
// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences
// and Alfred Bratterud
//
// 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.

// https://github.com/pillarjs/path-to-regexp/blob/master/index.js

#ifndef PATH_TO_REGEX_HPP
#define PATH_TO_REGEX_HPP

#include <regex>
#include <string>
#include <vector>
#include <map>

namespace route {

struct Token {
	std::string name      {};	// can be a string or an int (index)
	std::string prefix    {};
	std::string delimiter {};
	std::string pattern   {};
  bool        optional  {false};
  bool        repeat    {false};
  bool        partial   {false};
  bool        asterisk  {false};
	bool        is_string {false};	// If it is a string we only put/have a string in the name-attribute (path in parse-method)
	               	// So if this is true, we can ignore all attributes except name

  void set_string_token(const std::string& name_) {
    name = name_;
    is_string = true;
  }
}; //< struct Token

class PathToRegex {

public:

  /**
   *  Creates a path-regex from string input (path)
   *  Updates keys-vector (empty input parameter)
   *
   *  Calls parse-method and then tokens_to_regex-method based on the tokens returned from parse-method
   *  Puts the tokens that are keys (not string-tokens) into keys-vector
   *
   *  std::vector<Token> keys (empty)
   *    Is created outside the class and sent in as parameter
   *    One Token-object in the keys-vector per parameter found in the path
   *
   *  std::map<std::string, bool> options (optional)
   *    Can contain bool-values for the keys "sensitive", "strict" and/or "end"
   *    Default:
   *      strict = false
   *      sensitive = false
   *      end = true
   */
	static std::regex path_to_regex(const std::string& path, std::vector<Token>& keys,
		const std::map<std::string, bool>& options = std::map<std::string, bool>());

  /**
   *  Creates a path-regex from string input (path)
   *
   *  Calls parse-method and then tokens_to_regex-method based on the tokens returned from parse-method
   *
   *  std::map<std::string, bool> options (optional)
   *    Can contain bool-values for the keys "sensitive", "strict" and/or "end"
   *    Default:
   *      strict = false
   *      sensitive = false
   *      end = true
   */
  static std::regex path_to_regex(const std::string& path,
    const std::map<std::string, bool>& options = std::map<std::string, bool>());

  /**
   *  Creates vector of tokens based on the given string (this vector of tokens can be sent as
   *  input to tokens_to_regex-method and includes tokens that are strings, not only tokens
   *  that are parameters in str)
   */
  static std::vector<Token> parse(const std::string& str);

  /**
   *  Creates a regex based on the tokens and options (optional) given
   */
  static std::regex tokens_to_regex(const std::vector<Token>& tokens,
    const std::map<std::string, bool>& options = std::map<std::string, bool>());

  /**
   *  Goes through the tokens-vector and push all tokens that are not string-tokens
   *  onto keys-vector
   */
  static void tokens_to_keys(const std::vector<Token>& tokens, std::vector<Token>& keys);

private:
	static const std::regex PATH_REGEXP;

};  // < class PathToRegex

};	// < namespace route

#endif	// < PATH_TO_REGEX_HPP
<|endoftext|>


<commit_before>// Copyright (c) 2019, Sergey Abbakumov
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "rst/files/file_utils.h"

#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <optional>
#include <utility>

#include "rst/macros/os.h"
#include "rst/status/status_macros.h"
#include "rst/strings/str_cat.h"

#if RST_BUILDFLAG(OS_WIN)
#include <Windows.h>
#else
#include <sys/stat.h>
#endif

namespace rst {
namespace {

#if RST_BUILDFLAG(OS_WIN)
bool Replace(const NotNull<const char*> old_filename,
             const NotNull<const char*> new_filename) {
  return ::MoveFileEx(old_filename.get(), new_filename.get(),
                      MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) != 0;
}

std::optional<int64_t> GetFileSize(const NotNull<const char*> filename) {
  ::WIN32_FILE_ATTRIBUTE_DATA attr;
  if (::GetFileAttributesEx(filename.get(), ::GetFileExInfoStandard, &attr) ==
      0) {
    return std::nullopt;
  }

  return static_cast<int64_t>(attr.nFileSizeHigh) << 32 |
         static_cast<int64_t>(attr.nFileSizeLow);
}

#else

bool Replace(const NotNull<const char*> old_filename,
             const NotNull<const char*> new_filename) {
  return std::rename(old_filename.get(), new_filename.get()) == 0;
}

std::optional<int64_t> GetFileSize(const NotNull<const char*> filename) {
  struct ::stat buf;
  if (::stat(filename.get(), &buf) != 0)
    return std::nullopt;

  return buf.st_size;
}
#endif

}  // namespace

char FileError::id_ = '\0';

FileError::FileError(std::string&& message) : message_(std::move(message)) {}

FileError::~FileError() = default;

const std::string& FileError::AsString() const { return message_; }

char FileOpenError::id_ = '\0';

FileOpenError::FileOpenError(std::string&& message)
    : ErrorInfo(std::move(message)) {}

FileOpenError::~FileOpenError() = default;

Status WriteFile(const NotNull<const char*> filename,
                 const std::string_view data) {
  std::unique_ptr<std::FILE, void (*)(std::FILE*)> file(
      std::fopen(filename.get(), "wb"), [](std::FILE* f) {
        if (f != nullptr)
          (void)std::fclose(f);
      });

  if (file == nullptr)
    return MakeStatus<FileOpenError>(StrCat({"Can't open file ", filename}));

  if (std::fwrite(data.data(), 1, data.size(), file.get()) != data.size())
    return MakeStatus<FileError>(StrCat({"Can't write file ", filename}));

  const auto raw_file = file.release();
  if (std::fclose(raw_file) != 0)
    return MakeStatus<FileError>(StrCat({"Can't close file ", filename}));

  return Status::OK();
}

Status WriteImportantFile(const NotNull<const char*> filename,
                          const std::string_view data) {
  const auto temp_filename = StrCat({filename, "_tmp_"});
  RST_TRY(WriteFile(temp_filename.c_str(), data));

  if (!Replace(temp_filename.c_str(), filename.get())) {
    (void)std::remove(temp_filename.c_str());
    return MakeStatus<FileError>(
        StrCat({"Can't rename temp file ", temp_filename}));
  }

  return Status::OK();
}

StatusOr<std::string> ReadFile(const NotNull<const char*> filename) {
  std::unique_ptr<std::FILE, void (*)(std::FILE*)> file(
      std::fopen(filename.get(), "rb"), [](std::FILE* f) {
        if (f != nullptr)
          (void)std::fclose(f);
      });

  if (file == nullptr)
    return MakeStatus<FileOpenError>(StrCat({"Can't open file ", filename}));

  static constexpr int64_t kDefaultChunkSize = 128 * 1024 - 1;
  auto chunk_size = GetFileSize(filename).value_or(kDefaultChunkSize);
  RST_DCHECK(chunk_size >= 0);
  if (chunk_size == 0)  // Some files return 0 bytes (/etc/*).
    chunk_size = kDefaultChunkSize;
  chunk_size++;

  std::rewind(file.get());

  std::string content;
  size_t bytes_read_so_far = 0;
  for (size_t bytes_read_this_pass = 0;
       std::feof(file.get()) == 0 && std::ferror(file.get()) == 0;
       bytes_read_so_far += bytes_read_this_pass) {
    RST_DCHECK(content.size() == bytes_read_so_far);
    content.resize(bytes_read_so_far + static_cast<size_t>(chunk_size));
    bytes_read_this_pass =
        std::fread(content.data() + bytes_read_so_far, 1,
                   static_cast<size_t>(chunk_size), file.get());
  }

  if (std::ferror(file.get()) != 0)
    return MakeStatus<FileError>(StrCat({"Can't read file ", filename}));

  const auto raw_file = file.release();
  if (std::fclose(raw_file) != 0)
    return MakeStatus<FileError>(StrCat({"Can't close file ", filename}));

  content.resize(bytes_read_so_far);
  return content;
}

}  // namespace rst
<commit_msg>Use GUID for getting temp filename<commit_after>// Copyright (c) 2019, Sergey Abbakumov
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "rst/files/file_utils.h"

#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <optional>
#include <utility>

#include "rst/guid/guid.h"
#include "rst/macros/os.h"
#include "rst/status/status_macros.h"
#include "rst/strings/str_cat.h"

#if RST_BUILDFLAG(OS_WIN)
#include <Windows.h>
#else
#include <sys/stat.h>
#endif

namespace rst {
namespace {

#if RST_BUILDFLAG(OS_WIN)
bool Replace(const NotNull<const char*> old_filename,
             const NotNull<const char*> new_filename) {
  return ::MoveFileEx(old_filename.get(), new_filename.get(),
                      MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) != 0;
}

std::optional<int64_t> GetFileSize(const NotNull<const char*> filename) {
  ::WIN32_FILE_ATTRIBUTE_DATA attr;
  if (::GetFileAttributesEx(filename.get(), ::GetFileExInfoStandard, &attr) ==
      0) {
    return std::nullopt;
  }

  return static_cast<int64_t>(attr.nFileSizeHigh) << 32 |
         static_cast<int64_t>(attr.nFileSizeLow);
}

#else

bool Replace(const NotNull<const char*> old_filename,
             const NotNull<const char*> new_filename) {
  return std::rename(old_filename.get(), new_filename.get()) == 0;
}

std::optional<int64_t> GetFileSize(const NotNull<const char*> filename) {
  struct ::stat buf;
  if (::stat(filename.get(), &buf) != 0)
    return std::nullopt;

  return buf.st_size;
}
#endif

}  // namespace

char FileError::id_ = '\0';

FileError::FileError(std::string&& message) : message_(std::move(message)) {}

FileError::~FileError() = default;

const std::string& FileError::AsString() const { return message_; }

char FileOpenError::id_ = '\0';

FileOpenError::FileOpenError(std::string&& message)
    : ErrorInfo(std::move(message)) {}

FileOpenError::~FileOpenError() = default;

Status WriteFile(const NotNull<const char*> filename,
                 const std::string_view data) {
  std::unique_ptr<std::FILE, void (*)(std::FILE*)> file(
      std::fopen(filename.get(), "wb"), [](std::FILE* f) {
        if (f != nullptr)
          (void)std::fclose(f);
      });

  if (file == nullptr)
    return MakeStatus<FileOpenError>(StrCat({"Can't open file ", filename}));

  if (std::fwrite(data.data(), 1, data.size(), file.get()) != data.size())
    return MakeStatus<FileError>(StrCat({"Can't write file ", filename}));

  const auto raw_file = file.release();
  if (std::fclose(raw_file) != 0)
    return MakeStatus<FileError>(StrCat({"Can't close file ", filename}));

  return Status::OK();
}

Status WriteImportantFile(const NotNull<const char*> filename,
                          const std::string_view data) {
  const auto temp_filename = StrCat({filename, GenerateGuid(), ".tmp"});
  RST_TRY(WriteFile(temp_filename.c_str(), data));

  if (!Replace(temp_filename.c_str(), filename.get())) {
    (void)std::remove(temp_filename.c_str());
    return MakeStatus<FileError>(
        StrCat({"Can't rename temp file ", temp_filename}));
  }

  return Status::OK();
}

StatusOr<std::string> ReadFile(const NotNull<const char*> filename) {
  std::unique_ptr<std::FILE, void (*)(std::FILE*)> file(
      std::fopen(filename.get(), "rb"), [](std::FILE* f) {
        if (f != nullptr)
          (void)std::fclose(f);
      });

  if (file == nullptr)
    return MakeStatus<FileOpenError>(StrCat({"Can't open file ", filename}));

  static constexpr int64_t kDefaultChunkSize = 128 * 1024 - 1;
  auto chunk_size = GetFileSize(filename).value_or(kDefaultChunkSize);
  RST_DCHECK(chunk_size >= 0);
  if (chunk_size == 0)  // Some files return 0 bytes (/etc/*).
    chunk_size = kDefaultChunkSize;
  chunk_size++;

  std::rewind(file.get());

  std::string content;
  size_t bytes_read_so_far = 0;
  for (size_t bytes_read_this_pass = 0;
       std::feof(file.get()) == 0 && std::ferror(file.get()) == 0;
       bytes_read_so_far += bytes_read_this_pass) {
    RST_DCHECK(content.size() == bytes_read_so_far);
    content.resize(bytes_read_so_far + static_cast<size_t>(chunk_size));
    bytes_read_this_pass =
        std::fread(content.data() + bytes_read_so_far, 1,
                   static_cast<size_t>(chunk_size), file.get());
  }

  if (std::ferror(file.get()) != 0)
    return MakeStatus<FileError>(StrCat({"Can't read file ", filename}));

  const auto raw_file = file.release();
  if (std::fclose(raw_file) != 0)
    return MakeStatus<FileError>(StrCat({"Can't close file ", filename}));

  content.resize(bytes_read_so_far);
  return content;
}

}  // namespace rst
<|endoftext|>