<commit_before>/*
 * Copyright (C) 2013 midnightBITS
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "schema.hpp"
#include <crypt.hpp>
#include <model.hpp>
#include <algorithm>

namespace db
{
	namespace model
	{
		struct SDBuilder
		{
			SchemaDefinition sd;
			SDBuilder()
			{
				sd.table("user")
					._id()
					.field("login")
					.field("name")
					.field("email")
					.field("passphrase")
					.field("root_folder", "0", field_type::KEY)
					.field("is_admin", "0", field_type::BOOLEAN)
					;

				//hash is built from email and new salt
				//hash goes into cookie, seed is saved for later
				sd.table("session")
					.text_id("hash")
					.field("seed")
					.refer("user")
					.field("set_on", std::string(), field_type::TIME)
					;

				sd.table("folder")
					._id()
					.refer("user")
					.field("name")
					.field("parent", "0", field_type::KEY)
					.field("ord", "0", field_type::INTEGER)
					;

				sd.table("feed")
					._id()
					.field("title")
					.field("site")
					.field("feed")
					.field("last_update", std::string(), field_type::TIME)
					.nullable("author")
					.nullable("authorLink")
					.nullable("etag")
					.nullable("last_modified")
					;

				sd.table("entry")
					._id()
					.refer("feed")
					.field("guid")
					.field("title", "")
					.nullable("url")
					.nullable("date", std::string(), field_type::TIME)
					.nullable("author")
					.nullable("authorLink")
					//only one of those can acutally be null:
					.nullable("description")
					.nullable("contents")
					;

				sd.table("enclosure")
					.refer("entry")
					.field("url")
					.field("mime")
					.field("length", std::string(), field_type::INTEGER)
					;

				sd.table("categories")
					.refer("entry")
					.field("cat")
					;

				sd.table("subscription")
					.refer("feed")
					.refer("folder")
					.field("ord", std::string(), field_type::INTEGER)
					;

				sd.table("state")
					.refer("user")
					.refer("entry")
					.field("type", std::string(), field_type::INTEGER) // 0 - unread; 1 - starred; ?2 - important?
					;
			}
		};

		SchemaDefinition& SchemaDefinition::schema()
		{
			static SDBuilder builder;
			return builder.sd;
		}

		bool Schema::install()
		{
			printf("dbtool: installing the schema\n");
			SchemaDefinition& sd = SchemaDefinition::schema();
			std::list<std::string> program;
			std::list<std::string>::const_iterator _cur, _end;

			db::Transaction transaction(m_conn);
			if (!transaction.begin())
				return false;

			program = sd.drop();
			auto it = std::find_if(program.begin(), program.end(), [this](const std::string& sql) -> bool
			{
				bool ret = !m_conn->exec(sql.c_str());
				if (ret)
				{
					printf("%s\n", sql.c_str());
				}
				return ret;
			});
			if (it != program.end())
				return false;

			program = sd.create();
			it = std::find_if(program.begin(), program.end(), [this](const std::string& sql) -> bool
			{
				bool ret = !m_conn->exec(sql.c_str());
				if (ret)
				{
					printf("%s\n", sql.c_str());
				}
				return ret;
			});
			if (it != program.end())
				return false;

			return transaction.commit();
		}

		bool Schema::addUser(const char* login, const char* mail, const char* name)
		{
			char pass[13];
			Crypt::newSalt(pass);
			Crypt::password_t hash;
			Crypt::password(pass, hash);

			db::StatementPtr select = m_conn->prepare("SELECT count(*) FROM user WHERE email=? OR login=?");
			if (!select.get())
				return false;

			db::StatementPtr insert = m_conn->prepare("INSERT INTO user (login, name, email, passphrase) VALUES (?, ?, ?, ?)");
			if (!insert.get())
				return false;

			db::StatementPtr query = m_conn->prepare("SELECT _id FROM user WHERE login=?");
			if (!query.get())
				return false;

			db::StatementPtr insert_root = m_conn->prepare("INSERT INTO folder (user_id, name) VALUES (?, \"\")");
			if (!insert_root.get())
				return false;

			db::StatementPtr update = m_conn->prepare("UPDATE user SET root_folder=(SELECT _id FROM folder WHERE user_id=? AND parent=0) WHERE _id=?");
			if (!update.get())
				return false;

			if (!select->bind(0, mail)) return false;
			if (!select->bind(1, login)) return false;

			if (!insert->bind(0, login)) return false;
			if (!insert->bind(1, name)) return false;
			if (!insert->bind(2, mail)) return false;
			if (!insert->bind(3, hash)) return false;

			if (!query->bind(0, login)) return false;

			db::Transaction transaction(m_conn);
			if (!transaction.begin())
				return false;

			CursorPtr c = select->query();
			if (!c.get()) return false;
			if (!c->next()) return false;
			long count = c->getLong(0);
			if (count != 0)
			{
				long long id = -1;
				fprintf(stderr, "error: user already exists:\n");
				select = m_conn->prepare("SELECT * FROM user WHERE login=?");
				if (select.get())
				{
					select->bind(0, login);
					c = select->query();
					if (c.get() && c->next())
					{
						id = c->getLongLong(0);
						const char* login = c->getText(1);
						const char* name = c->getText(2);
						const char* mail = c->getText(3);
						bool is_admin = c->getInt(4) != 0;
						fprintf(stderr, "    #%llu: %s / %s <%s> %s\n",
							id, login, name, mail, is_admin ? "(admin)" : ""
							);
					}
				}
				select = m_conn->prepare("SELECT * FROM user WHERE email=?");
				if (select.get())
				{
					select->bind(0, mail);
					c = select->query();
					if (c.get() && c->next() && (id == -1 || id != c->getLongLong(0)))
					{
						id = c->getLongLong(0);
						const char* login = c->getText(1);
						const char* name = c->getText(2);
						const char* mail = c->getText(3);
						bool is_admin = c->getInt(4) != 0;
						fprintf(stderr, "    #%llu: %s / %s <%s> %s\n",
							id, login, name, mail, is_admin ? "(admin)" : ""
							);
					}
				}

				return false;
			}
			if (!insert->execute()) return false;

			long long _id = -1;
			c = query->query();
			if (c && c->next())
				_id = c->getLongLong(0);
			if (_id == -1)
				return false;

			if (!insert_root->bind(0, _id)) return false;
			if (!update->bind(0, _id)) return false;
			if (!update->bind(1, _id)) return false;

			if (!insert_root->execute()) return false;
			if (!update->execute()) return false;

			printf("pass: %s\n", pass);
			return transaction.commit();
		}

		bool Schema::removeUser(const char* mail)
		{
			db::StatementPtr select = m_conn->prepare("SELECT count(*) FROM user WHERE email=?");
			if (!select.get())
				return false;

			db::StatementPtr del = m_conn->prepare("DELETE FROM user WHERE email=?");
			if (!del.get())
				return false;

			if (!select->bind(0, mail)) return false;
			if (!del->bind(0, mail)) return false;

			db::Transaction transaction(m_conn);
			if (!transaction.begin())
				return false;

			CursorPtr c = select->query();
			if (!c.get()) return false;
			if (!c->next()) return false;
			long count = c->getLong(0);
			if (count == 0)
			{
				fprintf(stderr, "error: no such user\n", mail);
				return false;
			}
			if (!del->execute()) return false;

			return transaction.commit();
		}

		bool Schema::changePasswd(const char* mail, const char* passwd)
		{
			Crypt::password_t hash;
			Crypt::password(passwd, hash);

			db::StatementPtr update = m_conn->prepare("UPDATE user SET passphrase=? WHERE email=?");
			if (!update.get())
				return false;

			if (!update->bind(0, hash)) return false;
			if (!update->bind(1, mail)) return false;

			return update->execute();
		}

		std::string Field::repr() const
		{
			std::string sql = m_name + " ";
			switch (m_fld_type)
			{
			case field_type::INTEGER: sql += "INTEGER"; break;
			case field_type::KEY: sql += "BIGINT"; break;
			case field_type::TEXT: sql += "TEXT CHARACTER SET utf8"; break;
			case field_type::TEXT_KEY: sql += "VARCHAR(100) CHARACTER SET utf8"; break;
			case field_type::BOOLEAN: sql += "BOOLEAN"; break;
			case field_type::TIME: sql += "TIMESTAMP"; break;
			}

			if (m_attributes & att::NOTNULL)
				sql += " NOT NULL";
			if (m_attributes & att::AUTOINCREMENT)
				sql += " AUTO_INCREMENT";
			if (m_attributes & att::UNIQUE)
				sql += " UNIQUE";
			if (m_attributes & att::KEY)
				sql += " PRIMARY KEY";

			if (!m_ref.empty())
				if (m_attributes & att::REFERENCES)
				{
#if 0
					sql += " REFERENCES " + m_ref + " (_id)";
					if (m_attributes & att::DELETE_RESTRICT)
						sql += " ON DELETE RESTRICT";
					if (m_attributes & att::UPDATE_RESTRICT)
						sql += " ON UPDATE RESTRICT";
					if (m_attributes & att::DELETE_CASCADE)
						sql += " ON DELETE CASCADE";
					if (m_attributes & att::UPDATE_CASCADE)
						sql += " ON UPDATE CASCADE";
					if (m_attributes & att::DELETE_NULLIFY)
						sql += " ON DELETE SET NULL";
					if (m_attributes & att::UPDATE_NULLIFY)
						sql += " ON UPDATE SET NULL";
#endif
				}
				else if (m_attributes & att::DEFAULT)
				{
					sql += " DEFAULT ";
					if (m_fld_type == field_type::TEXT)
						sql += "\"" + m_ref + "\"";
					else
						sql += m_ref;
				}

				return sql;
		}

		void Field::constraints(std::list<std::string>& cos) const
		{
			if (!m_ref.empty() && m_attributes & att::REFERENCES)
			{
				std::string sql = "FOREIGN KEY (" + m_name + ") REFERENCES " + m_ref + " (_id)";
				if (m_attributes & att::DELETE_RESTRICT)
					sql += " ON DELETE RESTRICT";
				if (m_attributes & att::UPDATE_RESTRICT)
					sql += " ON UPDATE RESTRICT";
				if (m_attributes & att::DELETE_CASCADE)
					sql += " ON DELETE CASCADE";
				if (m_attributes & att::UPDATE_CASCADE)
					sql += " ON UPDATE CASCADE";
				if (m_attributes & att::DELETE_NULLIFY)
					sql += " ON DELETE SET NULL";
				if (m_attributes & att::UPDATE_NULLIFY)
					sql += " ON UPDATE SET NULL";
				//cos.push_back("INDEX (" + m_name + ")");
				cos.push_back(sql);
			}
		}

		std::string Table::drop() const
		{
			return "DROP TABLE IF EXISTS " + m_name;
		}

		std::string Table::create() const
		{
			std::string sql = "CREATE TABLE " + m_name + " (\n  ";
			bool first = true;
			for (auto _cur = m_fields.begin(); _cur != m_fields.end(); ++_cur)
			{
				if (first) first = false;
				else sql += ",\n  ";
				sql += _cur->repr();
			}
			std::list<std::string> cos;
			for (auto _cur = m_fields.begin(); _cur != m_fields.end(); ++_cur)
				_cur->constraints(cos);
			for (auto _cur = cos.begin(); _cur != cos.end(); ++_cur)
			{
				if (first) first = false;
				else sql += ",\n  ";
				sql += *_cur;
			}
			return sql += "\n)";
		}

		std::string View::drop() const
		{
			return "DROP VIEW IF EXISTS " + m_name;
		}

		std::string View::create() const
		{
			return "CREATE VIEW " + m_name + " AS (" + m_select + ")";
		}

	}
}<commit_msg>Subscription views<commit_after>/*
 * Copyright (C) 2013 midnightBITS
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "schema.hpp"
#include <crypt.hpp>
#include <model.hpp>
#include <algorithm>

namespace db
{
	namespace model
	{
		struct SDBuilder
		{
			SchemaDefinition sd;
			SDBuilder()
			{
				sd.table("user")
					._id()
					.field("login")
					.field("name")
					.field("email")
					.field("passphrase")
					.field("root_folder", "0", field_type::KEY)
					.field("is_admin", "0", field_type::BOOLEAN)
					;

				//hash is built from email and new salt
				//hash goes into cookie, seed is saved for later
				sd.table("session")
					.text_id("hash")
					.field("seed")
					.refer("user")
					.field("set_on", std::string(), field_type::TIME)
					;

				sd.table("folder")
					._id()
					.refer("user")
					.field("name")
					.field("parent", "0", field_type::KEY)
					.field("ord", "0", field_type::INTEGER)
					;

				sd.table("feed")
					._id()
					.field("title")
					.field("site")
					.field("feed")
					.field("last_update", std::string(), field_type::TIME)
					.nullable("author")
					.nullable("authorLink")
					.nullable("etag")
					.nullable("last_modified")
					;

				sd.table("entry")
					._id()
					.refer("feed")
					.field("guid")
					.field("title", "")
					.nullable("url")
					.nullable("date", std::string(), field_type::TIME)
					.nullable("author")
					.nullable("authorLink")
					//only one of those can acutally be null:
					.nullable("description")
					.nullable("contents")
					;

				sd.table("enclosure")
					.refer("entry")
					.field("url")
					.field("mime")
					.field("length", std::string(), field_type::INTEGER)
					;

				sd.table("categories")
					.refer("entry")
					.field("cat")
					;

				sd.table("subscription")
					.refer("feed")
					.refer("folder")
					.field("ord", std::string(), field_type::INTEGER)
					;

				sd.table("state")
					.refer("user")
					.refer("entry")
					.field("type", std::string(), field_type::INTEGER) // 0 - unread; 1 - starred; ?2 - important?
					;

				sd.view("parents",
					"SELECT folder.user_id AS user_id, folder._id AS folder_id, folder.name AS name, parent.name AS parent, folder.ord AS ord "
					"FROM folder "
					"LEFT JOIN folder AS parent ON (parent._id = folder.parent) "
					"ORDER BY parent.name, folder.ord");

				sd.view("state_stats",
					"SELECT state.user_id AS user_id, entry.feed_id AS feed_id, state.type AS type, count(*) AS count "
					"FROM state "
					"LEFT JOIN entry ON (entry._id = state.entry_id) "
					"GROUP BY state.type, entry.feed_id, state.user_id");

				sd.view("ordered_stats",
					"SELECT state_stats.user_id AS user_id, subscription.folder_id AS folder_id, subscription.ord AS ord, feed._id AS feed_id, feed.title AS feed, type, count "
					"FROM subscription "
					"LEFT JOIN folder ON (subscription.folder_id = folder._id) "
					"LEFT JOIN feed ON (subscription.feed_id = feed._id) "
					"LEFT JOIN state_stats ON (state_stats.feed_id = feed._id AND state_stats.user_id = folder.user_id) "
					"ORDER BY folder_id, type, ord");
			}
		};

		SchemaDefinition& SchemaDefinition::schema()
		{
			static SDBuilder builder;
			return builder.sd;
		}

		bool Schema::install()
		{
			printf("dbtool: installing the schema\n");
			SchemaDefinition& sd = SchemaDefinition::schema();
			std::list<std::string> program;
			std::list<std::string>::const_iterator _cur, _end;

			db::Transaction transaction(m_conn);
			if (!transaction.begin())
				return false;

			program = sd.drop();
			auto it = std::find_if(program.begin(), program.end(), [this](const std::string& sql) -> bool
			{
				bool ret = !m_conn->exec(sql.c_str());
				if (ret)
				{
					printf("%s\n", sql.c_str());
				}
				return ret;
			});
			if (it != program.end())
				return false;

			program = sd.create();
			it = std::find_if(program.begin(), program.end(), [this](const std::string& sql) -> bool
			{
				bool ret = !m_conn->exec(sql.c_str());
				if (ret)
				{
					printf("%s\n", sql.c_str());
				}
				return ret;
			});
			if (it != program.end())
				return false;

			return transaction.commit();
		}

		bool Schema::addUser(const char* login, const char* mail, const char* name)
		{
			char pass[13];
			Crypt::newSalt(pass);
			Crypt::password_t hash;
			Crypt::password(pass, hash);

			db::StatementPtr select = m_conn->prepare("SELECT count(*) FROM user WHERE email=? OR login=?");
			if (!select.get())
				return false;

			db::StatementPtr insert = m_conn->prepare("INSERT INTO user (login, name, email, passphrase) VALUES (?, ?, ?, ?)");
			if (!insert.get())
				return false;

			db::StatementPtr query = m_conn->prepare("SELECT _id FROM user WHERE login=?");
			if (!query.get())
				return false;

			db::StatementPtr insert_root = m_conn->prepare("INSERT INTO folder (user_id, name) VALUES (?, \"\")");
			if (!insert_root.get())
				return false;

			db::StatementPtr update = m_conn->prepare("UPDATE user SET root_folder=(SELECT _id FROM folder WHERE user_id=? AND parent=0) WHERE _id=?");
			if (!update.get())
				return false;

			if (!select->bind(0, mail)) return false;
			if (!select->bind(1, login)) return false;

			if (!insert->bind(0, login)) return false;
			if (!insert->bind(1, name)) return false;
			if (!insert->bind(2, mail)) return false;
			if (!insert->bind(3, hash)) return false;

			if (!query->bind(0, login)) return false;

			db::Transaction transaction(m_conn);
			if (!transaction.begin())
				return false;

			CursorPtr c = select->query();
			if (!c.get()) return false;
			if (!c->next()) return false;
			long count = c->getLong(0);
			if (count != 0)
			{
				long long id = -1;
				fprintf(stderr, "error: user already exists:\n");
				select = m_conn->prepare("SELECT * FROM user WHERE login=?");
				if (select.get())
				{
					select->bind(0, login);
					c = select->query();
					if (c.get() && c->next())
					{
						id = c->getLongLong(0);
						const char* login = c->getText(1);
						const char* name = c->getText(2);
						const char* mail = c->getText(3);
						bool is_admin = c->getInt(4) != 0;
						fprintf(stderr, "    #%llu: %s / %s <%s> %s\n",
							id, login, name, mail, is_admin ? "(admin)" : ""
							);
					}
				}
				select = m_conn->prepare("SELECT * FROM user WHERE email=?");
				if (select.get())
				{
					select->bind(0, mail);
					c = select->query();
					if (c.get() && c->next() && (id == -1 || id != c->getLongLong(0)))
					{
						id = c->getLongLong(0);
						const char* login = c->getText(1);
						const char* name = c->getText(2);
						const char* mail = c->getText(3);
						bool is_admin = c->getInt(4) != 0;
						fprintf(stderr, "    #%llu: %s / %s <%s> %s\n",
							id, login, name, mail, is_admin ? "(admin)" : ""
							);
					}
				}

				return false;
			}
			if (!insert->execute()) return false;

			long long _id = -1;
			c = query->query();
			if (c && c->next())
				_id = c->getLongLong(0);
			if (_id == -1)
				return false;

			if (!insert_root->bind(0, _id)) return false;
			if (!update->bind(0, _id)) return false;
			if (!update->bind(1, _id)) return false;

			if (!insert_root->execute()) return false;
			if (!update->execute()) return false;

			printf("pass: %s\n", pass);
			return transaction.commit();
		}

		bool Schema::removeUser(const char* mail)
		{
			db::StatementPtr select = m_conn->prepare("SELECT count(*) FROM user WHERE email=?");
			if (!select.get())
				return false;

			db::StatementPtr del = m_conn->prepare("DELETE FROM user WHERE email=?");
			if (!del.get())
				return false;

			if (!select->bind(0, mail)) return false;
			if (!del->bind(0, mail)) return false;

			db::Transaction transaction(m_conn);
			if (!transaction.begin())
				return false;

			CursorPtr c = select->query();
			if (!c.get()) return false;
			if (!c->next()) return false;
			long count = c->getLong(0);
			if (count == 0)
			{
				fprintf(stderr, "error: no such user\n", mail);
				return false;
			}
			if (!del->execute()) return false;

			return transaction.commit();
		}

		bool Schema::changePasswd(const char* mail, const char* passwd)
		{
			Crypt::password_t hash;
			Crypt::password(passwd, hash);

			db::StatementPtr update = m_conn->prepare("UPDATE user SET passphrase=? WHERE email=?");
			if (!update.get())
				return false;

			if (!update->bind(0, hash)) return false;
			if (!update->bind(1, mail)) return false;

			return update->execute();
		}

		std::string Field::repr() const
		{
			std::string sql = m_name + " ";
			switch (m_fld_type)
			{
			case field_type::INTEGER: sql += "INTEGER"; break;
			case field_type::KEY: sql += "BIGINT"; break;
			case field_type::TEXT: sql += "TEXT CHARACTER SET utf8"; break;
			case field_type::TEXT_KEY: sql += "VARCHAR(100) CHARACTER SET utf8"; break;
			case field_type::BOOLEAN: sql += "BOOLEAN"; break;
			case field_type::TIME: sql += "TIMESTAMP"; break;
			}

			if (m_attributes & att::NOTNULL)
				sql += " NOT NULL";
			if (m_attributes & att::AUTOINCREMENT)
				sql += " AUTO_INCREMENT";
			if (m_attributes & att::UNIQUE)
				sql += " UNIQUE";
			if (m_attributes & att::KEY)
				sql += " PRIMARY KEY";

			if (!m_ref.empty())
				if (m_attributes & att::REFERENCES)
				{
#if 0
					sql += " REFERENCES " + m_ref + " (_id)";
					if (m_attributes & att::DELETE_RESTRICT)
						sql += " ON DELETE RESTRICT";
					if (m_attributes & att::UPDATE_RESTRICT)
						sql += " ON UPDATE RESTRICT";
					if (m_attributes & att::DELETE_CASCADE)
						sql += " ON DELETE CASCADE";
					if (m_attributes & att::UPDATE_CASCADE)
						sql += " ON UPDATE CASCADE";
					if (m_attributes & att::DELETE_NULLIFY)
						sql += " ON DELETE SET NULL";
					if (m_attributes & att::UPDATE_NULLIFY)
						sql += " ON UPDATE SET NULL";
#endif
				}
				else if (m_attributes & att::DEFAULT)
				{
					sql += " DEFAULT ";
					if (m_fld_type == field_type::TEXT)
						sql += "\"" + m_ref + "\"";
					else
						sql += m_ref;
				}

				return sql;
		}

		void Field::constraints(std::list<std::string>& cos) const
		{
			if (!m_ref.empty() && m_attributes & att::REFERENCES)
			{
				std::string sql = "FOREIGN KEY (" + m_name + ") REFERENCES " + m_ref + " (_id)";
				if (m_attributes & att::DELETE_RESTRICT)
					sql += " ON DELETE RESTRICT";
				if (m_attributes & att::UPDATE_RESTRICT)
					sql += " ON UPDATE RESTRICT";
				if (m_attributes & att::DELETE_CASCADE)
					sql += " ON DELETE CASCADE";
				if (m_attributes & att::UPDATE_CASCADE)
					sql += " ON UPDATE CASCADE";
				if (m_attributes & att::DELETE_NULLIFY)
					sql += " ON DELETE SET NULL";
				if (m_attributes & att::UPDATE_NULLIFY)
					sql += " ON UPDATE SET NULL";
				//cos.push_back("INDEX (" + m_name + ")");
				cos.push_back(sql);
			}
		}

		std::string Table::drop() const
		{
			return "DROP TABLE IF EXISTS " + m_name;
		}

		std::string Table::create() const
		{
			std::string sql = "CREATE TABLE " + m_name + " (\n  ";
			bool first = true;
			for (auto _cur = m_fields.begin(); _cur != m_fields.end(); ++_cur)
			{
				if (first) first = false;
				else sql += ",\n  ";
				sql += _cur->repr();
			}
			std::list<std::string> cos;
			for (auto _cur = m_fields.begin(); _cur != m_fields.end(); ++_cur)
				_cur->constraints(cos);
			for (auto _cur = cos.begin(); _cur != cos.end(); ++_cur)
			{
				if (first) first = false;
				else sql += ",\n  ";
				sql += *_cur;
			}
			return sql += "\n)";
		}

		std::string View::drop() const
		{
			return "DROP VIEW IF EXISTS " + m_name;
		}

		std::string View::create() const
		{
			return "CREATE VIEW " + m_name + " AS (" + m_select + ")";
		}

	}
}<|endoftext|>


<commit_before>#ifndef BUW_SHAPE_HPP
#define BUW_SHAPE_HPP
#include <iostream>
#include <string>
#include "color.hpp"
#include "ray.hpp"
#include "material.hpp"

class Shape
{
public:
	/*
	Shape(): name_{""}, color_{0.0f, 0.0f, 0.0f}{}
	Shape(std::string name, Color const& color): name_{name}, color_{color} {}
	virtual ~Shape(){std::cout << "Shape Destructor" << std::endl;}
	~Shape(){std::cout << "Shape Destructor" << std::endl;}

	Shape(): name_{""}, material_{}{}
	Shape(std::string name, Material const& mat): name_{name}, material_{mat} {}
	Shape(std::string name, std::string name_mat, Color const& ka, Color const& kd, Color const& ks, float m): name_{name},
	material_{name_mat, ka, kd, ks, m} {}*/

	Shape(): name_{""}, matname_{""}{}
	Shape(std::string name, std::string material): name_{name}, matname_{material} {}

	virtual std::ostream& print(std::ostream& os) const{
		os << "Name: " << name_ << " Material: " << matname_ << std::endl;;
		return os;
	}

	std::string const& get_name()const{
		return name_;
	}

	std::string const& get_material()const{
		return matname_;
	}
	/*Material const& get_material() const{
		return material_;
	}*/

	//Ausgangsgleichung: ax^2 + bx + c = 0
	bool solveQuadratic(const float &a, const float &b, const float &c, float &x0, float &x1){ //x0, x1 geben t0,t1 Auftreffpunkte
		float discr = b * b - 4 * a * c;
		if (discr < 0){
			return false;
		}
		else if (discr == 0) {
		x0 = - 0.5 * b / a;
		x1 = - 0.5 * b / a;
		}
		else {
			float q = (b > 0) ?
				-0.5 * (b + sqrt(discr)) :
				-0.5 * (b - sqrt(discr));
			x0 = q / a;
			x1 = c / q;
		}
		if (x0 > x1) {
			std::swap(x0, x1);
		}
		return true;
	}

	friend std::ostream& operator<<(std::ostream& os, Shape const& s){
		return s.print(os);
	}

	virtual bool intersect(Ray const& ray, float& distance) const = 0;
	virtual bool intersect(Ray const& ray, float& distance, glm::vec3& intersection,
	glm::vec3& normal) const = 0;

private:
	std::string name_;
	//Color color_;
	//Material material_;
	std::string matname_;
};

#endif // #ifndef BUW_SHAPE_HPP
<commit_msg>Include math.h<commit_after>#ifndef BUW_SHAPE_HPP
#define BUW_SHAPE_HPP
#include <iostream>
#include <string>
#include "color.hpp"
#include "ray.hpp"
#include "material.hpp"
#include <math.h>

class Shape
{
public:
	/*
	Shape(): name_{""}, color_{0.0f, 0.0f, 0.0f}{}
	Shape(std::string name, Color const& color): name_{name}, color_{color} {}
	virtual ~Shape(){std::cout << "Shape Destructor" << std::endl;}
	~Shape(){std::cout << "Shape Destructor" << std::endl;}

	Shape(): name_{""}, material_{}{}
	Shape(std::string name, Material const& mat): name_{name}, material_{mat} {}
	Shape(std::string name, std::string name_mat, Color const& ka, Color const& kd, Color const& ks, float m): name_{name},
	material_{name_mat, ka, kd, ks, m} {}*/

	Shape(): name_{""}, matname_{""}{}
	Shape(std::string name, std::string material): name_{name}, matname_{material} {}

	virtual std::ostream& print(std::ostream& os) const{
		os << "Name: " << name_ << " Material: " << matname_ << std::endl;;
		return os;
	}

	std::string const& get_name()const{
		return name_;
	}

	std::string const& get_material()const{
		return matname_;
	}
	/*Material const& get_material() const{
		return material_;
	}*/

	//Ausgangsgleichung: ax^2 + bx + c = 0
	bool solveQuadratic(const float &a, const float &b, const float &c, float &x0, float &x1){ //x0, x1 geben t0,t1 Auftreffpunkte
		float discr = b * b - 4 * a * c;
		if (discr < 0){
			return false;
		}
		else if (discr == 0) {
		x0 = - 0.5 * b / a;
		x1 = - 0.5 * b / a;
		}
		else {
			float q = (b > 0) ?
				-0.5 * (b + sqrt(discr)) :
				-0.5 * (b - sqrt(discr));
			x0 = q / a;
			x1 = c / q;
		}
		if (x0 > x1) {
			std::swap(x0, x1);
		}
		return true;
	}

	friend std::ostream& operator<<(std::ostream& os, Shape const& s){
		return s.print(os);
	}

	virtual bool intersect(Ray const& ray, float& distance) const = 0;
	virtual bool intersect(Ray const& ray, float& distance, glm::vec3& intersection,
	glm::vec3& normal) const = 0;

private:
	std::string name_;
	//Color color_;
	//Material material_;
	std::string matname_;
};

#endif // #ifndef BUW_SHAPE_HPP
<|endoftext|>


<commit_before>#include "sequential.h"
#include "utils.h"
#include "stdlib.h"
#include "control.h"
#include "profiler.h"
#include "groups.h"
#include "CL/cl.h"

////////////////// RESAMPLE USING POLYNOMIAL APPROXIMATION /////////////////
int exSeq_Resample(ResultsStruct* results)
{
	return 0;
}

// Calculate Q and R matrixes for QR Decomposition of matrix
void QR(cl_float* R, cl_float* Q, cl_uint arrayWidth, cl_uint arrayHeight)
{
	cl_float a;
	cl_float b;
	cl_float c;
	cl_float s;
	cl_float r;
	cl_float Rnew1[2048];
	cl_float Rnew2[2048];
	cl_float Qnew1[2048];
	cl_float Qnew2[2048];
	for (int j = 0; j < arrayWidth; j++)
	{
		for (int i = arrayHeight - 1; i > j; i--)
		{
			// Calculate Givens rotations
			a = R[arrayWidth * (i - 1) + j];
			b = R[arrayWidth * i + j];
			r = sqrt(a * a + b * b);
			c = a / r;
			s = -b / r;
			// Zero out elements in R matrix
			for (int k = j; k < arrayWidth; k++)
			{
				Rnew1[k] = R[arrayWidth * (i - 1) + k] * c - R[arrayWidth * i + k] * s;
				Rnew2[k] = R[arrayWidth * (i - 1) + k] * s + R[arrayWidth * i + k] * c;
			}
			// Copy new values back to R matrix
			for (int k = j; k < arrayWidth; k++)
			{
				R[arrayWidth * (i - 1) + k] = Rnew1[k];
				R[arrayWidth * i + k] = Rnew2[k];
			}
			// Update Q matrix
			for (int k = 0; k < arrayHeight; k++)
			{
				Qnew1[k] = Q[arrayHeight * (i - 1) + k] * c + Q[arrayHeight * i + k] * s;
				Qnew2[k] = -Q[arrayHeight * (i - 1) + k] * s + Q[arrayHeight * i + k] * c;
			}
			for (int k = 0; k < arrayHeight; k++)
			{
				Q[arrayHeight * (i - 1) + k] = Qnew1[k];
				Q[arrayHeight * i + k] = Qnew2[k];
			}
		}
	}
}

int exSeq_QRD(ResultsStruct* results)
{
	const cl_uint arrayWidth = 3;
	const cl_uint arrayHeight = 5;
	cl_uint numIter = 10000; // Number of iterations for runtime averaging

							 // allocate working buffers.
							 // the buffer should be aligned with 4K page and size should fit 64-byte cached line
	cl_uint optimizedSize = ((sizeof(cl_float) * arrayWidth * arrayHeight - 1) / 64 + 1) * 64;
	cl_float* A = (cl_float*)_aligned_malloc(optimizedSize, 4096);

	optimizedSize = ((sizeof(cl_float) * arrayHeight * arrayHeight - 1) / 64 + 1) * 64;
	cl_float* Q = (cl_float*)_aligned_malloc(optimizedSize, 4096);

	cl_float Atmp[] = { 0.8147, 0.0975, 0.1576,
		0.9058, 0.2785, 0.9706,
		0.1270, 0.5469, 0.9572,
		0.9134, 0.9575, 0.4854,
		0.6324, 0.9649, 0.8003 };

	cl_float Qtmp[] = { 1.0,   0,   0,   0,   0,
		0, 1.0,   0,   0,   0,
		0,   0, 1.0,   0,   0,
		0,   0,   0, 1.0,   0,
		0,   0,   0,   0, 1.0 };

	if (NULL == A)
	{
		LogError("Error: _aligned_malloc failed to allocate buffers.\n");
		return -1;
	}

	// Initialize A
	for (int i = 0; i < arrayWidth * arrayHeight; i++)
	{
		A[i] = Atmp[i];
	}

	// Initialize Q
	for (int i = 0; i < arrayHeight * arrayHeight; i++)
	{
		Q[i] = Qtmp[i];
	}

	// add
	ProfilerStruct profiler;
	profiler.Start();

	QR(A, Q, arrayWidth, arrayHeight);

	profiler.Stop();
	float runTime = profiler.Log();

	_aligned_free(A);

	results->WindowsRunTime = (double)runTime;
	results->HasWindowsRunTime = true;
	return 0;
}

// Solves for coefficients in equation R*x=Q*b where
// R and Q are from QR Decomposition
// x is desired coefficients
// b is column vector of sampled data
// @param[in] R upper triangular matrix from QR Decomposition
// @param[in] Qtb Q*b column vector
// @param[in] rows number of sample data points
// @param[in] cols number of coefficients to solve for
// @param[out] Result is where x is stored
void BackSub(cl_float* R, cl_float* Qtb, cl_uint rows, cl_uint cols, cl_float* Result)
{
	if (!Result)
		Result = (cl_float*)malloc(sizeof(cl_float*)*rows);

	// solve for last last value without any subtraction
	Result[cols] = Qtb[cols] / R[cols][cols];
	for (size_t i = cols - 1; i > 1; i--)
	{
		size_t from = i + 1;
		size_t to = cols;

		cl_float subtractSum = 0;
		for (int j = from; j < to; j++)
			subtractSum += R[i][j] * Result[j];

		Result[i] = (Qtb[i] - subtractSum);
	}
}

// Tests BackSub
int Test_BackSub(ResultsStruct* results)
{
	// get R matrix
	// get column vector product of Q matrix and b column vector
	// run BackSub function to solve for coefficients
	// store coefficients in ControlObject
	return -1;
}

// Sequential evaluation of polynomial Test
// Evauluates data in ControlObject
int exSeq_PolyEval(ResultsStruct* results)
{
	// get coeffs from ControlObject
	// get sample_rate_output_
	// calculate output data
	// write to output file
	return -1;
}
<commit_msg>Fixing the backSub function. Changed the row, col input parameters to one parameter dim since matrix R is always square.<commit_after>#include "sequential.h"
#include "utils.h"
#include "stdlib.h"
#include "control.h"
#include "profiler.h"
#include "groups.h"
#include "CL/cl.h"

////////////////// RESAMPLE USING POLYNOMIAL APPROXIMATION /////////////////
int exSeq_Resample(ResultsStruct* results)
{
	return 0;
}

// Calculate Q and R matrixes for QR Decomposition of matrix
void QR(cl_float* R, cl_float* Q, cl_uint arrayWidth, cl_uint arrayHeight)
{
	cl_float a;
	cl_float b;
	cl_float c;
	cl_float s;
	cl_float r;
	cl_float Rnew1[2048];
	cl_float Rnew2[2048];
	cl_float Qnew1[2048];
	cl_float Qnew2[2048];
	for (int j = 0; j < arrayWidth; j++)
	{
		for (int i = arrayHeight - 1; i > j; i--)
		{
			// Calculate Givens rotations
			a = R[arrayWidth * (i - 1) + j];
			b = R[arrayWidth * i + j];
			r = sqrt(a * a + b * b);
			c = a / r;
			s = -b / r;
			// Zero out elements in R matrix
			for (int k = j; k < arrayWidth; k++)
			{
				Rnew1[k] = R[arrayWidth * (i - 1) + k] * c - R[arrayWidth * i + k] * s;
				Rnew2[k] = R[arrayWidth * (i - 1) + k] * s + R[arrayWidth * i + k] * c;
			}
			// Copy new values back to R matrix
			for (int k = j; k < arrayWidth; k++)
			{
				R[arrayWidth * (i - 1) + k] = Rnew1[k];
				R[arrayWidth * i + k] = Rnew2[k];
			}
			// Update Q matrix
			for (int k = 0; k < arrayHeight; k++)
			{
				Qnew1[k] = Q[arrayHeight * (i - 1) + k] * c + Q[arrayHeight * i + k] * s;
				Qnew2[k] = -Q[arrayHeight * (i - 1) + k] * s + Q[arrayHeight * i + k] * c;
			}
			for (int k = 0; k < arrayHeight; k++)
			{
				Q[arrayHeight * (i - 1) + k] = Qnew1[k];
				Q[arrayHeight * i + k] = Qnew2[k];
			}
		}
	}
}

int exSeq_QRD(ResultsStruct* results)
{
	const cl_uint arrayWidth = 3;
	const cl_uint arrayHeight = 5;
	cl_uint numIter = 10000; // Number of iterations for runtime averaging

							 // allocate working buffers.
							 // the buffer should be aligned with 4K page and size should fit 64-byte cached line
	cl_uint optimizedSize = ((sizeof(cl_float) * arrayWidth * arrayHeight - 1) / 64 + 1) * 64;
	cl_float* A = (cl_float*)_aligned_malloc(optimizedSize, 4096);

	optimizedSize = ((sizeof(cl_float) * arrayHeight * arrayHeight - 1) / 64 + 1) * 64;
	cl_float* Q = (cl_float*)_aligned_malloc(optimizedSize, 4096);

	cl_float Atmp[] = { 0.8147, 0.0975, 0.1576,
		0.9058, 0.2785, 0.9706,
		0.1270, 0.5469, 0.9572,
		0.9134, 0.9575, 0.4854,
		0.6324, 0.9649, 0.8003 };

	cl_float Qtmp[] = { 1.0,   0,   0,   0,   0,
		0, 1.0,   0,   0,   0,
		0,   0, 1.0,   0,   0,
		0,   0,   0, 1.0,   0,
		0,   0,   0,   0, 1.0 };

	if (NULL == A)
	{
		LogError("Error: _aligned_malloc failed to allocate buffers.\n");
		return -1;
	}

	// Initialize A
	for (int i = 0; i < arrayWidth * arrayHeight; i++)
	{
		A[i] = Atmp[i];
	}

	// Initialize Q
	for (int i = 0; i < arrayHeight * arrayHeight; i++)
	{
		Q[i] = Qtmp[i];
	}

	// add
	ProfilerStruct profiler;
	profiler.Start();

	QR(A, Q, arrayWidth, arrayHeight);

	profiler.Stop();
	float runTime = profiler.Log();

	_aligned_free(A);

	results->WindowsRunTime = (double)runTime;
	results->HasWindowsRunTime = true;
	return 0;
}

// Solves for coefficients in equation R*x=Q*b where
// R and Q are from QR Decomposition
// x is desired coefficients
// b is column vector of sampled data
// @param[in] R upper triangular matrix from QR Decomposition
// @param[in] Qtb Q*b column vector
// @param[in] dim number of coefficients to solve for (R is square, so we only need one parameter for the matrix dimension).
// @param[out] Result is where x is stored
void BackSub(cl_float* R, cl_float* Qtb, int dim, cl_float* Result)
{
	if (!Result)
		Result = (cl_float*)malloc(sizeof(cl_float*)*dim);

	// solve for last last value without any subtraction
	Result[dim - 1] = Qtb[dim - 1] / R[dim*dim - 1];
	int count = dim - 2;
	for (int i = dim - 2; i >= 0; i--)
	{
		int from = i + 1;
		int to = dim;

		float subtractSum = 0;
		for (int j = from; j < to; j++)
			subtractSum += R[i*dim + j] * Result[j];

		Result[i] = (Qtb[i] - subtractSum) / R[i*dim + count];
		count -= 1;
	}
}

// Tests BackSub
int Test_BackSub(ResultsStruct* results)
{
	// get R matrix
	// get column vector product of Q matrix and b column vector
	// run BackSub function to solve for coefficients
	// store coefficients in ControlObject
	return -1;
}

// Sequential evaluation of polynomial Test
// Evauluates data in ControlObject
int exSeq_PolyEval(ResultsStruct* results)
{
	// get coeffs from ControlObject
	// get sample_rate_output_
	// calculate output data
	// write to output file
	return -1;
}
<|endoftext|>


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

#include "chrome/browser/defaults.h"

namespace browser_defaults {

#if defined(OS_CHROMEOS)

const double kAutocompleteEditFontPixelSize = 12.0;
const double kAutocompleteEditFontPixelSizeInPopup = 10;

// This is only used by AutocompletePopupViewGtk which is unused
// unless TOOLKIT_VIEWS is undefined:
const int kAutocompletePopupFontSize = 7;

const SessionStartupPref::Type kDefaultSessionStartupType =
    SessionStartupPref::LAST;
const int kMiniTabWidth = 64;
const bool kCanToggleSystemTitleBar = false;
const bool kRestorePopups = true;
const bool kShowImportOnBookmarkBar = false;
const bool kShowExitMenuItem = true;
const bool kShowAboutMenuItem = true;
const bool kOSSupportsOtherBrowsers = false;
const bool kDownloadPageHasShowInFolder = true;
const bool kSizeTabButtonToTopOfTabStrip = true;
const bool kBootstrapSyncAuthentication = true;
const bool kShowOtherBrowsersInAboutMemory = false;
const bool kAlwaysOpenIncognitoWindow = true;

#elif defined(TOOLKIT_USES_GTK)

// 13.4px = 10pt @ 96dpi.
const double kAutocompleteEditFontPixelSize = 13.4;

// On Windows, popup windows' autocomplete box have a font 5/6 the size of a
// regular window, which we duplicate here for GTK.
const double kAutocompleteEditFontPixelSizeInPopup =
    kAutocompleteEditFontPixelSize * 5.0 / 6.0;

const int kAutocompletePopupFontSize = 10;

#if defined(TOOLKIT_VIEWS)
const bool kCanToggleSystemTitleBar = false;
#else
const bool kCanToggleSystemTitleBar = true;
#endif

#endif

#if !defined(OS_CHROMEOS)

const SessionStartupPref::Type kDefaultSessionStartupType =
    SessionStartupPref::DEFAULT;
const int kMiniTabWidth = 56;
const bool kRestorePopups = false;
const bool kShowImportOnBookmarkBar = true;
const bool kDownloadPageHasShowInFolder = true;
#if defined(OS_MACOSX)
const bool kShowExitMenuItem = false;
const bool kShowAboutMenuItem = false;
#else
const bool kShowExitMenuItem = true;
const bool kShowAboutMenuItem = true;
#endif
const bool kOSSupportsOtherBrowsers = true;
const bool kSizeTabButtonToTopOfTabStrip = false;
const bool kBootstrapSyncAuthentication = false;
const bool kShowOtherBrowsersInAboutMemory = true;
const bool kAlwaysOpenIncognitoWindow = false;
#endif

#if defined(OS_MACOSX)
const bool kBrowserAliveWithNoWindows = true;
#else
const bool kBrowserAliveWithNoWindows = false;
#endif

const bool kPhantomTabsEnabled = false;

bool bookmarks_enabled = true;

}  // namespace browser_defaults
<commit_msg>chromeos: Increase omnibox font size.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/defaults.h"

namespace browser_defaults {

#if defined(OS_CHROMEOS)

// Make the regular omnibox text two points larger than the nine-point font
// used in the tab strip (11pt / 72pt/in * 96px/in = 14.667px).
const double kAutocompleteEditFontPixelSize = 14.7;
const double kAutocompleteEditFontPixelSizeInPopup = 10.0;

// This is only used by AutocompletePopupViewGtk which is unused
// unless TOOLKIT_VIEWS is undefined:
const int kAutocompletePopupFontSize = 7;

const SessionStartupPref::Type kDefaultSessionStartupType =
    SessionStartupPref::LAST;
const int kMiniTabWidth = 64;
const bool kCanToggleSystemTitleBar = false;
const bool kRestorePopups = true;
const bool kShowImportOnBookmarkBar = false;
const bool kShowExitMenuItem = true;
const bool kShowAboutMenuItem = true;
const bool kOSSupportsOtherBrowsers = false;
const bool kDownloadPageHasShowInFolder = true;
const bool kSizeTabButtonToTopOfTabStrip = true;
const bool kBootstrapSyncAuthentication = true;
const bool kShowOtherBrowsersInAboutMemory = false;
const bool kAlwaysOpenIncognitoWindow = true;

#elif defined(TOOLKIT_USES_GTK)

// 13.4px = 10pt @ 96dpi.
const double kAutocompleteEditFontPixelSize = 13.4;

// On Windows, popup windows' autocomplete box have a font 5/6 the size of a
// regular window, which we duplicate here for GTK.
const double kAutocompleteEditFontPixelSizeInPopup =
    kAutocompleteEditFontPixelSize * 5.0 / 6.0;

const int kAutocompletePopupFontSize = 10;

#if defined(TOOLKIT_VIEWS)
const bool kCanToggleSystemTitleBar = false;
#else
const bool kCanToggleSystemTitleBar = true;
#endif

#endif

#if !defined(OS_CHROMEOS)

const SessionStartupPref::Type kDefaultSessionStartupType =
    SessionStartupPref::DEFAULT;
const int kMiniTabWidth = 56;
const bool kRestorePopups = false;
const bool kShowImportOnBookmarkBar = true;
const bool kDownloadPageHasShowInFolder = true;
#if defined(OS_MACOSX)
const bool kShowExitMenuItem = false;
const bool kShowAboutMenuItem = false;
#else
const bool kShowExitMenuItem = true;
const bool kShowAboutMenuItem = true;
#endif
const bool kOSSupportsOtherBrowsers = true;
const bool kSizeTabButtonToTopOfTabStrip = false;
const bool kBootstrapSyncAuthentication = false;
const bool kShowOtherBrowsersInAboutMemory = true;
const bool kAlwaysOpenIncognitoWindow = false;
#endif

#if defined(OS_MACOSX)
const bool kBrowserAliveWithNoWindows = true;
#else
const bool kBrowserAliveWithNoWindows = false;
#endif

const bool kPhantomTabsEnabled = false;

bool bookmarks_enabled = true;

}  // namespace browser_defaults
<|endoftext|>


<commit_before>// @(#)root/pyroot:$Id$
// Author: Wim Lavrijsen, Jan 2005

// Bindings
#include "PyROOT.h"
#include "PyRootType.h"
#include "MethodProxy.h"
#include "PropertyProxy.h"
#include "RootWrapper.h"
#include "TFunctionHolder.h"
#include "TemplateProxy.h"
#include "PyStrings.h"

// ROOT
#include "TClass.h"     // for method and enum finding
#include "TList.h"      // id.

// Standard
#include <string.h>
#include <string>
#include <vector>

namespace PyROOT {

namespace {

//= PyROOT type proxy construction/destruction ===============================
   PyObject* meta_alloc( PyTypeObject* metatype, Py_ssize_t nitems )
   {
   // specialized allocator, fitting in a few extra bytes for a TClassRef
      PyObject* pyclass = PyType_Type.tp_alloc( metatype, nitems );

      return pyclass;
   }

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

   void meta_dealloc( PyRootClass* pytype )
   {
      return PyType_Type.tp_dealloc( (PyObject*)pytype );
   }

////////////////////////////////////////////////////////////////////////////////
/// Called when PyRootType acts as a metaclass; since type_new always resets
/// tp_alloc, and since it does not call tp_init on types, the metaclass is
/// being fixed up here, and the class is initialized here as well.

   PyObject* pt_new( PyTypeObject* subtype, PyObject* args, PyObject* kwds )
   {
   // fixup of metaclass (left permanent, and in principle only called once b/c
   // PyROOT caches python classes)
      subtype->tp_alloc   = (allocfunc)meta_alloc;
      subtype->tp_dealloc = (destructor)meta_dealloc;

   // creation of the python-side class
      PyRootClass* result = (PyRootClass*)PyType_Type.tp_new( subtype, args, kwds );

   // initialization of class (based on name only, initially, which is lazy)

   // there's a snag here: if a python class is derived from the bound class,
   // the name will not be known by TClassRef, hence we'll use the meta class
   // name from the subtype, rather than given class name

      const char* mp = strstr( subtype->tp_name, "_meta" );
      if ( ! mp ) {
      // there has been a user meta class override in a derived class, so do
      // the consistent thing, thus allowing user control over naming
         result->fCppType = Cppyy::GetScope(
            PyROOT_PyUnicode_AsString( PyTuple_GET_ITEM( args, 0 ) ) );
      } else {
      // coming here from PyROOT, use meta class name instead of given name,
      // so that it is safe to inherit python classes from the bound class
         result->fCppType = Cppyy::GetScope(
            std::string( subtype->tp_name ).substr( 0, mp-subtype->tp_name ).c_str() );
      }

      return (PyObject*)result;
   }

//= PyROOT type metaclass behavior ===========================================
   PyObject* pt_getattro( PyObject* pyclass, PyObject* pyname )
   {
   // normal type lookup
      PyObject* attr = PyType_Type.tp_getattro( pyclass, pyname );

   // extra ROOT lookup in case of failure (e.g. for inner classes on demand)
      if ( ! attr && PyROOT_PyUnicode_CheckExact( pyname ) ) {
         PyObject *etype, *value, *trace;
         PyErr_Fetch( &etype, &value, &trace );         // clears current exception

      // filter for python specials and lookup qualified class or function
         std::string name = PyROOT_PyUnicode_AsString( pyname );
         if ( name.size() <= 2 || name.substr( 0, 2 ) != "__" ) {
            attr = CreateScopeProxy( name, pyclass );

         // namespaces may have seen updates in their list of global functions, which
         // are available as "methods" even though they're not really that
            if ( ! attr && ! PyRootType_CheckExact( pyclass ) && PyType_Check( pyclass ) ) {
               PyErr_Clear();
               PyObject* pycppname = PyObject_GetAttr( pyclass, PyStrings::gCppName );
               const char* cppname = PyROOT_PyUnicode_AsString(pycppname);
               Py_DECREF(pycppname);
               Cppyy::TCppScope_t scope = Cppyy::GetScope( cppname );
               TClass* klass = TClass::GetClass( cppname );
               if ( Cppyy::IsNamespace( scope ) ) {

               // tickle lazy lookup of functions
                  if ( ! attr ) {
                     if ( klass->GetListOfMethods()->FindObject( name.c_str() ) ) {
                     // function exists, now collect overloads
                        std::vector< PyCallable* > overloads;
                        const size_t nmeth = Cppyy::GetNumMethods( scope );
                        for ( size_t imeth = 0; imeth < nmeth; ++imeth ) {
                           Cppyy::TCppMethod_t method = Cppyy::GetMethod( scope, imeth );
                           if ( Cppyy::GetMethodName( method ) == name )
                              overloads.push_back( new TFunctionHolder( scope, method ) );
                        }

                     // Note: can't re-use Utility::AddClass here, as there's the risk of
                     // a recursive call. Simply add method directly, as we're guaranteed
                     // that it doesn't exist yet.
                        attr = (PyObject*)MethodProxy_New( name.c_str(), overloads );
                     }
                  }

               // tickle lazy lookup of data members
                  if ( ! attr ) {
                      Cppyy::TCppIndex_t dmi = Cppyy::GetDatamemberIndex( scope, name );
                      if ( 0 <= dmi ) attr = (PyObject*)PropertyProxy_New( scope, dmi );
                  }
               }

            // function templates that have not been instantiated
               if ( ! attr && klass ) {
                  TFunctionTemplate* tmpl = klass->GetFunctionTemplate( name.c_str() );
                  if ( tmpl )
                     attr = (PyObject*)TemplateProxy_New( name, pyclass );
               }

            // enums types requested as type (rather than the constants)
               if ( ! attr && klass && klass->GetListOfEnums()->FindObject( name.c_str() ) ) {
               // special case; enum types; for now, pretend int
               // TODO: although fine for C++98, this isn't correct in C++11
                  Py_INCREF( &PyInt_Type );
                  attr = (PyObject*)&PyInt_Type;
               }

               if ( attr ) {
                  PyObject_SetAttr( pyclass, pyname, attr );
                  Py_DECREF( attr );
                  attr = PyType_Type.tp_getattro( pyclass, pyname );
               }
            }

            if ( ! attr && ! PyRootType_Check( pyclass ) /* at global or module-level only */ ) {
               PyErr_Clear();
            // get class name to look up CINT tag info ...
               attr = GetCppGlobal( name /*, tag */ );
               if ( PropertyProxy_Check( attr ) ) {
                  PyObject_SetAttr( (PyObject*)Py_TYPE(pyclass), pyname, attr );
                  Py_DECREF( attr );
                  attr = PyType_Type.tp_getattro( pyclass, pyname );
               } else if ( attr )
                  PyObject_SetAttr( pyclass, pyname, attr );
            }

         }

      // if failed, then the original error is likely to be more instructive
         if ( ! attr && etype )
            PyErr_Restore( etype, value, trace );
         else if ( ! attr ) {
            PyObject* sklass = PyObject_Str( pyclass );
            PyErr_Format( PyExc_AttributeError, "%s has no attribute \'%s\'",
               PyROOT_PyUnicode_AsString( sklass ), PyROOT_PyUnicode_AsString( pyname ) );
            Py_DECREF( sklass );
         }

      // attribute is cached, if found
      }

      return attr;
   }

} // unnamed namespace

//= PyROOT object proxy type type ============================================
PyTypeObject PyRootType_Type = {
   PyVarObject_HEAD_INIT( &PyType_Type, 0 )
   (char*)"ROOT.PyRootType",  // tp_name
   sizeof(PyROOT::PyRootClass),// tp_basicsize
   0,                         // tp_itemsize
   0,                         // tp_dealloc
   0,                         // tp_print
   0,                         // tp_getattr
   0,                         // tp_setattr
   0,                         // tp_compare
   0,                         // tp_repr
   0,                         // tp_as_number
   0,                         // tp_as_sequence
   0,                         // tp_as_mapping
   0,                         // tp_hash
   0,                         // tp_call
   0,                         // tp_str
   (getattrofunc)pt_getattro, // tp_getattro
   0,                         // tp_setattro
   0,                         // tp_as_buffer
   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,     // tp_flags
   (char*)"PyROOT metatype (internal)",          // tp_doc
   0,                         // tp_traverse
   0,                         // tp_clear
   0,                         // tp_richcompare
   0,                         // tp_weaklistoffset
   0,                         // tp_iter
   0,                         // tp_iternext
   0,                         // tp_methods
   0,                         // tp_members
   0,                         // tp_getset
   &PyType_Type,              // tp_base
   0,                         // tp_dict
   0,                         // tp_descr_get
   0,                         // tp_descr_set
   0,                         // tp_dictoffset
   0,                         // tp_init
   0,                         // tp_alloc
   (newfunc)pt_new,           // tp_new
   0,                         // tp_free
   0,                         // tp_is_gc
   0,                         // tp_bases
   0,                         // tp_mro
   0,                         // tp_cache
   0,                         // tp_subclasses
   0                          // tp_weaklist
#if PY_VERSION_HEX >= 0x02030000
   , 0                        // tp_del
#endif
#if PY_VERSION_HEX >= 0x02060000
   , 0                        // tp_version_tag
#endif
#if PY_VERSION_HEX >= 0x03040000
   , 0                        // tp_finalize
#endif
};

} // namespace PyROOT
<commit_msg>[ROOT-9836] Include templated function in list of overloads<commit_after>// @(#)root/pyroot:$Id$
// Author: Wim Lavrijsen, Jan 2005

// Bindings
#include "PyROOT.h"
#include "PyRootType.h"
#include "MethodProxy.h"
#include "PropertyProxy.h"
#include "RootWrapper.h"
#include "TFunctionHolder.h"
#include "TemplateProxy.h"
#include "PyStrings.h"

// ROOT
#include "TClass.h"     // for method and enum finding
#include "TList.h"      // id.

// Standard
#include <string.h>
#include <string>
#include <vector>

namespace PyROOT {

namespace {

//= PyROOT type proxy construction/destruction ===============================
   PyObject* meta_alloc( PyTypeObject* metatype, Py_ssize_t nitems )
   {
   // specialized allocator, fitting in a few extra bytes for a TClassRef
      PyObject* pyclass = PyType_Type.tp_alloc( metatype, nitems );

      return pyclass;
   }

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

   void meta_dealloc( PyRootClass* pytype )
   {
      return PyType_Type.tp_dealloc( (PyObject*)pytype );
   }

////////////////////////////////////////////////////////////////////////////////
/// Called when PyRootType acts as a metaclass; since type_new always resets
/// tp_alloc, and since it does not call tp_init on types, the metaclass is
/// being fixed up here, and the class is initialized here as well.

   PyObject* pt_new( PyTypeObject* subtype, PyObject* args, PyObject* kwds )
   {
   // fixup of metaclass (left permanent, and in principle only called once b/c
   // PyROOT caches python classes)
      subtype->tp_alloc   = (allocfunc)meta_alloc;
      subtype->tp_dealloc = (destructor)meta_dealloc;

   // creation of the python-side class
      PyRootClass* result = (PyRootClass*)PyType_Type.tp_new( subtype, args, kwds );

   // initialization of class (based on name only, initially, which is lazy)

   // there's a snag here: if a python class is derived from the bound class,
   // the name will not be known by TClassRef, hence we'll use the meta class
   // name from the subtype, rather than given class name

      const char* mp = strstr( subtype->tp_name, "_meta" );
      if ( ! mp ) {
      // there has been a user meta class override in a derived class, so do
      // the consistent thing, thus allowing user control over naming
         result->fCppType = Cppyy::GetScope(
            PyROOT_PyUnicode_AsString( PyTuple_GET_ITEM( args, 0 ) ) );
      } else {
      // coming here from PyROOT, use meta class name instead of given name,
      // so that it is safe to inherit python classes from the bound class
         result->fCppType = Cppyy::GetScope(
            std::string( subtype->tp_name ).substr( 0, mp-subtype->tp_name ).c_str() );
      }

      return (PyObject*)result;
   }

//= PyROOT type metaclass behavior ===========================================
   PyObject* pt_getattro( PyObject* pyclass, PyObject* pyname )
   {
   // normal type lookup
      PyObject* attr = PyType_Type.tp_getattro( pyclass, pyname );

   // extra ROOT lookup in case of failure (e.g. for inner classes on demand)
      if ( ! attr && PyROOT_PyUnicode_CheckExact( pyname ) ) {
         PyObject *etype, *value, *trace;
         PyErr_Fetch( &etype, &value, &trace );         // clears current exception

      // filter for python specials and lookup qualified class or function
         std::string name = PyROOT_PyUnicode_AsString( pyname );
         if ( name.size() <= 2 || name.substr( 0, 2 ) != "__" ) {
            attr = CreateScopeProxy( name, pyclass );

         // namespaces may have seen updates in their list of global functions, which
         // are available as "methods" even though they're not really that
            if ( ! attr && ! PyRootType_CheckExact( pyclass ) && PyType_Check( pyclass ) ) {
               PyErr_Clear();
               PyObject* pycppname = PyObject_GetAttr( pyclass, PyStrings::gCppName );
               const char* cppname = PyROOT_PyUnicode_AsString(pycppname);
               Py_DECREF(pycppname);
               Cppyy::TCppScope_t scope = Cppyy::GetScope( cppname );
               TClass* klass = TClass::GetClass( cppname );
               if ( Cppyy::IsNamespace( scope ) ) {

               // tickle lazy lookup of functions
                  if ( ! attr ) {
                     if ( klass->GetListOfMethods()->FindObject( name.c_str() ) ) {
                     // function exists, now collect overloads
                        std::vector< PyCallable* > overloads;
                        const size_t nmeth = Cppyy::GetNumMethods( scope );
                        for ( size_t imeth = 0; imeth < nmeth; ++imeth ) {
                           Cppyy::TCppMethod_t method = Cppyy::GetMethod( scope, imeth );
                           auto currentName = Cppyy::GetMethodName(method);
                           // We need to compare with the resolved name too, in case there are
                           // typedefs to be resolved (e.g. Float_t -> float)
                           if (currentName == name || currentName == Cppyy::ResolveName(name))
                              overloads.push_back( new TFunctionHolder( scope, method ) );
                        }

                     // Note: can't re-use Utility::AddClass here, as there's the risk of
                     // a recursive call. Simply add method directly, as we're guaranteed
                     // that it doesn't exist yet.
                        attr = (PyObject*)MethodProxy_New( name.c_str(), overloads );
                     }
                  }

               // tickle lazy lookup of data members
                  if ( ! attr ) {
                      Cppyy::TCppIndex_t dmi = Cppyy::GetDatamemberIndex( scope, name );
                      if ( 0 <= dmi ) attr = (PyObject*)PropertyProxy_New( scope, dmi );
                  }
               }

            // function templates that have not been instantiated
               if ( ! attr && klass ) {
                  TFunctionTemplate* tmpl = klass->GetFunctionTemplate( name.c_str() );
                  if ( tmpl )
                     attr = (PyObject*)TemplateProxy_New( name, pyclass );
               }

            // enums types requested as type (rather than the constants)
               if ( ! attr && klass && klass->GetListOfEnums()->FindObject( name.c_str() ) ) {
               // special case; enum types; for now, pretend int
               // TODO: although fine for C++98, this isn't correct in C++11
                  Py_INCREF( &PyInt_Type );
                  attr = (PyObject*)&PyInt_Type;
               }

               if ( attr ) {
                  PyObject_SetAttr( pyclass, pyname, attr );
                  Py_DECREF( attr );
                  attr = PyType_Type.tp_getattro( pyclass, pyname );
               }
            }

            if ( ! attr && ! PyRootType_Check( pyclass ) /* at global or module-level only */ ) {
               PyErr_Clear();
            // get class name to look up CINT tag info ...
               attr = GetCppGlobal( name /*, tag */ );
               if ( PropertyProxy_Check( attr ) ) {
                  PyObject_SetAttr( (PyObject*)Py_TYPE(pyclass), pyname, attr );
                  Py_DECREF( attr );
                  attr = PyType_Type.tp_getattro( pyclass, pyname );
               } else if ( attr )
                  PyObject_SetAttr( pyclass, pyname, attr );
            }

         }

      // if failed, then the original error is likely to be more instructive
         if ( ! attr && etype )
            PyErr_Restore( etype, value, trace );
         else if ( ! attr ) {
            PyObject* sklass = PyObject_Str( pyclass );
            PyErr_Format( PyExc_AttributeError, "%s has no attribute \'%s\'",
               PyROOT_PyUnicode_AsString( sklass ), PyROOT_PyUnicode_AsString( pyname ) );
            Py_DECREF( sklass );
         }

      // attribute is cached, if found
      }

      return attr;
   }

} // unnamed namespace

//= PyROOT object proxy type type ============================================
PyTypeObject PyRootType_Type = {
   PyVarObject_HEAD_INIT( &PyType_Type, 0 )
   (char*)"ROOT.PyRootType",  // tp_name
   sizeof(PyROOT::PyRootClass),// tp_basicsize
   0,                         // tp_itemsize
   0,                         // tp_dealloc
   0,                         // tp_print
   0,                         // tp_getattr
   0,                         // tp_setattr
   0,                         // tp_compare
   0,                         // tp_repr
   0,                         // tp_as_number
   0,                         // tp_as_sequence
   0,                         // tp_as_mapping
   0,                         // tp_hash
   0,                         // tp_call
   0,                         // tp_str
   (getattrofunc)pt_getattro, // tp_getattro
   0,                         // tp_setattro
   0,                         // tp_as_buffer
   Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,     // tp_flags
   (char*)"PyROOT metatype (internal)",          // tp_doc
   0,                         // tp_traverse
   0,                         // tp_clear
   0,                         // tp_richcompare
   0,                         // tp_weaklistoffset
   0,                         // tp_iter
   0,                         // tp_iternext
   0,                         // tp_methods
   0,                         // tp_members
   0,                         // tp_getset
   &PyType_Type,              // tp_base
   0,                         // tp_dict
   0,                         // tp_descr_get
   0,                         // tp_descr_set
   0,                         // tp_dictoffset
   0,                         // tp_init
   0,                         // tp_alloc
   (newfunc)pt_new,           // tp_new
   0,                         // tp_free
   0,                         // tp_is_gc
   0,                         // tp_bases
   0,                         // tp_mro
   0,                         // tp_cache
   0,                         // tp_subclasses
   0                          // tp_weaklist
#if PY_VERSION_HEX >= 0x02030000
   , 0                        // tp_del
#endif
#if PY_VERSION_HEX >= 0x02060000
   , 0                        // tp_version_tag
#endif
#if PY_VERSION_HEX >= 0x03040000
   , 0                        // tp_finalize
#endif
};

} // namespace PyROOT
<|endoftext|>


<commit_before>/*
 * SlideParser.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SlideParser.hpp"

#include <iostream>

#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>

#include <core/Error.hpp>
#include <core/FilePath.hpp>
#include <core/FileSerializer.hpp>
#include <core/SafeConvert.hpp>
#include <core/StringUtils.hpp>
#include <core/text/DcfParser.hpp>

#include <session/SessionModuleContext.hpp>

using namespace core;

namespace session {
namespace modules {
namespace presentation {

namespace {

struct CompareName
{
  CompareName(const std::string& name) : name_(name) {}
  bool operator()(const Slide::Field& field) const {
    return boost::iequals(name_, field.first);
  }
  private:
    std::string name_;
};

bool isCommandField(const std::string& name)
{
   return boost::iequals(name, "help-doc") ||
          boost::iequals(name, "help-topic") ||
          boost::iequals(name, "source") ||
          boost::iequals(name, "console") ||
          boost::iequals(name, "console-input") ||
          boost::iequals(name, "execute") ||
          boost::iequals(name, "pause");
}

bool isAtCommandField(const std::string& name)
{
   return isCommandField(name) ||
          boost::iequals(name, "pause");
}

bool isValidField(const std::string& name)
{
   return isCommandField(name) ||
          boost::iequals(name, "title") ||
          boost::iequals(name, "author") ||
          boost::iequals(name, "date") ||
          boost::iequals(name, "navigation") ||
          boost::iequals(name, "incremental") ||
          boost::iequals(name, "id") ||
          boost::iequals(name, "audio") ||
          boost::iequals(name, "video") ||
          boost::iequals(name, "type") ||
          boost::iequals(name, "at");
}

std::string normalizeFieldValue(const std::string& value)
{
   std::string normalized = text::dcfMultilineAsFolded(value);
   return boost::algorithm::trim_copy(normalized);
}

} // anonymous namespace

json::Object Command::asJson() const
{
   json::Object commandJson;
   commandJson["name"] = name();
   commandJson["params"] = params();
   return commandJson;
}

json::Object AtCommand::asJson() const
{
   json::Object atCommandJson;
   atCommandJson["at"] = seconds();
   atCommandJson["command"] = command().asJson();
   return atCommandJson;
}

// default title to true unless this is a video slide
bool Slide::showTitle() const
{
   if (video().empty())
      return !boost::iequals(fieldValue("title", "true"), "false");
   else
      return boost::iequals(fieldValue("title", "false"), "true");
}

std::vector<Command> Slide::commands() const
{
   std::vector<Command> commands;
   std::vector<std::string> flds = fields();
   for (size_t i=0; i<flds.size(); i++)
   {
      std::string field = flds[i];
      if (isCommandField(field))
         commands.push_back(Command(field, fieldValue(field)));
   }
   return commands;
}

std::vector<AtCommand> Slide::atCommands() const
{
   std::vector<AtCommand> atCommands;
   boost::regex re("^([0-9]+)\\:([0-9]{2})\\s+([^\\:]+)(?:\\:\\s+(.*))?$");

   std::vector<std::string> atFields = fieldValues("at");
   BOOST_FOREACH(const std::string& atField, atFields)
   {
      boost::smatch match;
      if (boost::regex_match(atField, match, re))
      {
         std::string cmd = match[3];
         if (isAtCommandField(cmd))
         {
            int mins = safe_convert::stringTo<int>(match[1], 0);
            int secs = (mins*60) + safe_convert::stringTo<int>(match[2], 0);
            Command command(cmd, match[4]);
            atCommands.push_back(AtCommand(secs, command));
         }
         else
         {
            module_context::consoleWriteError("Unrecognized command '" +
                                              cmd + "'\n");
         }
      }
      else
      {
         module_context::consoleWriteError(
                  "Skipping at command with invalid syntax:\n   at: " +
                  atField + "\n");
      }
   }

   return atCommands;
}

std::vector<std::string> Slide::fields() const
{
   std::vector<std::string> fields;
   BOOST_FOREACH(const Field& field, fields_)
   {
      fields.push_back(field.first);
   }
   return fields;
}

std::string Slide::fieldValue(const std::string& name,
                              const std::string& defaultValue) const
{
   std::vector<Field>::const_iterator it =
        std::find_if(fields_.begin(), fields_.end(), CompareName(name));
   if (it != fields_.end())
      return normalizeFieldValue(it->second);
   else
      return defaultValue;
}

std::vector<std::string> Slide::fieldValues(const std::string& name) const
{
   std::vector<std::string> values;
   BOOST_FOREACH(const Field& field, fields_)
   {
      if (boost::iequals(name, field.first))
         values.push_back(normalizeFieldValue(field.second));
   }
   return values;
}

namespace {

void insertField(std::vector<Slide::Field>* pFields, const Slide::Field& field)
{
   pFields->push_back(field);
}

} // anonymous namespace

std::string SlideDeck::title() const
{
   if (!slides_.empty())
      return slides_[0].title();
   else
      return std::string();
}

std::string SlideDeck::navigation() const
{
   if (!slides_.empty())
      return slides_[0].navigation();
   else
      return "slides";
}

std::string SlideDeck::incremental() const
{
   std::string val = !slides_.empty() ? slides_[0].incremental() : "";
   if (!val.empty())
      return val;
   else
      return "false";
}

Error SlideDeck::readSlides(const FilePath& filePath)
{
   // clear existing
   slides_.clear();

   // capture base dir
   baseDir_ = filePath.parent();

   // read the file
   std::string slides;
   Error error = readStringFromFile(filePath, &slides);
   if (error)
      return error;

   // split into lines
   std::vector<std::string> lines;
   boost::algorithm::split(lines, slides, boost::algorithm::is_any_of("\r\n"));

   // find indexes of lines with 3 or more consecutive equals
   boost::regex re("^\\={3,}\\s*$");
   std::vector<std::size_t> headerLines;
   for (std::size_t i = 0; i<lines.size(); i++)
   {
      boost::smatch m;
      if (boost::regex_match(lines[i], m, re))
         headerLines.push_back(i);
   }

   // capture the preamble (if any)
   preamble_.clear();
   if (!headerLines.empty())
   {
      for (std::size_t i = 0; i<(headerLines[0]-1); i++)
         preamble_.append(lines[i]);
   }

   // loop through the header lines to capture the slides
   for (std::size_t i = 0; i<headerLines.size(); i++)
   {
      // line index
      std::size_t lineIndex = headerLines[i];

      // title is the line before (if there is one)
      std::string title = lineIndex > 0 ? lines[lineIndex-1] : "";

      // find the begin index (line after)
      std::size_t beginIndex = lineIndex + 1;

      // find the end index (next section or end of file)
      std::size_t endIndex;
      if (i < (headerLines.size()-1))
         endIndex = headerLines[i+1] - 1;
      else
         endIndex = lines.size();

      // now iterate through from begin to end and break into fields and content
      bool inFields = true;
      std::string fields, content;
      for (std::size_t l = beginIndex; l<endIndex; l++)
      {
         if (inFields)
         {
            std::string line = boost::algorithm::trim_copy(lines[l]);
            if (!line.empty())
               fields += line + "\n";
            else
               inFields = false;
         }
         else
         {
            content += lines[l] + "\n";
         }
      }

      // now parse the fields
      std::string errMsg;
      std::vector<Slide::Field> slideFields;
      Error error = text::parseDcfFile(fields,
                                       false,
                                       boost::bind(insertField,
                                                   &slideFields, _1),
                                       &errMsg);
      if (error)
      {
         std::string badLine = error.getProperty("line-contents");
         if (!badLine.empty())
            module_context::consoleWriteError("Invalid DCF field:\n   "
                                              + badLine + "\n");
         return error;
      }

      // validate all of the fields
      BOOST_FOREACH(const Slide::Field& field, slideFields)
      {
         if (!isValidField(field.first))
         {
            module_context::consoleWriteError("Unrecognized field '" +
                                              field.first + "'\n");
         }
      }

      // create the slide
      slides_.push_back(Slide(title, slideFields, content));
   }

   // if the deck is empty then insert a placeholder first slide
   if (slides_.empty())
   {
      slides_.push_back(Slide(filePath.parent().filename(),
                              std::vector<Slide::Field>(),
                              std::string()));
   }

   return Success();
}

} // namespace presentation
} // namespace modules
} // namesapce session

<commit_msg>don't takeup space for title if it's not provided<commit_after>/*
 * SlideParser.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SlideParser.hpp"

#include <iostream>

#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>

#include <core/Error.hpp>
#include <core/FilePath.hpp>
#include <core/FileSerializer.hpp>
#include <core/SafeConvert.hpp>
#include <core/StringUtils.hpp>
#include <core/text/DcfParser.hpp>

#include <session/SessionModuleContext.hpp>

using namespace core;

namespace session {
namespace modules {
namespace presentation {

namespace {

struct CompareName
{
  CompareName(const std::string& name) : name_(name) {}
  bool operator()(const Slide::Field& field) const {
    return boost::iequals(name_, field.first);
  }
  private:
    std::string name_;
};

bool isCommandField(const std::string& name)
{
   return boost::iequals(name, "help-doc") ||
          boost::iequals(name, "help-topic") ||
          boost::iequals(name, "source") ||
          boost::iequals(name, "console") ||
          boost::iequals(name, "console-input") ||
          boost::iequals(name, "execute") ||
          boost::iequals(name, "pause");
}

bool isAtCommandField(const std::string& name)
{
   return isCommandField(name) ||
          boost::iequals(name, "pause");
}

bool isValidField(const std::string& name)
{
   return isCommandField(name) ||
          boost::iequals(name, "title") ||
          boost::iequals(name, "author") ||
          boost::iequals(name, "date") ||
          boost::iequals(name, "navigation") ||
          boost::iequals(name, "incremental") ||
          boost::iequals(name, "id") ||
          boost::iequals(name, "audio") ||
          boost::iequals(name, "video") ||
          boost::iequals(name, "type") ||
          boost::iequals(name, "at");
}

std::string normalizeFieldValue(const std::string& value)
{
   std::string normalized = text::dcfMultilineAsFolded(value);
   return boost::algorithm::trim_copy(normalized);
}

} // anonymous namespace

json::Object Command::asJson() const
{
   json::Object commandJson;
   commandJson["name"] = name();
   commandJson["params"] = params();
   return commandJson;
}

json::Object AtCommand::asJson() const
{
   json::Object atCommandJson;
   atCommandJson["at"] = seconds();
   atCommandJson["command"] = command().asJson();
   return atCommandJson;
}

// default title to true if there is a title provided and we
// aren't in a video slide
bool Slide::showTitle() const
{
   std::string defaultTitle = (!title().empty() && video().empty()) ? "true" :
                                                                      "false";
   return boost::iequals(fieldValue("title", defaultTitle), "true");
}

std::vector<Command> Slide::commands() const
{
   std::vector<Command> commands;
   std::vector<std::string> flds = fields();
   for (size_t i=0; i<flds.size(); i++)
   {
      std::string field = flds[i];
      if (isCommandField(field))
         commands.push_back(Command(field, fieldValue(field)));
   }
   return commands;
}

std::vector<AtCommand> Slide::atCommands() const
{
   std::vector<AtCommand> atCommands;
   boost::regex re("^([0-9]+)\\:([0-9]{2})\\s+([^\\:]+)(?:\\:\\s+(.*))?$");

   std::vector<std::string> atFields = fieldValues("at");
   BOOST_FOREACH(const std::string& atField, atFields)
   {
      boost::smatch match;
      if (boost::regex_match(atField, match, re))
      {
         std::string cmd = match[3];
         if (isAtCommandField(cmd))
         {
            int mins = safe_convert::stringTo<int>(match[1], 0);
            int secs = (mins*60) + safe_convert::stringTo<int>(match[2], 0);
            Command command(cmd, match[4]);
            atCommands.push_back(AtCommand(secs, command));
         }
         else
         {
            module_context::consoleWriteError("Unrecognized command '" +
                                              cmd + "'\n");
         }
      }
      else
      {
         module_context::consoleWriteError(
                  "Skipping at command with invalid syntax:\n   at: " +
                  atField + "\n");
      }
   }

   return atCommands;
}

std::vector<std::string> Slide::fields() const
{
   std::vector<std::string> fields;
   BOOST_FOREACH(const Field& field, fields_)
   {
      fields.push_back(field.first);
   }
   return fields;
}

std::string Slide::fieldValue(const std::string& name,
                              const std::string& defaultValue) const
{
   std::vector<Field>::const_iterator it =
        std::find_if(fields_.begin(), fields_.end(), CompareName(name));
   if (it != fields_.end())
      return normalizeFieldValue(it->second);
   else
      return defaultValue;
}

std::vector<std::string> Slide::fieldValues(const std::string& name) const
{
   std::vector<std::string> values;
   BOOST_FOREACH(const Field& field, fields_)
   {
      if (boost::iequals(name, field.first))
         values.push_back(normalizeFieldValue(field.second));
   }
   return values;
}

namespace {

void insertField(std::vector<Slide::Field>* pFields, const Slide::Field& field)
{
   pFields->push_back(field);
}

} // anonymous namespace

std::string SlideDeck::title() const
{
   if (!slides_.empty())
      return slides_[0].title();
   else
      return std::string();
}

std::string SlideDeck::navigation() const
{
   if (!slides_.empty())
      return slides_[0].navigation();
   else
      return "slides";
}

std::string SlideDeck::incremental() const
{
   std::string val = !slides_.empty() ? slides_[0].incremental() : "";
   if (!val.empty())
      return val;
   else
      return "false";
}

Error SlideDeck::readSlides(const FilePath& filePath)
{
   // clear existing
   slides_.clear();

   // capture base dir
   baseDir_ = filePath.parent();

   // read the file
   std::string slides;
   Error error = readStringFromFile(filePath, &slides);
   if (error)
      return error;

   // split into lines
   std::vector<std::string> lines;
   boost::algorithm::split(lines, slides, boost::algorithm::is_any_of("\r\n"));

   // find indexes of lines with 3 or more consecutive equals
   boost::regex re("^\\={3,}\\s*$");
   std::vector<std::size_t> headerLines;
   for (std::size_t i = 0; i<lines.size(); i++)
   {
      boost::smatch m;
      if (boost::regex_match(lines[i], m, re))
         headerLines.push_back(i);
   }

   // capture the preamble (if any)
   preamble_.clear();
   if (!headerLines.empty())
   {
      for (std::size_t i = 0; i<(headerLines[0]-1); i++)
         preamble_.append(lines[i]);
   }

   // loop through the header lines to capture the slides
   for (std::size_t i = 0; i<headerLines.size(); i++)
   {
      // line index
      std::size_t lineIndex = headerLines[i];

      // title is the line before (if there is one)
      std::string title = lineIndex > 0 ? lines[lineIndex-1] : "";

      // find the begin index (line after)
      std::size_t beginIndex = lineIndex + 1;

      // find the end index (next section or end of file)
      std::size_t endIndex;
      if (i < (headerLines.size()-1))
         endIndex = headerLines[i+1] - 1;
      else
         endIndex = lines.size();

      // now iterate through from begin to end and break into fields and content
      bool inFields = true;
      std::string fields, content;
      for (std::size_t l = beginIndex; l<endIndex; l++)
      {
         if (inFields)
         {
            std::string line = boost::algorithm::trim_copy(lines[l]);
            if (!line.empty())
               fields += line + "\n";
            else
               inFields = false;
         }
         else
         {
            content += lines[l] + "\n";
         }
      }

      // now parse the fields
      std::string errMsg;
      std::vector<Slide::Field> slideFields;
      Error error = text::parseDcfFile(fields,
                                       false,
                                       boost::bind(insertField,
                                                   &slideFields, _1),
                                       &errMsg);
      if (error)
      {
         std::string badLine = error.getProperty("line-contents");
         if (!badLine.empty())
            module_context::consoleWriteError("Invalid DCF field:\n   "
                                              + badLine + "\n");
         return error;
      }

      // validate all of the fields
      BOOST_FOREACH(const Slide::Field& field, slideFields)
      {
         if (!isValidField(field.first))
         {
            module_context::consoleWriteError("Unrecognized field '" +
                                              field.first + "'\n");
         }
      }

      // create the slide
      slides_.push_back(Slide(title, slideFields, content));
   }

   // if the deck is empty then insert a placeholder first slide
   if (slides_.empty())
   {
      slides_.push_back(Slide(filePath.parent().filename(),
                              std::vector<Slide::Field>(),
                              std::string()));
   }

   return Success();
}

} // namespace presentation
} // namespace modules
} // namesapce session

<|endoftext|>


<commit_before>#include "ImageFileImporter.hpp"
#include "MetaImageImporter.hpp"
#ifdef FAST_MODULE_DICOM
#include "DICOMFileImporter.hpp"
#endif
#ifdef FAST_MODULE_VISUALIZATION
#include "ImageImporter.hpp"
#endif
#include "FAST/Data/Image.hpp"
#include <algorithm>
#include <utility>

namespace fast {

ImageFileImporter::ImageFileImporter() {
    createOutputPort<Image>(0);
    setMainDevice(Host::getInstance()); // Default is to put image on host
}

ImageFileImporter::ImageFileImporter(std::string filename) : FileImporter(filename) {
    createOutputPort<Image>(0);
    setMainDevice(Host::getInstance()); // Default is to put image on host
    setFilename(filename);
}

inline bool matchExtension(std::string extension, std::string extension2) {
    // Convert to lower case first
    std::transform(extension2.begin(), extension2.end(), extension2.begin(), ::tolower);
    return extension == extension2;

}

void ImageFileImporter::execute() {
    if(m_filename.empty())
        throw Exception("No filename was given to the ImageFileImporter");

    if(!fileExists(m_filename))
        throw FileNotFoundException(m_filename);

    // Get file extension
    size_t pos = m_filename.rfind(".", -5);
    if(pos == std::string::npos) {
        reportWarning() << "Filename " << m_filename << " had no extension, guessing it to be DICOM.." << reportEnd();
#ifdef FAST_MODULE_DICOM
        auto importer = DICOMFileImporter::New();
        importer->setMainDevice(getMainDevice());
        importer->setFilename(m_filename);
        DataChannel::pointer port = importer->getOutputPort();
        importer->update(); // Have to to update because otherwise the data will not be available
        Image::pointer data = port->getNextFrame<Image>();
        addOutputData(0, data);
#else
        throw Exception("The ImageFileImporter needs the dicom module (DCMTK) to be enabled in order to read dicom files.");
#endif
    } else {
        std::string ext = m_filename.substr(pos + 1);
        if(matchExtension(ext, "mhd")) {
            MetaImageImporter::pointer importer = MetaImageImporter::New();
            importer->setMainDevice(getMainDevice());
            importer->setFilename(m_filename);
            DataChannel::pointer port = importer->getOutputPort();
            importer->update(); // Have to to update because otherwise the data will not be available
            Image::pointer data = port->getNextFrame<Image>();
            addOutputData(0, data);
        } else if(matchExtension(ext, "dcm")) {
#ifdef FAST_MODULE_DICOM
            auto importer = DICOMFileImporter::New();
            importer->setFilename(m_filename);
            importer->setMainDevice(getMainDevice());
            DataChannel::pointer port = importer->getOutputPort();
            importer->update(); // Have to to update because otherwise the data will not be available
            Image::pointer data = port->getNextFrame<Image>();
            addOutputData(0, data);
#else
            throw Exception("The ImageFileImporter needs the dicom module (DCMTK) to be enabled in order to read dicom files.");
#endif
        } else if(matchExtension(ext, "jpg") ||
                  matchExtension(ext, "jpeg") ||
                  matchExtension(ext, "png") ||
                  matchExtension(ext, "bmp")) {
#ifdef FAST_MODULE_VISUALIZATION
            auto importer = ImageImporter::New();
            importer->setFilename(m_filename);
            importer->setMainDevice(getMainDevice());
            DataChannel::pointer port = importer->getOutputPort();
            importer->update(); // Have to to update because otherwise the data will not be available
            Image::pointer data = port->getNextFrame<Image>();
            addOutputData(0, data);
#else
            throw Exception("The ImageFileImporter needs the visualization module (Qt) to be enabled in order to read image files.");
#endif
        } else {
            throw Exception("The ImageFileImporter does not recognize the file extension " + ext);
        }
    }

}

void ImageFileImporter::loadAttributes() {
    setFilename(getStringAttribute("filename"));
}

}
<commit_msg>Enable ImageFileImporter to open normal tif/tiff files<commit_after>#include "ImageFileImporter.hpp"
#include "MetaImageImporter.hpp"
#ifdef FAST_MODULE_DICOM
#include "DICOMFileImporter.hpp"
#endif
#ifdef FAST_MODULE_VISUALIZATION
#include "ImageImporter.hpp"
#endif
#include "FAST/Data/Image.hpp"
#include <algorithm>
#include <utility>

namespace fast {

ImageFileImporter::ImageFileImporter() {
    createOutputPort<Image>(0);
    setMainDevice(Host::getInstance()); // Default is to put image on host
}

ImageFileImporter::ImageFileImporter(std::string filename) : FileImporter(filename) {
    createOutputPort<Image>(0);
    setMainDevice(Host::getInstance()); // Default is to put image on host
    setFilename(filename);
}

inline bool matchExtension(std::string extension, std::string extension2) {
    // Convert to lower case first
    std::transform(extension2.begin(), extension2.end(), extension2.begin(), ::tolower);
    return extension == extension2;

}

void ImageFileImporter::execute() {
    if(m_filename.empty())
        throw Exception("No filename was given to the ImageFileImporter");

    if(!fileExists(m_filename))
        throw FileNotFoundException(m_filename);

    // Get file extension
    size_t pos = m_filename.rfind(".", -5);
    if(pos == std::string::npos) {
        reportWarning() << "Filename " << m_filename << " had no extension, guessing it to be DICOM.." << reportEnd();
#ifdef FAST_MODULE_DICOM
        auto importer = DICOMFileImporter::New();
        importer->setMainDevice(getMainDevice());
        importer->setFilename(m_filename);
        DataChannel::pointer port = importer->getOutputPort();
        importer->update(); // Have to to update because otherwise the data will not be available
        Image::pointer data = port->getNextFrame<Image>();
        addOutputData(0, data);
#else
        throw Exception("The ImageFileImporter needs the dicom module (DCMTK) to be enabled in order to read dicom files.");
#endif
    } else {
        std::string ext = m_filename.substr(pos + 1);
        if(matchExtension(ext, "mhd")) {
            MetaImageImporter::pointer importer = MetaImageImporter::New();
            importer->setMainDevice(getMainDevice());
            importer->setFilename(m_filename);
            DataChannel::pointer port = importer->getOutputPort();
            importer->update(); // Have to to update because otherwise the data will not be available
            Image::pointer data = port->getNextFrame<Image>();
            addOutputData(0, data);
        } else if(matchExtension(ext, "dcm")) {
#ifdef FAST_MODULE_DICOM
            auto importer = DICOMFileImporter::New();
            importer->setFilename(m_filename);
            importer->setMainDevice(getMainDevice());
            DataChannel::pointer port = importer->getOutputPort();
            importer->update(); // Have to to update because otherwise the data will not be available
            Image::pointer data = port->getNextFrame<Image>();
            addOutputData(0, data);
#else
            throw Exception("The ImageFileImporter needs the dicom module (DCMTK) to be enabled in order to read dicom files.");
#endif
        } else if(matchExtension(ext, "jpg") ||
                  matchExtension(ext, "jpeg") ||
                  matchExtension(ext, "png") ||
                  matchExtension(ext, "bmp") ||
                  matchExtension(ext, "tif") ||
                  matchExtension(ext, "tiff")
                ) {
            auto importer = ImageImporter::New();
            importer->setFilename(m_filename);
            importer->setMainDevice(getMainDevice());
            DataChannel::pointer port = importer->getOutputPort();
            importer->update(); // Have to to update because otherwise the data will not be available
            Image::pointer data = port->getNextFrame<Image>();
            addOutputData(0, data);
        } else {
            throw Exception("The ImageFileImporter does not recognize the file extension " + ext);
        }
    }

}

void ImageFileImporter::loadAttributes() {
    setFilename(getStringAttribute("filename"));
}

}
<|endoftext|>


<commit_before>

#include "grove_comp_hmc5883l.h"

GroveCompass::GroveCompass(int pinsda, int pinscl)
{
    this->i2c = (I2C_T *)malloc(sizeof(I2C_T));
    suli_i2c_init(i2c, pinsda, pinscl);
}

bool GroveCompass::GroveCompass::write_setup(void)
{
    uint8_t regValue = 0x00;

	//setScale(this->i2c, (float)1.3); // Set the scale of the compass.
	regValue = 0x01;
	m_Scale = 0.92;
	// Setting is in the top 3 bits of the register.
	regValue = regValue << 5;
	//write(CONFIGURATION_REGISTERB, regValue);
	cmdbuf[0] = CONFIGURATION_REGISTERB;
	cmdbuf[1] = regValue;
	suli_i2c_write(this->i2c, HMC5883L_ADDRESS, cmdbuf, 2);
	setMeasurementMode(this->i2c, MEASUREMENT_CONTINUOUS); // Set the measurement mode to Continuous

    return true;
}

void GroveCompass::setMeasurementMode(I2C_T *i2c, uint8_t mode)
{
	//write(MODE_REGISTER, mode);
	cmdbuf[0] = MODE_REGISTER;
	cmdbuf[1] = mode;
	suli_i2c_write(i2c, HMC5883L_ADDRESS, cmdbuf, 2);
}

void GroveCompass::grove_compass_getxyz_raw(I2C_T *i2c, int16_t *x, int16_t *y, int16_t *z)
{
//	cmdbuf[0] = DATA_REGISTER_BEGIN;
//	suli_i2c_write(i2c, HMC5883L_ADDRESS, &cmdbuf[0], 1);
//	suli_i2c_read(i2c, HMC5883L_ADDRESS, databuf, 6);
	suli_i2c_read_reg(i2c, HMC5883L_ADDRESS, DATA_REGISTER_BEGIN, databuf, 6);
	*x = (databuf[0] << 8) | databuf[1];
	*y = (databuf[2] << 8) | databuf[3];
	*z = (databuf[4] << 8) | databuf[5];
}

bool GroveCompass::grove_compass_getxyz_scaled(I2C_T *i2c, float *cx, float *cy, float *cz)
{
    int16_t x,y,z;

    grove_compass_getxyz_raw(i2c, &x,&y,&z);
	*cx = (float)x * m_Scale;
	*cy = (float)y * m_Scale;
	*cz = (float)z * m_Scale;

	return true;
}

bool GroveCompass::read_compass_heading(float *heading)
{
    float cx, cy, cz;

	grove_compass_getxyz_scaled(this->i2c, &cx, &cy, &cz);

	float head = atan2(cy, cx) - 0.0457;

	// Correct for when signs are reversed.
	if(head < 0)
	head += 2*PI;

	// Check for wrap due to addition of declination.
	if(head > 2*PI)
	head -= 2*PI;

	// Convert radians to degrees for readability.
	*heading = head * 180 / PI;

	return true;
}

<commit_msg>bug fixed: HMC5883L: xyz -> xzy<commit_after>

#include "grove_comp_hmc5883l.h"

GroveCompass::GroveCompass(int pinsda, int pinscl)
{
    this->i2c = (I2C_T *)malloc(sizeof(I2C_T));
    suli_i2c_init(i2c, pinsda, pinscl);
}

bool GroveCompass::GroveCompass::write_setup(void)
{
    uint8_t regValue = 0x00;

	//setScale(this->i2c, (float)1.3); // Set the scale of the compass.
	regValue = 0x01;
	m_Scale = 0.92;
	// Setting is in the top 3 bits of the register.
	regValue = regValue << 5;
	//write(CONFIGURATION_REGISTERB, regValue);
	cmdbuf[0] = CONFIGURATION_REGISTERB;
	cmdbuf[1] = regValue;
	suli_i2c_write(this->i2c, HMC5883L_ADDRESS, cmdbuf, 2);
	setMeasurementMode(this->i2c, MEASUREMENT_CONTINUOUS); // Set the measurement mode to Continuous

    return true;
}

void GroveCompass::setMeasurementMode(I2C_T *i2c, uint8_t mode)
{
	//write(MODE_REGISTER, mode);
	cmdbuf[0] = MODE_REGISTER;
	cmdbuf[1] = mode;
	suli_i2c_write(i2c, HMC5883L_ADDRESS, cmdbuf, 2);
}

void GroveCompass::grove_compass_getxyz_raw(I2C_T *i2c, int16_t *x, int16_t *y, int16_t *z)
{
//	cmdbuf[0] = DATA_REGISTER_BEGIN;
//	suli_i2c_write(i2c, HMC5883L_ADDRESS, &cmdbuf[0], 1);
//	suli_i2c_read(i2c, HMC5883L_ADDRESS, databuf, 6);
	suli_i2c_read_reg(i2c, HMC5883L_ADDRESS, DATA_REGISTER_BEGIN, databuf, 6);
	*x = (databuf[0] << 8) | databuf[1];
	*z = (databuf[2] << 8) | databuf[3];
	*y = (databuf[4] << 8) | databuf[5];
}

bool GroveCompass::grove_compass_getxyz_scaled(I2C_T *i2c, float *cx, float *cy, float *cz)
{
    int16_t x,y,z;

    grove_compass_getxyz_raw(i2c, &x,&y,&z);
	*cx = (float)x * m_Scale;
	*cy = (float)y * m_Scale;
	*cz = (float)z * m_Scale;

	return true;
}

bool GroveCompass::read_compass_heading(float *heading)
{
    float cx, cy, cz;

	grove_compass_getxyz_scaled(this->i2c, &cx, &cy, &cz);

	float head = atan2(cy, cx) - 0.0457;

	// Correct for when signs are reversed.
	if(head < 0)
	head += 2*PI;

	// Check for wrap due to addition of declination.
	if(head > 2*PI)
	head -= 2*PI;

	// Convert radians to degrees for readability.
	*heading = head * 180 / PI;

	return true;
}

<|endoftext|>