<commit_before>#pragma once

#include "simlib/concat_tostr.hh"
#include "simlib/file_path.hh"
#include "simlib/string_compare.hh"
#include "simlib/string_transform.hh"

#include <map>
#include <utility>
#include <vector>

class ConfigFile {
public:
	class ParseError : public std::runtime_error {
		std::string diagnostics_;

	public:
		explicit ParseError(const std::string& msg)
		: runtime_error(msg) {}

		template <class... Args,
		          std::enable_if_t<(is_string_argument<Args> and ...), int> = 0>
		ParseError(size_t line, size_t pos, Args&&... msg)
		: runtime_error(concat_tostr("line ", line, ':', pos, ": ",
		                             std::forward<Args>(msg)...)) {}

		ParseError(const ParseError& pe) = default;
		ParseError(ParseError&&) noexcept = default;
		ParseError& operator=(const ParseError& pe) = default;
		ParseError& operator=(ParseError&&) noexcept = default;

		using runtime_error::what;

		[[nodiscard]] const std::string& diagnostics() const noexcept {
			return diagnostics_;
		}

		~ParseError() noexcept override = default;

		friend class ConfigFile;
	};

	class Variable {
	public:
		static constexpr uint8_t SET = 1; // set if variable appears in the
		                                  // config
		static constexpr uint8_t ARRAY = 2; // set if variable is an array

		struct ValueSpan {
			size_t beg = 0;
			size_t end = 0;
		};

	private:
		uint8_t flag_ = 0;
		std::string str_;
		std::vector<std::string> arr_;
		ValueSpan value_span_; // value occupies [beg, end) range of the file

		void unset() noexcept {
			flag_ = 0;
			str_.clear();
			arr_.clear();
		}

	public:
		Variable() {} // NOLINT(modernize-use-equals-default): compiler bug

		[[nodiscard]] bool is_set() const noexcept { return flag_ & SET; }

		[[nodiscard]] bool is_array() const noexcept { return flag_ & ARRAY; }

		// Returns value as bool or false on error
		[[nodiscard]] bool as_bool() const noexcept {
			return (str_ == "1" || lower_equal(str_, "on") ||
			        lower_equal(str_, "true"));
		}

		template <class T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
		[[nodiscard]] std::optional<T> as() const noexcept {
			return str2num<T>(str_);
		}

		// Returns value as string (empty if not a string or variable isn't
		// set)
		[[nodiscard]] const std::string& as_string() const noexcept {
			return str_;
		}

		// Returns value as array (empty if not an array or variable isn't set)
		[[nodiscard]] const std::vector<std::string>&
		as_array() const noexcept {
			return arr_;
		}

		[[nodiscard]] ValueSpan value_span() const noexcept {
			return value_span_;
		}

		friend class ConfigFile;
	};

private:
	std::map<std::string, Variable, std::less<>> vars; // (name => value)
	// TODO: ^ maybe StringView would be better?
	static inline const Variable null_var{};

public:
	ConfigFile() = default;

	ConfigFile(const ConfigFile&) = default;
	ConfigFile(ConfigFile&&) noexcept = default;
	ConfigFile& operator=(const ConfigFile&) = default;
	ConfigFile& operator=(ConfigFile&&) noexcept = default;

	~ConfigFile() = default;

	// Adds variables @p names to variable set, ignores duplications
	template <class... Args>
	void add_vars(Args&&... names) {
		int t[] = {(vars.emplace(std::forward<Args>(names), Variable{}), 0)...};
		(void)t;
	}

	// Clears variable set
	void clear() { vars.clear(); }

	// Returns a reference to a variable @p name from variable set or to a
	// null_var
	[[nodiscard]] const Variable&
	get_var(const StringView& name) const noexcept {
		return (*this)[name];
	}

	const Variable& operator[](const StringView& name) const noexcept {
		auto it = vars.find(name);
		return (it != vars.end() ? it->second : null_var);
	}

	[[nodiscard]] const decltype(vars)& get_vars() const { return vars; }

	/**
	 * @brief Loads config (variables) form file @p pathname
	 * @details Uses load_config_from_string()
	 *
	 * @param pathname config file
	 * @param load_all whether load all variables from @p pathname or load only
	 *   these from variable set
	 *
	 * @errors Throws an exception std::runtime_error if an open(2) error
	 *   occurs and all exceptions from load_config_from_string()
	 */
	void load_config_from_file(FilePath pathname, bool load_all = false);

	/**
	 * @brief Loads config (variables) form string @p config
	 *
	 * @param config input string
	 * @param load_all whether load all variables from @p config or load only
	 *   these from variable set
	 *
	 * @errors Throws an exception (ParseError) if an error occurs
	 */
	void load_config_from_string(std::string config, bool load_all = false);

	// Check if string @p str is a valid string literal
	static bool is_string_literal(StringView str) noexcept;

	/**
	 * @brief Escapes unsafe sequences in str
	 * @details '\'' replaces with "''"
	 *
	 * @param str input string
	 *
	 * @return escaped, single-quoted string
	 */
	static std::string escape_to_single_quoted_string(StringView str);

	/**
	 * @brief Escapes unsafe sequences in str
	 * @details Escapes '\"' with "\\\"" and characters for which
	 *   is_cntrl(3) != 0 with "\\xnn" sequence where n is a hexadecimal digit.
	 *
	 * @param str input string
	 *
	 * @return escaped, double-quoted string
	 */
	static std::string escape_to_double_quoted_string(StringView str);

	/**
	 * @brief Escapes unsafe sequences in str
	 * @details Escapes '\"' with "\\\"" and characters for which
	 *   is_print(3) == 0 with "\\xnn" sequence where n is a hexadecimal digit.
	 *
	 *   The difference between escape_to_double_quoted_string() and this
	 *   function is that characters (bytes) not from interval [0, 127] are also
	 *   escaped hexadecimally, that is why this option is not recommended when
	 *   using utf-8 encoding - it will escape every non-ascii character (byte)
	 *
	 * @param str input string
	 *
	 * @return escaped, double-quoted string
	 */
	static std::string full_escape_to_double_quoted_string(StringView str);

	/**
	 * @brief Converts string @p str so that it can be safely placed in config
	 *   file
	 * @details Possible cases:
	 *   1) @p str contains '\'' or character for which iscrtnl(3) != 0:
	 *     Escaped @p str via escape_to_double_quoted_string() will be returned.
	 *   2) Otherwise if @p str is string literal (is_string_literal(@p str)):
	 *     Unchanged @p str will be returned.
	 *   3) Otherwise:
	 *     Escaped @p str via escape_to_single_quoted_string() will be returned.
	 *
	 *   Examples:
	 *     "" -> '' (single quoted string, special case)
	 *     "foo-bar" -> foo-bar (string literal)
	 *     "line: 1\nab d E\n" -> "line: 1\nab d E\n" (double quoted string)
	 *     " My awesome text" -> ' My awesome text' (single quoted string)
	 *     " \\\\\\\\ " -> ' \\\\ ' (single quoted string)
	 *     " '\t\n' ś " -> " '\t\n' ś " (double quoted string)
	 *
	 * @param str input string
	 *
	 * @return escaped (and possibly quoted) string
	 */
	static std::string escape_string(StringView str);

	/**
	 * @brief Converts string @p str so that it can be safely placed in config
	 *   file
	 * @details Possible cases:
	 *   1) @p str contains '\'' or character for which is_print(3) == 0:
	 *     Escaped @p str via full_escape_to_double_quoted_string() will be
	 *     returned.
	 *   2) Otherwise if @p str is string literal (is_string_literal(@p str)):
	 *     Unchanged @p str will be returned.
	 *   3) Otherwise:
	 *     Escaped @p str via escape_to_single_quoted_string() will be returned.
	 *
	 *   The difference between escape_string() and this function is that
	 *   characters not from interval [0, 127] are also escaped hexadecimally
	 *   with full_escape_to_double_quoted_string()
	 *
	 *   Examples:
	 *     "" -> '' (single quoted string, special case)
	 *     "foo-bar" -> foo-bar (string literal)
	 *     "line: 1\nab d E\n" -> "line: 1\nab d E\n" (double quoted string)
	 *     " My awesome text" -> ' My awesome text' (single quoted string)
	 *     " \\\\\\\\ " -> ' \\\\ ' (single quoted string)
	 *     " '\t\n' ś " -> " '\t\n' ś " (double quoted string)
	 *
	 * @param str input string
	 *
	 * @return escaped (and possibly quoted) string
	 */
	static std::string full_escape_string(StringView str);
};
<commit_msg>config_file.hh: fixed doc comment of full_escape_string()<commit_after>#pragma once

#include "simlib/concat_tostr.hh"
#include "simlib/file_path.hh"
#include "simlib/string_compare.hh"
#include "simlib/string_transform.hh"

#include <map>
#include <utility>
#include <vector>

class ConfigFile {
public:
	class ParseError : public std::runtime_error {
		std::string diagnostics_;

	public:
		explicit ParseError(const std::string& msg)
		: runtime_error(msg) {}

		template <class... Args,
		          std::enable_if_t<(is_string_argument<Args> and ...), int> = 0>
		ParseError(size_t line, size_t pos, Args&&... msg)
		: runtime_error(concat_tostr("line ", line, ':', pos, ": ",
		                             std::forward<Args>(msg)...)) {}

		ParseError(const ParseError& pe) = default;
		ParseError(ParseError&&) noexcept = default;
		ParseError& operator=(const ParseError& pe) = default;
		ParseError& operator=(ParseError&&) noexcept = default;

		using runtime_error::what;

		[[nodiscard]] const std::string& diagnostics() const noexcept {
			return diagnostics_;
		}

		~ParseError() noexcept override = default;

		friend class ConfigFile;
	};

	class Variable {
	public:
		static constexpr uint8_t SET = 1; // set if variable appears in the
		                                  // config
		static constexpr uint8_t ARRAY = 2; // set if variable is an array

		struct ValueSpan {
			size_t beg = 0;
			size_t end = 0;
		};

	private:
		uint8_t flag_ = 0;
		std::string str_;
		std::vector<std::string> arr_;
		ValueSpan value_span_; // value occupies [beg, end) range of the file

		void unset() noexcept {
			flag_ = 0;
			str_.clear();
			arr_.clear();
		}

	public:
		Variable() {} // NOLINT(modernize-use-equals-default): compiler bug

		[[nodiscard]] bool is_set() const noexcept { return flag_ & SET; }

		[[nodiscard]] bool is_array() const noexcept { return flag_ & ARRAY; }

		// Returns value as bool or false on error
		[[nodiscard]] bool as_bool() const noexcept {
			return (str_ == "1" || lower_equal(str_, "on") ||
			        lower_equal(str_, "true"));
		}

		template <class T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
		[[nodiscard]] std::optional<T> as() const noexcept {
			return str2num<T>(str_);
		}

		// Returns value as string (empty if not a string or variable isn't
		// set)
		[[nodiscard]] const std::string& as_string() const noexcept {
			return str_;
		}

		// Returns value as array (empty if not an array or variable isn't set)
		[[nodiscard]] const std::vector<std::string>&
		as_array() const noexcept {
			return arr_;
		}

		[[nodiscard]] ValueSpan value_span() const noexcept {
			return value_span_;
		}

		friend class ConfigFile;
	};

private:
	std::map<std::string, Variable, std::less<>> vars; // (name => value)
	// TODO: ^ maybe StringView would be better?
	static inline const Variable null_var{};

public:
	ConfigFile() = default;

	ConfigFile(const ConfigFile&) = default;
	ConfigFile(ConfigFile&&) noexcept = default;
	ConfigFile& operator=(const ConfigFile&) = default;
	ConfigFile& operator=(ConfigFile&&) noexcept = default;

	~ConfigFile() = default;

	// Adds variables @p names to variable set, ignores duplications
	template <class... Args>
	void add_vars(Args&&... names) {
		int t[] = {(vars.emplace(std::forward<Args>(names), Variable{}), 0)...};
		(void)t;
	}

	// Clears variable set
	void clear() { vars.clear(); }

	// Returns a reference to a variable @p name from variable set or to a
	// null_var
	[[nodiscard]] const Variable&
	get_var(const StringView& name) const noexcept {
		return (*this)[name];
	}

	const Variable& operator[](const StringView& name) const noexcept {
		auto it = vars.find(name);
		return (it != vars.end() ? it->second : null_var);
	}

	[[nodiscard]] const decltype(vars)& get_vars() const { return vars; }

	/**
	 * @brief Loads config (variables) form file @p pathname
	 * @details Uses load_config_from_string()
	 *
	 * @param pathname config file
	 * @param load_all whether load all variables from @p pathname or load only
	 *   these from variable set
	 *
	 * @errors Throws an exception std::runtime_error if an open(2) error
	 *   occurs and all exceptions from load_config_from_string()
	 */
	void load_config_from_file(FilePath pathname, bool load_all = false);

	/**
	 * @brief Loads config (variables) form string @p config
	 *
	 * @param config input string
	 * @param load_all whether load all variables from @p config or load only
	 *   these from variable set
	 *
	 * @errors Throws an exception (ParseError) if an error occurs
	 */
	void load_config_from_string(std::string config, bool load_all = false);

	// Check if string @p str is a valid string literal
	static bool is_string_literal(StringView str) noexcept;

	/**
	 * @brief Escapes unsafe sequences in str
	 * @details '\'' replaces with "''"
	 *
	 * @param str input string
	 *
	 * @return escaped, single-quoted string
	 */
	static std::string escape_to_single_quoted_string(StringView str);

	/**
	 * @brief Escapes unsafe sequences in str
	 * @details Escapes '\"' with "\\\"" and characters for which
	 *   is_cntrl(3) != 0 with "\\xnn" sequence where n is a hexadecimal digit.
	 *
	 * @param str input string
	 *
	 * @return escaped, double-quoted string
	 */
	static std::string escape_to_double_quoted_string(StringView str);

	/**
	 * @brief Escapes unsafe sequences in str
	 * @details Escapes '\"' with "\\\"" and characters for which
	 *   is_print(3) == 0 with "\\xnn" sequence where n is a hexadecimal digit.
	 *
	 *   The difference between escape_to_double_quoted_string() and this
	 *   function is that characters (bytes) not from interval [0, 127] are also
	 *   escaped hexadecimally, that is why this option is not recommended when
	 *   using utf-8 encoding - it will escape every non-ascii character (byte)
	 *
	 * @param str input string
	 *
	 * @return escaped, double-quoted string
	 */
	static std::string full_escape_to_double_quoted_string(StringView str);

	/**
	 * @brief Converts string @p str so that it can be safely placed in config
	 *   file
	 * @details Possible cases:
	 *   1) @p str contains '\'' or character for which iscrtnl(3) != 0:
	 *     Escaped @p str via escape_to_double_quoted_string() will be returned.
	 *   2) Otherwise if @p str is string literal (is_string_literal(@p str)):
	 *     Unchanged @p str will be returned.
	 *   3) Otherwise:
	 *     Escaped @p str via escape_to_single_quoted_string() will be returned.
	 *
	 *   Examples:
	 *     "" -> '' (single quoted string, special case)
	 *     "foo-bar" -> foo-bar (string literal)
	 *     "line: 1\nab d E\n" -> "line: 1\nab d E\n" (double quoted string)
	 *     " My awesome text" -> ' My awesome text' (single quoted string)
	 *     " \\\\\\\\ " -> ' \\\\ ' (single quoted string)
	 *     " '\t\n' ś " -> " '\t\n' ś " (double quoted string)
	 *
	 * @param str input string
	 *
	 * @return escaped (and possibly quoted) string
	 */
	static std::string escape_string(StringView str);

	/**
	 * @brief Converts string @p str so that it can be safely placed in config
	 *   file
	 * @details Possible cases:
	 *   1) @p str contains '\'' or character for which is_print(3) == 0:
	 *     Escaped @p str via full_escape_to_double_quoted_string() will be
	 *     returned.
	 *   2) Otherwise if @p str is string literal (is_string_literal(@p str)):
	 *     Unchanged @p str will be returned.
	 *   3) Otherwise:
	 *     Escaped @p str via escape_to_single_quoted_string() will be returned.
	 *
	 *   The difference between escape_string() and this function is that
	 *   characters not from interval [0, 127] are also escaped hexadecimally
	 *   with full_escape_to_double_quoted_string()
	 *
	 *   Examples:
	 *     "" -> '' (single quoted string, special case)
	 *     "foo-bar" -> foo-bar (string literal)
	 *     "line: 1\nab d E\n" -> "line: 1\nab d E\n" (double quoted string)
	 *     " My awesome text" -> ' My awesome text' (single quoted string)
	 *     " \\\\\\\\ " -> ' \\\\ ' (single quoted string)
	 *     " '\t\n' ś " -> " '\t\n' \xc5\x9b " (double quoted string)
	 *
	 * @param str input string
	 *
	 * @return escaped (and possibly quoted) string
	 */
	static std::string full_escape_string(StringView str);
};
<|endoftext|>


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

#ifndef SPIRV_CROSS_IMAGE_HPP
#define SPIRV_CROSS_IMAGE_HPP

#ifndef GLM_SWIZZLE
#define GLM_SWIZZLE
#endif

#ifndef GLM_FORCE_RADIANS
#define GLM_FORCE_RADIANS
#endif

#include <glm/glm.hpp>

namespace spirv_cross
{
template <typename T>
struct image2DBase
{
	virtual ~image2DBase() = default;
	inline virtual T load(glm::ivec2 coord)
	{
		return T(0, 0, 0, 1);
	}
	inline virtual void store(glm::ivec2 coord, const T &v)
	{
	}
};

typedef image2DBase<glm::vec4> image2D;
typedef image2DBase<glm::ivec4> iimage2D;
typedef image2DBase<glm::uvec4> uimage2D;

template <typename T>
inline T imageLoad(const image2DBase<T> &image, glm::ivec2 coord)
{
	return image.load(coord);
}

template <typename T>
void imageStore(image2DBase<T> &image, glm::ivec2 coord, const T &value)
{
	image.store(coord, value);
}
}

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

#ifndef SPIRV_CROSS_IMAGE_HPP
#define SPIRV_CROSS_IMAGE_HPP

#ifndef GLM_SWIZZLE
#define GLM_SWIZZLE
#endif

#ifndef GLM_FORCE_RADIANS
#define GLM_FORCE_RADIANS
#endif

#include <glm/glm.hpp>

namespace spirv_cross
{
template <typename T>
struct image2DBase
{
	virtual ~image2DBase() = default;
	inline virtual T load(glm::ivec2 coord) const
	{
		return T(0, 0, 0, 1);
	}
	inline virtual void store(glm::ivec2 coord, const T &v)
	{
	}
};

typedef image2DBase<glm::vec4> image2D;
typedef image2DBase<glm::ivec4> iimage2D;
typedef image2DBase<glm::uvec4> uimage2D;

template <typename T>
inline T imageLoad(const image2DBase<T> &image, glm::ivec2 coord)
{
	return image.load(coord);
}

template <typename T>
void imageStore(image2DBase<T> &image, glm::ivec2 coord, const T &value)
{
	image.store(coord, value);
}
}

#endif
<|endoftext|>


<commit_before>#include "categories_holder.hpp"
#include "search_delimiters.hpp"
#include "search_string_utils.hpp"
#include "classificator.hpp"

#include "../coding/reader.hpp"
#include "../coding/reader_streambuf.hpp"

#include "../base/logging.hpp"
#include "../base/stl_add.hpp"

namespace
{

enum State
{
  EParseTypes,
  EParseLanguages
};

}  // unnamed namespace

int8_t const CategoriesHolder::UNSUPPORTED_LOCALE_CODE;

CategoriesHolder::CategoriesHolder(Reader * reader)
{
  ReaderStreamBuf buffer(reader);
  istream s(&buffer);
  LoadFromStream(s);
}

void CategoriesHolder::AddCategory(Category & cat, vector<uint32_t> & types)
{
  if (!cat.m_synonyms.empty() && !types.empty())
  {
    shared_ptr<Category> p(new Category());
    p->Swap(cat);

    for (size_t i = 0; i < types.size(); ++i)
      m_type2cat.insert(make_pair(types[i], p));

    for (size_t i = 0; i < p->m_synonyms.size(); ++i)
    {
      ASSERT(p->m_synonyms[i].m_locale != UNSUPPORTED_LOCALE_CODE, ());

      StringT const uniName = search::NormalizeAndSimplifyString(p->m_synonyms[i].m_name);

      vector<StringT> tokens;
      SplitUniString(uniName, MakeBackInsertFunctor(tokens), search::Delimiters());

      for (size_t j = 0; j < tokens.size(); ++j)
        for (size_t k = 0; k < types.size(); ++k)
          if (ValidKeyToken(tokens[j]))
            m_name2type.insert(make_pair(make_pair(p->m_synonyms[i].m_locale, tokens[j]), types[k]));
    }
  }

  cat.m_synonyms.clear();
  types.clear();
}

bool CategoriesHolder::ValidKeyToken(StringT const & s)
{
  if (s.size() > 2)
    return true;

  /// @todo We need to have global stop words array for the most used languages.
  char const * arr[] = { "a", "z", "s", "d", "di", "de", "le" };
  for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
    if (s.IsEqualAscii(arr[i]))
      return false;

  return true;
}

void CategoriesHolder::LoadFromStream(istream & s)
{
  m_type2cat.clear();
  m_name2type.clear();

  State state = EParseTypes;
  string line;

  Category cat;
  vector<uint32_t> types;

  Classificator const & c = classif();

  int lineNumber = 0;
  while (s.good())
  {
    ++lineNumber;
    getline(s, line);
    strings::SimpleTokenizer iter(line, state == EParseTypes ? "|" : ":|");

    switch (state)
    {
    case EParseTypes:
      {
        AddCategory(cat, types);

        while (iter)
        {
          // split category to sub categories for classificator
          vector<string> v;
          strings::Tokenize(*iter, "-", MakeBackInsertFunctor(v));

          // get classificator type
          uint32_t const type = c.GetTypeByPathSafe(v);
          if (type != 0)
            types.push_back(type);
          else
            LOG(LWARNING, ("Invalid type:", v, "at line:", lineNumber));

          ++iter;
        }

        if (!types.empty())
          state = EParseLanguages;
      }
      break;

    case EParseLanguages:
      {
        if (!iter)
        {
          state = EParseTypes;
          continue;
        }

        int8_t const langCode = MapLocaleToInteger(*iter);
        CHECK(langCode != UNSUPPORTED_LOCALE_CODE, ("Invalid language code:", *iter, "at line:", lineNumber));

        while (++iter)
        {
          Category::Name name;
          name.m_locale = langCode;
          name.m_name = *iter;

          if (name.m_name.empty())
          {
            LOG(LWARNING, ("Empty category name at line:", lineNumber));
            continue;
          }

          if (name.m_name[0] >= '0' && name.m_name[0] <= '9')
          {
            name.m_prefixLengthToSuggest = name.m_name[0] - '0';
            name.m_name = name.m_name.substr(1);
          }
          else
            name.m_prefixLengthToSuggest = Category::EMPTY_PREFIX_LENGTH;

          cat.m_synonyms.push_back(name);
        }
      }
      break;
    }
  }

  // add last category
  AddCategory(cat, types);
}

bool CategoriesHolder::GetNameByType(uint32_t type, int8_t locale, string & name) const
{
  pair<IteratorT, IteratorT> const range = m_type2cat.equal_range(type);

  for (IteratorT i = range.first; i != range.second; ++i)
  {
    Category const & cat = *i->second;
    for (size_t j = 0; j < cat.m_synonyms.size(); ++j)
      if (cat.m_synonyms[j].m_locale == locale)
      {
        name = cat.m_synonyms[j].m_name;
        return true;
      }
  }

  if (range.first != range.second)
  {
    name = range.first->second->m_synonyms[0].m_name;
    return true;
  }

  return false;
}

bool CategoriesHolder::IsTypeExist(uint32_t type) const
{
  pair<IteratorT, IteratorT> const range = m_type2cat.equal_range(type);
  return range.first != range.second;
}

namespace
{
struct Mapping
{
  char const * m_name;
  int8_t m_code;
};
} // namespace

int8_t CategoriesHolder::MapLocaleToInteger(string const & locale)
{
  static const Mapping mapping[] = {
    {"en", 1 },
    {"ru", 2 },
    {"uk", 3 },
    {"de", 4 },
    {"fr", 5 },
    {"it", 6 },
    {"es", 7 },
    {"ko", 8 },
    {"ja", 9 },
    {"cs", 10 },
    {"nl", 11 },
    {"zh-Hant", 12 },
    {"pl", 13 },
    {"pt", 14 },
    {"hu", 15 },
    {"th", 16 },
    {"zh-Hans", 17 },
    {"ar", 18 },
    {"da", 19 },
    {"tr", 20 },
    {"sk", 21 },
  };
  for (size_t i = 0; i < ARRAY_SIZE(mapping); ++i)
    if (locale.find(mapping[i].m_name) == 0)
      return mapping[i].m_code;

  // Special cases for different Chinese variations
  if (locale.find("zh") == 0)
  {
    string lower = locale;
    strings::AsciiToLower(lower);

    if (lower.find("hant") != string::npos
        || lower.find("tw") != string::npos
        || lower.find("hk") != string::npos
        || lower.find("mo") != string::npos)
      return 12; // Traditional Chinese

    return 17; // Simplified Chinese by default for all other cases
  }

  return UNSUPPORTED_LOCALE_CODE;
}
<commit_msg>[search] Do not match Wi-Fi with “wi” or “fi” token.<commit_after>#include "categories_holder.hpp"
#include "search_delimiters.hpp"
#include "search_string_utils.hpp"
#include "classificator.hpp"

#include "../coding/reader.hpp"
#include "../coding/reader_streambuf.hpp"

#include "../base/logging.hpp"
#include "../base/stl_add.hpp"

namespace
{

enum State
{
  EParseTypes,
  EParseLanguages
};

}  // unnamed namespace

int8_t const CategoriesHolder::UNSUPPORTED_LOCALE_CODE;

CategoriesHolder::CategoriesHolder(Reader * reader)
{
  ReaderStreamBuf buffer(reader);
  istream s(&buffer);
  LoadFromStream(s);
}

void CategoriesHolder::AddCategory(Category & cat, vector<uint32_t> & types)
{
  if (!cat.m_synonyms.empty() && !types.empty())
  {
    shared_ptr<Category> p(new Category());
    p->Swap(cat);

    for (size_t i = 0; i < types.size(); ++i)
      m_type2cat.insert(make_pair(types[i], p));

    for (size_t i = 0; i < p->m_synonyms.size(); ++i)
    {
      ASSERT(p->m_synonyms[i].m_locale != UNSUPPORTED_LOCALE_CODE, ());

      StringT const uniName = search::NormalizeAndSimplifyString(p->m_synonyms[i].m_name);

      vector<StringT> tokens;
      SplitUniString(uniName, MakeBackInsertFunctor(tokens), search::Delimiters());

      for (size_t j = 0; j < tokens.size(); ++j)
        for (size_t k = 0; k < types.size(); ++k)
          if (ValidKeyToken(tokens[j]))
            m_name2type.insert(make_pair(make_pair(p->m_synonyms[i].m_locale, tokens[j]), types[k]));
    }
  }

  cat.m_synonyms.clear();
  types.clear();
}

bool CategoriesHolder::ValidKeyToken(StringT const & s)
{
  if (s.size() > 2)
    return true;

  /// @todo We need to have global stop words array for the most used languages.
  char const * arr[] = { "a", "z", "s", "d", "di", "de", "le", "wi", "fi" };
  for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
    if (s.IsEqualAscii(arr[i]))
      return false;

  return true;
}

void CategoriesHolder::LoadFromStream(istream & s)
{
  m_type2cat.clear();
  m_name2type.clear();

  State state = EParseTypes;
  string line;

  Category cat;
  vector<uint32_t> types;

  Classificator const & c = classif();

  int lineNumber = 0;
  while (s.good())
  {
    ++lineNumber;
    getline(s, line);
    strings::SimpleTokenizer iter(line, state == EParseTypes ? "|" : ":|");

    switch (state)
    {
    case EParseTypes:
      {
        AddCategory(cat, types);

        while (iter)
        {
          // split category to sub categories for classificator
          vector<string> v;
          strings::Tokenize(*iter, "-", MakeBackInsertFunctor(v));

          // get classificator type
          uint32_t const type = c.GetTypeByPathSafe(v);
          if (type != 0)
            types.push_back(type);
          else
            LOG(LWARNING, ("Invalid type:", v, "at line:", lineNumber));

          ++iter;
        }

        if (!types.empty())
          state = EParseLanguages;
      }
      break;

    case EParseLanguages:
      {
        if (!iter)
        {
          state = EParseTypes;
          continue;
        }

        int8_t const langCode = MapLocaleToInteger(*iter);
        CHECK(langCode != UNSUPPORTED_LOCALE_CODE, ("Invalid language code:", *iter, "at line:", lineNumber));

        while (++iter)
        {
          Category::Name name;
          name.m_locale = langCode;
          name.m_name = *iter;

          if (name.m_name.empty())
          {
            LOG(LWARNING, ("Empty category name at line:", lineNumber));
            continue;
          }

          if (name.m_name[0] >= '0' && name.m_name[0] <= '9')
          {
            name.m_prefixLengthToSuggest = name.m_name[0] - '0';
            name.m_name = name.m_name.substr(1);
          }
          else
            name.m_prefixLengthToSuggest = Category::EMPTY_PREFIX_LENGTH;

          cat.m_synonyms.push_back(name);
        }
      }
      break;
    }
  }

  // add last category
  AddCategory(cat, types);
}

bool CategoriesHolder::GetNameByType(uint32_t type, int8_t locale, string & name) const
{
  pair<IteratorT, IteratorT> const range = m_type2cat.equal_range(type);

  for (IteratorT i = range.first; i != range.second; ++i)
  {
    Category const & cat = *i->second;
    for (size_t j = 0; j < cat.m_synonyms.size(); ++j)
      if (cat.m_synonyms[j].m_locale == locale)
      {
        name = cat.m_synonyms[j].m_name;
        return true;
      }
  }

  if (range.first != range.second)
  {
    name = range.first->second->m_synonyms[0].m_name;
    return true;
  }

  return false;
}

bool CategoriesHolder::IsTypeExist(uint32_t type) const
{
  pair<IteratorT, IteratorT> const range = m_type2cat.equal_range(type);
  return range.first != range.second;
}

namespace
{
struct Mapping
{
  char const * m_name;
  int8_t m_code;
};
} // namespace

int8_t CategoriesHolder::MapLocaleToInteger(string const & locale)
{
  static const Mapping mapping[] = {
    {"en", 1 },
    {"ru", 2 },
    {"uk", 3 },
    {"de", 4 },
    {"fr", 5 },
    {"it", 6 },
    {"es", 7 },
    {"ko", 8 },
    {"ja", 9 },
    {"cs", 10 },
    {"nl", 11 },
    {"zh-Hant", 12 },
    {"pl", 13 },
    {"pt", 14 },
    {"hu", 15 },
    {"th", 16 },
    {"zh-Hans", 17 },
    {"ar", 18 },
    {"da", 19 },
    {"tr", 20 },
    {"sk", 21 },
  };
  for (size_t i = 0; i < ARRAY_SIZE(mapping); ++i)
    if (locale.find(mapping[i].m_name) == 0)
      return mapping[i].m_code;

  // Special cases for different Chinese variations
  if (locale.find("zh") == 0)
  {
    string lower = locale;
    strings::AsciiToLower(lower);

    if (lower.find("hant") != string::npos
        || lower.find("tw") != string::npos
        || lower.find("hk") != string::npos
        || lower.find("mo") != string::npos)
      return 12; // Traditional Chinese

    return 17; // Simplified Chinese by default for all other cases
  }

  return UNSUPPORTED_LOCALE_CODE;
}
<|endoftext|>


<commit_before>#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <mixerr.h>
#include <Instrument.h>
#include "PANECHO.h"
#include <rt.h>
#include <rtdefs.h>

extern "C" {
	#include <ugens.h>
	extern int resetval;
}

PANECHO::PANECHO() : Instrument()
{
	// future setup here?
}

PANECHO::~PANECHO()
{
	delete [] delarray1;
	delete [] delarray2;
}

int PANECHO::init(float p[], short n_args)
{
// p0 = output skip; p1 = input skip; p2 = output duration
// p3 = amplitude multiplier
// p4 = chennel 0 delay time
// p5 = chennel 1 delay time
// p6 = regeneration multiplier (< 1!)
// p7 = ring-down duration
// p8 = input channel [optional]
// assumes function table 1 is the amplitude envelope

	int amplen;
	long delsamps;

	rtsetinput(p[1], this);
	nsamps = rtsetoutput(p[0], p[2]+p[7], this);
	insamps = p[2] * SR;

	if (NCHANS != 2) {
		fprintf(stderr,"output must be stereo!\n");
		exit(-1);
		}

	delsamps = p[4] * SR;
	delarray1 = new float[delsamps];
	if (!delarray1) {
		fprintf(stderr,"Sorry, Charlie -- no space\n");
		exit(-1);
	}
	wait1 = p[4];
	delset(delarray1, deltabs1, wait1);

	delsamps = p[5] * SR + 0.5;
	delarray2 = new float[delsamps];
	if (!delarray2) {
		fprintf(stderr,"Sorry, Charlie -- no space\n");
		exit(-1);
	}
	wait2 = p[5];
	delset(delarray2, deltabs2, wait2);

	regen = p[5];

	amptable = floc(1);
	amplen = fsize(1);
	tableset(p[2], amplen, amptabs);

	amp = p[3];
	skip = SR/(float)resetval;
	inchan = p[8];
	if ((inchan+1) > inputchans) {
		fprintf(stderr,"uh oh, you have asked for channel %d of a %d-channel file...\n",inchan,inputchans);
		exit(-1);
		}

	return(nsamps);
}

int PANECHO::run()
{
	int i,rsamps;
	float in[2*MAXBUF],out[2];
	float aamp;
	int branch;

	rsamps = chunksamps*inputchans;

	rtgetin(in, this, rsamps);

	branch = 0;
	for (i = 0; i < rsamps; i += inputchans)  {
		if (cursamp > insamps) {
			out[0] = delget(delarray2, wait2, deltabs2) * regen;
			out[1] = delget(delarray1, wait1, deltabs1);
			}
		else {
			if (--branch < 0) {
				aamp = tablei(cursamp, amptable, amptabs) * amp;
				branch = skip;
				}
			out[0] = (in[i+inchan]*aamp) + (delget(delarray2, wait2, deltabs2)*regen);
			out[1] = delget(delarray1, wait1, deltabs1);
			}

		delput(out[0], delarray1, deltabs1);
		delput(out[1], delarray2, deltabs2);

		rtaddout(out);
		cursamp++;
		}
	return(i);
}

Instrument*
makePANECHO()
{
	PANECHO *inst;

	inst = new PANECHO();
	return inst;
}

void
rtprofile()
{
	RT_INTRO("PANECHO",makePANECHO);
}

<commit_msg>Left chan delay line didn't have the + .5 fix.<commit_after>#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <mixerr.h>
#include <Instrument.h>
#include "PANECHO.h"
#include <rt.h>
#include <rtdefs.h>

extern "C" {
	#include <ugens.h>
	extern int resetval;
}

PANECHO::PANECHO() : Instrument()
{
	// future setup here?
}

PANECHO::~PANECHO()
{
	delete [] delarray1;
	delete [] delarray2;
}

int PANECHO::init(float p[], short n_args)
{
// p0 = output skip; p1 = input skip; p2 = output duration
// p3 = amplitude multiplier
// p4 = chennel 0 delay time
// p5 = chennel 1 delay time
// p6 = regeneration multiplier (< 1!)
// p7 = ring-down duration
// p8 = input channel [optional]
// assumes function table 1 is the amplitude envelope

	int amplen;
	long delsamps;

	rtsetinput(p[1], this);
	nsamps = rtsetoutput(p[0], p[2]+p[7], this);
	insamps = p[2] * SR;

	if (NCHANS != 2) {
		fprintf(stderr,"output must be stereo!\n");
		exit(-1);
		}

	delsamps = p[4] * SR + 0.5;
	delarray1 = new float[delsamps];
	if (!delarray1) {
		fprintf(stderr,"Sorry, Charlie -- no space\n");
		exit(-1);
	}
	wait1 = p[4];
	delset(delarray1, deltabs1, wait1);

	delsamps = p[5] * SR + 0.5;
	delarray2 = new float[delsamps];
	if (!delarray2) {
		fprintf(stderr,"Sorry, Charlie -- no space\n");
		exit(-1);
	}
	wait2 = p[5];
	delset(delarray2, deltabs2, wait2);

	regen = p[5];

	amptable = floc(1);
	amplen = fsize(1);
	tableset(p[2], amplen, amptabs);

	amp = p[3];
	skip = SR/(float)resetval;
	inchan = p[8];
	if ((inchan+1) > inputchans) {
		fprintf(stderr,"uh oh, you have asked for channel %d of a %d-channel file...\n",inchan,inputchans);
		exit(-1);
		}

	return(nsamps);
}

int PANECHO::run()
{
	int i,rsamps;
	float in[2*MAXBUF],out[2];
	float aamp;
	int branch;

	rsamps = chunksamps*inputchans;

	rtgetin(in, this, rsamps);

	branch = 0;
	for (i = 0; i < rsamps; i += inputchans)  {
		if (cursamp > insamps) {
			out[0] = delget(delarray2, wait2, deltabs2) * regen;
			out[1] = delget(delarray1, wait1, deltabs1);
			}
		else {
			if (--branch < 0) {
				aamp = tablei(cursamp, amptable, amptabs) * amp;
				branch = skip;
				}
			out[0] = (in[i+inchan]*aamp) + (delget(delarray2, wait2, deltabs2)*regen);
			out[1] = delget(delarray1, wait1, deltabs1);
			}

		delput(out[0], delarray1, deltabs1);
		delput(out[1], delarray2, deltabs2);

		rtaddout(out);
		cursamp++;
		}
	return(i);
}

Instrument*
makePANECHO()
{
	PANECHO *inst;

	inst = new PANECHO();
	return inst;
}

void
rtprofile()
{
	RT_INTRO("PANECHO",makePANECHO);
}

<|endoftext|>


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

 This file is part of the GLC-lib library.
 Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
 Version 1.1.0, packaged on March, 2009.

 http://glc-lib.sourceforge.net

 GLC-lib is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 GLC-lib is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with GLC-lib; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*****************************************************************************/

//! \file glc_mesh2.cpp implementation of the GLC_Mesh2 class.

#include "glc_mesh2.h"
#include "glc_openglexception.h"
#include "glc_selectionmaterial.h"
#include "glc_state.h"

#include <QtDebug>

//////////////////////////////////////////////////////////////////////
// Constructor Destructor
//////////////////////////////////////////////////////////////////////
GLC_Mesh2::GLC_Mesh2()
:GLC_VboGeom("Mesh", false)
, m_Vertex()
, m_MaterialGroup()
, m_NumberOfFaces(0)
, m_IsSelected(false)
, m_ColorPearVertex(false)
{
	//qDebug() << "GLC_Mesh2::GLC_Mesh2" << id();
	// Index group with default material
	IndexList* pIndexList= new IndexList;
	m_MaterialGroup.insert(0, pIndexList);
}

GLC_Mesh2::GLC_Mesh2(const GLC_Mesh2 &meshToCopy)
: GLC_VboGeom(meshToCopy)
, m_Vertex(meshToCopy.m_Vertex)
, m_MaterialGroup()
, m_NumberOfFaces(meshToCopy.m_NumberOfFaces)
, m_IsSelected(false)
, m_ColorPearVertex(meshToCopy.m_ColorPearVertex)
{
	// Copy Vertex and index data if necessary
	if (m_VertexVector.isEmpty())
	{
		m_VertexVector= meshToCopy.getVertexVector();
		m_IndexVector= meshToCopy.getIndexVector();
	}
	//qDebug() << "GLC_Mesh2::GLC_Mesh2" << id();

	// Copy Material group IBO
	MaterialGroupHash::const_iterator j= meshToCopy.m_MaterialGroup.begin();
    while (j != meshToCopy.m_MaterialGroup.constEnd())
    {
    	IndexList* pIndexList= new IndexList(*j.value());
    	m_MaterialGroup.insert(j.key(), pIndexList);
        ++j;
    }
}

GLC_Mesh2::~GLC_Mesh2(void)
{
    // delete mesh inner index material group
	{
		MaterialGroupHash::const_iterator i= m_MaterialGroup.begin();
	    while (i != m_MaterialGroup.constEnd())
	    {
	    	// Delete index vector
	        delete i.value();
	        ++i;
	    }
	}

	m_Vertex.clear();
	m_MaterialGroup.clear();
}
/////////////////////////////////////////////////////////////////////
// Get Functions
//////////////////////////////////////////////////////////////////////

// return the mesh bounding box
GLC_BoundingBox& GLC_Mesh2::boundingBox(void)
{

	if (NULL == m_pBoundingBox)
	{
		//qDebug() << "GLC_Mesh2::boundingBox create boundingBox";
		m_pBoundingBox= new GLC_BoundingBox();

		if (m_VertexVector.isEmpty())
		{
			qDebug() << "GLC_Mesh2::getBoundingBox empty m_VertexVector";
		}
		else
		{
			const int max= m_VertexVector.size();
			for (int i= 0; i < max; ++i)
			{
				GLC_Vector3d vector(m_VertexVector[i].x, m_VertexVector[i].y, m_VertexVector[i].z);
				m_pBoundingBox->combine(vector);
			}
		}

	}
	return *m_pBoundingBox;
}

// Return a copy of the current geometry
GLC_VboGeom* GLC_Mesh2::clone() const
{
	return new GLC_Mesh2(*this);
}

// Return the Vertex Vector
VertexVector GLC_Mesh2::getVertexVector() const
{

	if (0 != m_VboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= numberOfVertex();
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLC_Vertex);
		VertexVector vertexVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_VboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(vertexVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return vertexVector;
	}
	else
	{
		return m_VertexVector;
	}
}

// Return the Index Vector
QVector<GLuint> GLC_Mesh2::getIndexVector() const
{
	if (0 != m_IboId)
	{
		// IBO created get data from IBO
		const int sizeOfVbo= numberOfVertex();
		const GLsizeiptr indexSize = sizeOfVbo * sizeof(GLuint);
		QVector<GLuint> indexVector(sizeOfVbo);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IboId);
		GLvoid* pIbo = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(indexVector.data(), pIbo, indexSize);
		glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
		return indexVector;
	}
	else
	{
		return m_IndexVector;
	}

}

/////////////////////////////////////////////////////////////////////
// Set Functions
//////////////////////////////////////////////////////////////////////

// Add triangles with the same material to the mesh
void GLC_Mesh2::addTriangles(const VertexList &triangles, GLC_Material* pMaterial)
{
	// test if the material is already in the mesh
	GLC_uint materialID;
	if (NULL != pMaterial)
	{
		materialID= pMaterial->id();
	}
	else
	{
		materialID= 0;
	}
	IndexList* pCurIndexList= NULL;
	if ((materialID == 0) or m_MaterialHash.contains(materialID))
	{
		pCurIndexList= m_MaterialGroup.value(materialID);
	}
	else
	{
		// Test if the mesh have already an equivalent material
		GLC_uint index=  materialIndex(*pMaterial);
		if (0 == index)
		{
			addMaterial(pMaterial);
			pCurIndexList= new IndexList;
			m_MaterialGroup.insert(materialID, pCurIndexList);
		}
		else
		{
			pCurIndexList= m_MaterialGroup.value(index);
		}

	}
	const int startVertexIndex= m_Vertex.size();
	const int delta= triangles.size();
	// Add triangles vertex to the mesh
	m_Vertex+= triangles;

	for (int i= 0; i < delta; ++i)
	{
		pCurIndexList->append(startVertexIndex + static_cast<GLuint>(i));
	}
	// Invalid the geometry
	m_GeometryIsValid = false;
	m_NumberOfFaces+= delta / 3;
}

// Reverse mesh normal
void GLC_Mesh2::reverseNormal()
{
	// Copy Vertex and index data if necessary
	if (m_VertexVector.isEmpty())
	{
		m_VertexVector= getVertexVector();
		m_IndexVector= getIndexVector();
	}

	const int max= m_VertexVector.size();
	for (int i= 0; i < max; ++i)
	{
		m_VertexVector[i].nx= m_VertexVector[i].nx * -1.0f;
		m_VertexVector[i].ny= m_VertexVector[i].ny * -1.0f;
		m_VertexVector[i].nz= m_VertexVector[i].nz * -1.0f;
	}
	// Invalid the geometry
	m_GeometryIsValid = false;
}

// Specific glExecute method
void GLC_Mesh2::glExecute(bool isSelected, bool transparent)
{
	m_IsSelected= isSelected;
	GLC_VboGeom::glExecute(isSelected, transparent);
	m_IsSelected= false;
}

//////////////////////////////////////////////////////////////////////
// OpenGL Functions
//////////////////////////////////////////////////////////////////////

// Virtual interface for OpenGL Geometry set up.
void GLC_Mesh2::glDraw(bool transparent)
{
	Q_ASSERT(m_GeometryIsValid or not m_VertexVector.isEmpty());

	const bool vboIsUsed= GLC_State::vboUsed();

	IndexList iboList;
	MaterialGroupHash::iterator iMaterialGroup;

	// Create VBO and IBO
	if (!m_GeometryIsValid)
	{
		// Fill index
		iMaterialGroup= m_MaterialGroup.begin();
	    while (iMaterialGroup != m_MaterialGroup.constEnd())
	    {
	    	if (!iMaterialGroup.value()->isEmpty())
	    	{
	    		iboList+= *(iMaterialGroup.value());
	    	}
	    	++iMaterialGroup;
	    }
		// Set index vector
		m_IndexVector= iboList.toVector();

		if (vboIsUsed)
		{
			// Create VBO
			const GLsizei dataNbr= static_cast<GLsizei>(m_VertexVector.size());
			const GLsizeiptr dataSize= dataNbr * sizeof(GLC_Vertex);
			glBufferData(GL_ARRAY_BUFFER, dataSize, m_VertexVector.data(), GL_STATIC_DRAW);
			m_VertexVector.clear();
			// Create IBO
			const GLsizei indexNbr= static_cast<GLsizei>(iboList.size());
			const GLsizeiptr indexSize = indexNbr * sizeof(GLuint);
			glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize, m_IndexVector.data(), GL_STATIC_DRAW);
			m_IndexVector.clear();
		}
	}

	if (vboIsUsed)
	{
		// Use VBO
		glVertexPointer(3, GL_FLOAT, sizeof(GLC_Vertex), BUFFER_OFFSET(0));
		glNormalPointer(GL_FLOAT, sizeof(GLC_Vertex), BUFFER_OFFSET(12));
		glTexCoordPointer(2, GL_FLOAT, sizeof(GLC_Vertex), BUFFER_OFFSET(24));

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);

		// test if color pear vertex is activated
		if (m_ColorPearVertex and not m_IsSelected and not GLC_State::isInSelectionMode())
		{
			glEnable(GL_COLOR_MATERIAL);
			glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
			glEnableClientState(GL_COLOR_ARRAY);
			glColorPointer(4, GL_FLOAT, sizeof(GLC_Vertex), BUFFER_OFFSET(32));
		}
	}
	else
	{
		// Use Vertex Array
		float* pVertexData= (float *) m_VertexVector.data();
		glVertexPointer(3, GL_FLOAT, sizeof(GLC_Vertex), pVertexData);
		glNormalPointer(GL_FLOAT, sizeof(GLC_Vertex), &pVertexData[3]);
		glTexCoordPointer(2, GL_FLOAT, sizeof(GLC_Vertex), &pVertexData[6]);

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);

		// test if color pear vertex is activated
		if (m_ColorPearVertex and not m_IsSelected and not GLC_State::isInSelectionMode())
		{
			glEnable(GL_COLOR_MATERIAL);
			glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
			glEnableClientState(GL_COLOR_ARRAY);
			glColorPointer(4, GL_FLOAT, sizeof(GLC_Vertex), &pVertexData[8]);
		}
	}

	GLC_Material* pCurrentMaterial= NULL;
	GLuint max;
	GLuint cur= 0;
	iMaterialGroup= m_MaterialGroup.begin();
    while (iMaterialGroup != m_MaterialGroup.constEnd())
    {
    	if (not iMaterialGroup.value()->isEmpty())
    	{
    		if ((not GLC_State::selectionShaderUsed() or not m_IsSelected) and not GLC_State::isInSelectionMode())
    		{
        		// Set current material
        		if (iMaterialGroup.key() == 0)
        		{
        			// Use default material
        			pCurrentMaterial= firstMaterial();
        		}
        		else
        		{
        			pCurrentMaterial= m_MaterialHash.value(iMaterialGroup.key());
        			Q_ASSERT(pCurrentMaterial != NULL);
        		}

        		if (pCurrentMaterial->isTransparent() == transparent)
        		{
            		// Execute current material
        			if (pCurrentMaterial->getAddRgbaTexture())
        			{
        				glEnable(GL_TEXTURE_2D);
        			}
        			else
        			{
        				glDisable(GL_TEXTURE_2D);
        			}
        			// Activate material
        			pCurrentMaterial->glExecute();
        			const GLfloat red= pCurrentMaterial->getDiffuseColor().redF();
        			const GLfloat green= pCurrentMaterial->getDiffuseColor().greenF();
        			const GLfloat blue= pCurrentMaterial->getDiffuseColor().blueF();
        			const GLfloat alpha= pCurrentMaterial->getDiffuseColor().alphaF();

        			glColor4f(red, green, blue, alpha);
        			if (m_IsSelected) GLC_SelectionMaterial::glExecute();
        		}
    		}
    		else if(not GLC_State::isInSelectionMode())
    		{
    			// Use Shader
        		glDisable(GL_TEXTURE_2D);
    		}

			max= static_cast<GLuint>(iMaterialGroup.value()->size());
    		if (m_IsSelected or GLC_State::isInSelectionMode() or (pCurrentMaterial->isTransparent() == transparent))
    		{
    			// Draw Mesh
    			if (vboIsUsed)
    			{
    				// Use VBO
    				//glDrawRangeElements(GL_TRIANGLES, 0, max, max, GL_UNSIGNED_INT, BUFFER_OFFSET((cur) * sizeof(unsigned int)));
    				glDrawElements(GL_TRIANGLES, max, GL_UNSIGNED_INT, BUFFER_OFFSET((cur) * sizeof(unsigned int)));
    			}
    			else
    			{
    				// Use Vertex Array
    				glDrawElements(GL_TRIANGLES, max, GL_UNSIGNED_INT, &m_IndexVector.data()[cur]);
    			}
    		}

			cur+= max;
    	}
    	++iMaterialGroup;
    }
	if (m_ColorPearVertex and not m_IsSelected and not GLC_State::isInSelectionMode())
	{
		glDisableClientState(GL_COLOR_ARRAY);
		glDisable(GL_COLOR_MATERIAL);
	}

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	// OpenGL error handler
	GLenum error= glGetError();
	if (error != GL_NO_ERROR)
	{
		GLC_OpenGlException OpenGlException("GLC_Mesh2::GlDraw ", error);
		throw(OpenGlException);
	}

}
<commit_msg>Bug Fix in copy constructor. Copy the inner material.<commit_after>/****************************************************************************

 This file is part of the GLC-lib library.
 Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
 Version 1.1.0, packaged on March, 2009.

 http://glc-lib.sourceforge.net

 GLC-lib is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 GLC-lib is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with GLC-lib; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*****************************************************************************/

//! \file glc_mesh2.cpp implementation of the GLC_Mesh2 class.

#include "glc_mesh2.h"
#include "glc_openglexception.h"
#include "glc_selectionmaterial.h"
#include "glc_state.h"

#include <QtDebug>

//////////////////////////////////////////////////////////////////////
// Constructor Destructor
//////////////////////////////////////////////////////////////////////
GLC_Mesh2::GLC_Mesh2()
:GLC_VboGeom("Mesh", false)
, m_Vertex()
, m_MaterialGroup()
, m_NumberOfFaces(0)
, m_IsSelected(false)
, m_ColorPearVertex(false)
{
	//qDebug() << "GLC_Mesh2::GLC_Mesh2" << id();
	// Index group with default material
	IndexList* pIndexList= new IndexList;
	m_MaterialGroup.insert(0, pIndexList);
}

GLC_Mesh2::GLC_Mesh2(const GLC_Mesh2 &meshToCopy)
: GLC_VboGeom(meshToCopy)
, m_Vertex(meshToCopy.m_Vertex)
, m_MaterialGroup()
, m_NumberOfFaces(meshToCopy.m_NumberOfFaces)
, m_IsSelected(false)
, m_ColorPearVertex(meshToCopy.m_ColorPearVertex)
{
	// Copy Vertex and index data if necessary
	if (m_VertexVector.isEmpty())
	{
		m_VertexVector= meshToCopy.getVertexVector();
		m_IndexVector= meshToCopy.getIndexVector();
	}
	//qDebug() << "GLC_Mesh2::GLC_Mesh2" << id();

	// Copy inner material hash
	MaterialHash::const_iterator i= m_MaterialHash.begin();
	MaterialHash newMaterialHash;
	QHash<GLC_uint, GLC_uint> materialMap;
    while (i != m_MaterialHash.constEnd())
    {
        // update inner material use table
    	i.value()->delGLC_Geom(id());
    	GLC_Material* pNewMaterial= new GLC_Material(*(i.value()));
    	newMaterialHash.insert(pNewMaterial->id(), pNewMaterial);
    	pNewMaterial->addGLC_Geom(this);
    	materialMap.insert(i.key(), pNewMaterial->id());
        ++i;
    }
    m_MaterialHash= newMaterialHash;

	// Copy Material group IBO
	MaterialGroupHash::const_iterator j= meshToCopy.m_MaterialGroup.begin();
    while (j != meshToCopy.m_MaterialGroup.constEnd())
    {
    	IndexList* pIndexList= new IndexList(*j.value());
    	m_MaterialGroup.insert(materialMap.value(j.key()), pIndexList);
        ++j;
    }
}

GLC_Mesh2::~GLC_Mesh2(void)
{
    // delete mesh inner index material group
	{
		MaterialGroupHash::const_iterator i= m_MaterialGroup.begin();
	    while (i != m_MaterialGroup.constEnd())
	    {
	    	// Delete index vector
	        delete i.value();
	        ++i;
	    }
	}

	m_Vertex.clear();
	m_MaterialGroup.clear();
}
/////////////////////////////////////////////////////////////////////
// Get Functions
//////////////////////////////////////////////////////////////////////

// return the mesh bounding box
GLC_BoundingBox& GLC_Mesh2::boundingBox(void)
{

	if (NULL == m_pBoundingBox)
	{
		//qDebug() << "GLC_Mesh2::boundingBox create boundingBox";
		m_pBoundingBox= new GLC_BoundingBox();

		if (m_VertexVector.isEmpty())
		{
			qDebug() << "GLC_Mesh2::getBoundingBox empty m_VertexVector";
		}
		else
		{
			const int max= m_VertexVector.size();
			for (int i= 0; i < max; ++i)
			{
				GLC_Vector3d vector(m_VertexVector[i].x, m_VertexVector[i].y, m_VertexVector[i].z);
				m_pBoundingBox->combine(vector);
			}
		}

	}
	return *m_pBoundingBox;
}

// Return a copy of the current geometry
GLC_VboGeom* GLC_Mesh2::clone() const
{
	return new GLC_Mesh2(*this);
}

// Return the Vertex Vector
VertexVector GLC_Mesh2::getVertexVector() const
{

	if (0 != m_VboId)
	{
		// VBO created get data from VBO
		const int sizeOfVbo= numberOfVertex();
		const GLsizeiptr dataSize= sizeOfVbo * sizeof(GLC_Vertex);
		VertexVector vertexVector(sizeOfVbo);

		glBindBuffer(GL_ARRAY_BUFFER, m_VboId);
		GLvoid* pVbo = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(vertexVector.data(), pVbo, dataSize);
		glUnmapBuffer(GL_ARRAY_BUFFER);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		return vertexVector;
	}
	else
	{
		return m_VertexVector;
	}
}

// Return the Index Vector
QVector<GLuint> GLC_Mesh2::getIndexVector() const
{
	if (0 != m_IboId)
	{
		// IBO created get data from IBO
		const int sizeOfVbo= numberOfVertex();
		const GLsizeiptr indexSize = sizeOfVbo * sizeof(GLuint);
		QVector<GLuint> indexVector(sizeOfVbo);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IboId);
		GLvoid* pIbo = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
		memcpy(indexVector.data(), pIbo, indexSize);
		glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
		return indexVector;
	}
	else
	{
		return m_IndexVector;
	}

}

/////////////////////////////////////////////////////////////////////
// Set Functions
//////////////////////////////////////////////////////////////////////

// Add triangles with the same material to the mesh
void GLC_Mesh2::addTriangles(const VertexList &triangles, GLC_Material* pMaterial)
{
	// test if the material is already in the mesh
	GLC_uint materialID;
	if (NULL != pMaterial)
	{
		materialID= pMaterial->id();
	}
	else
	{
		materialID= 0;
	}
	IndexList* pCurIndexList= NULL;
	if ((materialID == 0) or m_MaterialHash.contains(materialID))
	{
		pCurIndexList= m_MaterialGroup.value(materialID);
	}
	else
	{
		// Test if the mesh have already an equivalent material
		GLC_uint index=  materialIndex(*pMaterial);
		if (0 == index)
		{
			addMaterial(pMaterial);
			pCurIndexList= new IndexList;
			m_MaterialGroup.insert(materialID, pCurIndexList);
		}
		else
		{
			pCurIndexList= m_MaterialGroup.value(index);
		}

	}
	const int startVertexIndex= m_Vertex.size();
	const int delta= triangles.size();
	// Add triangles vertex to the mesh
	m_Vertex+= triangles;

	for (int i= 0; i < delta; ++i)
	{
		pCurIndexList->append(startVertexIndex + static_cast<GLuint>(i));
	}
	// Invalid the geometry
	m_GeometryIsValid = false;
	m_NumberOfFaces+= delta / 3;
}

// Reverse mesh normal
void GLC_Mesh2::reverseNormal()
{
	// Copy Vertex and index data if necessary
	if (m_VertexVector.isEmpty())
	{
		m_VertexVector= getVertexVector();
		m_IndexVector= getIndexVector();
	}

	const int max= m_VertexVector.size();
	for (int i= 0; i < max; ++i)
	{
		m_VertexVector[i].nx= m_VertexVector[i].nx * -1.0f;
		m_VertexVector[i].ny= m_VertexVector[i].ny * -1.0f;
		m_VertexVector[i].nz= m_VertexVector[i].nz * -1.0f;
	}
	// Invalid the geometry
	m_GeometryIsValid = false;
}

// Specific glExecute method
void GLC_Mesh2::glExecute(bool isSelected, bool transparent)
{
	m_IsSelected= isSelected;
	GLC_VboGeom::glExecute(isSelected, transparent);
	m_IsSelected= false;
}

//////////////////////////////////////////////////////////////////////
// OpenGL Functions
//////////////////////////////////////////////////////////////////////

// Virtual interface for OpenGL Geometry set up.
void GLC_Mesh2::glDraw(bool transparent)
{
	Q_ASSERT(m_GeometryIsValid or not m_VertexVector.isEmpty());

	const bool vboIsUsed= GLC_State::vboUsed();

	IndexList iboList;
	MaterialGroupHash::iterator iMaterialGroup;

	// Create VBO and IBO
	if (!m_GeometryIsValid)
	{
		// Fill index
		iMaterialGroup= m_MaterialGroup.begin();
	    while (iMaterialGroup != m_MaterialGroup.constEnd())
	    {
	    	if (!iMaterialGroup.value()->isEmpty())
	    	{
	    		iboList+= *(iMaterialGroup.value());
	    	}
	    	++iMaterialGroup;
	    }
		// Set index vector
		m_IndexVector= iboList.toVector();

		if (vboIsUsed)
		{
			// Create VBO
			const GLsizei dataNbr= static_cast<GLsizei>(m_VertexVector.size());
			const GLsizeiptr dataSize= dataNbr * sizeof(GLC_Vertex);
			glBufferData(GL_ARRAY_BUFFER, dataSize, m_VertexVector.data(), GL_STATIC_DRAW);
			m_VertexVector.clear();
			// Create IBO
			const GLsizei indexNbr= static_cast<GLsizei>(iboList.size());
			const GLsizeiptr indexSize = indexNbr * sizeof(GLuint);
			glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexSize, m_IndexVector.data(), GL_STATIC_DRAW);
			m_IndexVector.clear();
		}
	}

	if (vboIsUsed)
	{
		// Use VBO
		glVertexPointer(3, GL_FLOAT, sizeof(GLC_Vertex), BUFFER_OFFSET(0));
		glNormalPointer(GL_FLOAT, sizeof(GLC_Vertex), BUFFER_OFFSET(12));
		glTexCoordPointer(2, GL_FLOAT, sizeof(GLC_Vertex), BUFFER_OFFSET(24));

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);

		// test if color pear vertex is activated
		if (m_ColorPearVertex and not m_IsSelected and not GLC_State::isInSelectionMode())
		{
			glEnable(GL_COLOR_MATERIAL);
			glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
			glEnableClientState(GL_COLOR_ARRAY);
			glColorPointer(4, GL_FLOAT, sizeof(GLC_Vertex), BUFFER_OFFSET(32));
		}
	}
	else
	{
		// Use Vertex Array
		float* pVertexData= (float *) m_VertexVector.data();
		glVertexPointer(3, GL_FLOAT, sizeof(GLC_Vertex), pVertexData);
		glNormalPointer(GL_FLOAT, sizeof(GLC_Vertex), &pVertexData[3]);
		glTexCoordPointer(2, GL_FLOAT, sizeof(GLC_Vertex), &pVertexData[6]);

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);

		// test if color pear vertex is activated
		if (m_ColorPearVertex and not m_IsSelected and not GLC_State::isInSelectionMode())
		{
			glEnable(GL_COLOR_MATERIAL);
			glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
			glEnableClientState(GL_COLOR_ARRAY);
			glColorPointer(4, GL_FLOAT, sizeof(GLC_Vertex), &pVertexData[8]);
		}
	}

	GLC_Material* pCurrentMaterial= NULL;
	GLuint max;
	GLuint cur= 0;
	iMaterialGroup= m_MaterialGroup.begin();
    while (iMaterialGroup != m_MaterialGroup.constEnd())
    {
    	if (not iMaterialGroup.value()->isEmpty())
    	{
    		if ((not GLC_State::selectionShaderUsed() or not m_IsSelected) and not GLC_State::isInSelectionMode())
    		{
        		// Set current material
        		if (iMaterialGroup.key() == 0)
        		{
        			// Use default material
        			pCurrentMaterial= firstMaterial();
        		}
        		else
        		{
        			pCurrentMaterial= m_MaterialHash.value(iMaterialGroup.key());
        			Q_ASSERT(pCurrentMaterial != NULL);
        		}

        		if (pCurrentMaterial->isTransparent() == transparent)
        		{
            		// Execute current material
        			if (pCurrentMaterial->getAddRgbaTexture())
        			{
        				glEnable(GL_TEXTURE_2D);
        			}
        			else
        			{
        				glDisable(GL_TEXTURE_2D);
        			}
        			// Activate material
        			pCurrentMaterial->glExecute();
        			const GLfloat red= pCurrentMaterial->getDiffuseColor().redF();
        			const GLfloat green= pCurrentMaterial->getDiffuseColor().greenF();
        			const GLfloat blue= pCurrentMaterial->getDiffuseColor().blueF();
        			const GLfloat alpha= pCurrentMaterial->getDiffuseColor().alphaF();

        			glColor4f(red, green, blue, alpha);
        			if (m_IsSelected) GLC_SelectionMaterial::glExecute();
        		}
    		}
    		else if(not GLC_State::isInSelectionMode())
    		{
    			// Use Shader
        		glDisable(GL_TEXTURE_2D);
    		}

			max= static_cast<GLuint>(iMaterialGroup.value()->size());
    		if (m_IsSelected or GLC_State::isInSelectionMode() or (pCurrentMaterial->isTransparent() == transparent))
    		{
    			// Draw Mesh
    			if (vboIsUsed)
    			{
    				// Use VBO
    				//glDrawRangeElements(GL_TRIANGLES, 0, max, max, GL_UNSIGNED_INT, BUFFER_OFFSET((cur) * sizeof(unsigned int)));
    				glDrawElements(GL_TRIANGLES, max, GL_UNSIGNED_INT, BUFFER_OFFSET((cur) * sizeof(unsigned int)));
    			}
    			else
    			{
    				// Use Vertex Array
    				glDrawElements(GL_TRIANGLES, max, GL_UNSIGNED_INT, &m_IndexVector.data()[cur]);
    			}
    		}

			cur+= max;
    	}
    	++iMaterialGroup;
    }
	if (m_ColorPearVertex and not m_IsSelected and not GLC_State::isInSelectionMode())
	{
		glDisableClientState(GL_COLOR_ARRAY);
		glDisable(GL_COLOR_MATERIAL);
	}

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	// OpenGL error handler
	GLenum error= glGetError();
	if (error != GL_NO_ERROR)
	{
		GLC_OpenGlException OpenGlException("GLC_Mesh2::GlDraw ", error);
		throw(OpenGlException);
	}

}
<|endoftext|>


<commit_before>/*
  Q Light Controller Plus
  artnetplugin.cpp

  Copyright (c) Massimo Callegari

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include "artnetplugin.h"
#include "configureartnet.h"

#include <QSettings>
#include <QDebug>

ArtNetPlugin::~ArtNetPlugin()
{
}

void ArtNetPlugin::init()
{
    m_IOmapping.clear();

    foreach(QNetworkInterface interface, QNetworkInterface::allInterfaces())
    {
        foreach (QNetworkAddressEntry entry, interface.addressEntries())
        {
            QHostAddress addr = entry.ip();
            if (addr.protocol() != QAbstractSocket::IPv6Protocol)
            {
                ArtNetIO tmpIO;
                tmpIO.IPAddress = entry.ip().toString();
                if (addr == QHostAddress::LocalHost)
                    tmpIO.MACAddress = "11:22:33:44:55:66";
                else
                    tmpIO.MACAddress = interface.hardwareAddress();
                tmpIO.controller = NULL;
                m_IOmapping.append(tmpIO);

                m_netInterfaces.append(entry);
            }
        }
    }
}

QString ArtNetPlugin::name()
{
    return QString("ArtNet");
}

int ArtNetPlugin::capabilities() const
{
    return QLCIOPlugin::Output | QLCIOPlugin::Input | QLCIOPlugin::Infinite;
}

QString ArtNetPlugin::pluginInfo()
{
    QString str;

    str += QString("");
    str += QString("");
    str += QString("<TITLE>%1</TITLE>").arg(name());
    str += QString("");
    str += QString("");

    str += QString("
");
    str += QString("
%1
").arg(name());
    str += tr("This plugin provides DMX output for devices supporting the ArtNet communication protocol.");
    str += QString("
");

    return str;
}

/*********************************************************************
 * Outputs
 *********************************************************************/
QStringList ArtNetPlugin::outputs()
{
    QStringList list;
    int j = 0;
    if (m_IOmapping.count() == 1)
        init();
    foreach (ArtNetIO line, m_IOmapping)
    {
        list << QString(tr("%1: %2")).arg(j + 1).arg(line.IPAddress);
        j++;
    }
    return list;
}

QString ArtNetPlugin::outputInfo(quint32 output)
{
    if (m_IOmapping.count() == 1)
        init();

    if (output >= (quint32)m_IOmapping.length())
        return QString();

    QString str;

    str += QString("
%1 %2
").arg(tr("Output")).arg(outputs()[output]);
    str += QString("
");
    ArtNetController *ctrl = m_IOmapping.at(output).controller;
    if (ctrl == NULL || ctrl->type() == ArtNetController::Input)
        str += tr("Status: Not open");
    else
    {
        str += tr("Status: Open");
        str += QString("
");
        str += tr("Nodes discovered: ");
        str += QString("%1").arg(ctrl->getNodesList().size());
        str += QString("
");
        str += tr("Packets sent: ");
        str += QString("%1").arg(ctrl->getPacketSentNumber());
    }
    str += QString("
");
    str += QString("");
    str += QString("");

    return str;
}

bool ArtNetPlugin::openOutput(quint32 output)
{
    if (m_IOmapping.count() == 1)
        init();

    if (output >= (quint32)m_IOmapping.length())
        return false;

    qDebug() << "Open output with address :" << m_IOmapping.at(output).IPAddress;

    // already open ? Just add the type flag
    if (m_IOmapping[output].controller != NULL)
    {
        m_IOmapping[output].controller->setType(
                    (ArtNetController::Type)(m_IOmapping[output].controller->type() | ArtNetController::Output));
        m_IOmapping[output].controller->changeReferenceCount(ArtNetController::Output, +1);
        return true;
    }

    // not open ? Create a new ArtNetController
    ArtNetController *controller = new ArtNetController(m_IOmapping.at(output).IPAddress,
                                                        m_netInterfaces, m_IOmapping.at(output).MACAddress,
                                                        ArtNetController::Output, output, this);
    m_IOmapping[output].controller = controller;
    return true;
}

void ArtNetPlugin::closeOutput(quint32 output)
{
    if (output >= (quint32)m_IOmapping.length())
        return;
    ArtNetController *controller = m_IOmapping.at(output).controller;
    if (controller != NULL)
    {
        controller->changeReferenceCount(ArtNetController::Output, -1);
        // if a ArtNetController is also open as input
        // then just remove the output capability
        if (controller->type() & ArtNetController::Input)
        {
            controller->setType(ArtNetController::Input);
        }

        if (controller->referenceCount(ArtNetController::Input) == 0 &&
            controller->referenceCount(ArtNetController::Output) == 0)
        {
            delete m_IOmapping[output].controller;
            m_IOmapping[output].controller = NULL;
        }
    }
}

void ArtNetPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data)
{
    if (output >= (quint32)m_IOmapping.count())
        return;

    ArtNetController *controller = m_IOmapping[output].controller;
    if (controller != NULL)
        controller->sendDmx(universe, data);
}

/*************************************************************************
  * Inputs
  *************************************************************************/
QStringList ArtNetPlugin::inputs()
{
    QStringList list;
    int j = 0;
    if (m_IOmapping.count() == 1)
        init();
    foreach (ArtNetIO line, m_IOmapping)
    {
        list << QString(tr("%1: %2")).arg(j + 1).arg(line.IPAddress);
        j++;
    }
    return list;
}

bool ArtNetPlugin::openInput(quint32 input)
{
    if (m_IOmapping.count() == 1)
        init();

    if (input >= (quint32)m_IOmapping.length())
        return false;

    qDebug() << "Open input with address :" << m_IOmapping.at(input).IPAddress;

    // already open ? Just add the type flag
    if (m_IOmapping[input].controller != NULL)
    {
        m_IOmapping[input].controller->setType(
                    (ArtNetController::Type)(m_IOmapping[input].controller->type() | ArtNetController::Input));
        m_IOmapping[input].controller->changeReferenceCount(ArtNetController::Input, +1);
        return true;
    }

    // not open ? Create a new ArtNetController
    ArtNetController *controller = new ArtNetController(m_IOmapping.at(input).IPAddress,
                                                        m_netInterfaces, m_IOmapping.at(input).MACAddress,
                                                        ArtNetController::Input, input, this);
    connect(controller, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)),
            this, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)));
    m_IOmapping[input].controller = controller;

    return true;
}

void ArtNetPlugin::closeInput(quint32 input)
{
    if (input >= (quint32)m_IOmapping.length())
        return;
    ArtNetController *controller = m_IOmapping.at(input).controller;
    if (controller != NULL)
    {
        controller->changeReferenceCount(ArtNetController::Input, -1);
        // if a ArtNetController is also open as output
        // then just remove the input capability
        if (controller->type() & ArtNetController::Output)
        {
            controller->setType(ArtNetController::Output);
        }

        if (controller->referenceCount(ArtNetController::Input) == 0 &&
            controller->referenceCount(ArtNetController::Output) == 0)
        {
            delete m_IOmapping[input].controller;
            m_IOmapping[input].controller = NULL;
        }
    }
}

QString ArtNetPlugin::inputInfo(quint32 input)
{
    if (m_IOmapping.count() == 1)
        init();
    if (input >= (quint32)m_IOmapping.length())
        return QString();

    QString str;

    str += QString("
%1 %2
").arg(tr("Input")).arg(inputs()[input]);
    str += QString("
");
    ArtNetController *ctrl = m_IOmapping.at(input).controller;
    if (ctrl == NULL || ctrl->type() == ArtNetController::Output)
        str += tr("Status: Not open");
    else
    {
        str += tr("Status: Open");
        str += QString("
");
        str += tr("Packets received: ");
        str += QString("%1").arg(ctrl->getPacketReceivedNumber());
    }
    str += QString("
");
    str += QString("");
    str += QString("");

    return str;
}

/*********************************************************************
 * Configuration
 *********************************************************************/
void ArtNetPlugin::configure()
{
    ConfigureArtNet conf(this);
    conf.exec();
}

bool ArtNetPlugin::canConfigure()
{
    return true;
}

QList<QNetworkAddressEntry> ArtNetPlugin::interfaces()
{
    return m_netInterfaces;
}

QList<ArtNetIO> ArtNetPlugin::getIOMapping()
{
    return m_IOmapping;
}

/*****************************************************************************
 * Plugin export
 ****************************************************************************/
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_EXPORT_PLUGIN2(artnetplugin, ArtNetPlugin)
#endif
<commit_msg>Plugins/ArtNet: for unknown reasons the interfaces list could be empty too<commit_after>/*
  Q Light Controller Plus
  artnetplugin.cpp

  Copyright (c) Massimo Callegari

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include "artnetplugin.h"
#include "configureartnet.h"

#include <QSettings>
#include <QDebug>

ArtNetPlugin::~ArtNetPlugin()
{
}

void ArtNetPlugin::init()
{
    m_IOmapping.clear();

    foreach(QNetworkInterface interface, QNetworkInterface::allInterfaces())
    {
        foreach (QNetworkAddressEntry entry, interface.addressEntries())
        {
            QHostAddress addr = entry.ip();
            if (addr.protocol() != QAbstractSocket::IPv6Protocol)
            {
                ArtNetIO tmpIO;
                tmpIO.IPAddress = entry.ip().toString();
                if (addr == QHostAddress::LocalHost)
                    tmpIO.MACAddress = "11:22:33:44:55:66";
                else
                    tmpIO.MACAddress = interface.hardwareAddress();
                tmpIO.controller = NULL;
                m_IOmapping.append(tmpIO);

                m_netInterfaces.append(entry);
            }
        }
    }
}

QString ArtNetPlugin::name()
{
    return QString("ArtNet");
}

int ArtNetPlugin::capabilities() const
{
    return QLCIOPlugin::Output | QLCIOPlugin::Input | QLCIOPlugin::Infinite;
}

QString ArtNetPlugin::pluginInfo()
{
    QString str;

    str += QString("");
    str += QString("");
    str += QString("<TITLE>%1</TITLE>").arg(name());
    str += QString("");
    str += QString("");

    str += QString("
");
    str += QString("
%1
").arg(name());
    str += tr("This plugin provides DMX output for devices supporting the ArtNet communication protocol.");
    str += QString("
");

    return str;
}

/*********************************************************************
 * Outputs
 *********************************************************************/
QStringList ArtNetPlugin::outputs()
{
    QStringList list;
    int j = 0;
    if (m_IOmapping.count() < 2)
        init();
    foreach (ArtNetIO line, m_IOmapping)
    {
        list << QString(tr("%1: %2")).arg(j + 1).arg(line.IPAddress);
        j++;
    }
    return list;
}

QString ArtNetPlugin::outputInfo(quint32 output)
{
    if (m_IOmapping.count() < 2)
        init();

    if (output >= (quint32)m_IOmapping.length())
        return QString();

    QString str;

    str += QString("
%1 %2
").arg(tr("Output")).arg(outputs()[output]);
    str += QString("
");
    ArtNetController *ctrl = m_IOmapping.at(output).controller;
    if (ctrl == NULL || ctrl->type() == ArtNetController::Input)
        str += tr("Status: Not open");
    else
    {
        str += tr("Status: Open");
        str += QString("
");
        str += tr("Nodes discovered: ");
        str += QString("%1").arg(ctrl->getNodesList().size());
        str += QString("
");
        str += tr("Packets sent: ");
        str += QString("%1").arg(ctrl->getPacketSentNumber());
    }
    str += QString("
");
    str += QString("");
    str += QString("");

    return str;
}

bool ArtNetPlugin::openOutput(quint32 output)
{
    if (m_IOmapping.count() < 2)
        init();

    if (output >= (quint32)m_IOmapping.length())
        return false;

    qDebug() << "Open output with address :" << m_IOmapping.at(output).IPAddress;

    // already open ? Just add the type flag
    if (m_IOmapping[output].controller != NULL)
    {
        m_IOmapping[output].controller->setType(
                    (ArtNetController::Type)(m_IOmapping[output].controller->type() | ArtNetController::Output));
        m_IOmapping[output].controller->changeReferenceCount(ArtNetController::Output, +1);
        return true;
    }

    // not open ? Create a new ArtNetController
    ArtNetController *controller = new ArtNetController(m_IOmapping.at(output).IPAddress,
                                                        m_netInterfaces, m_IOmapping.at(output).MACAddress,
                                                        ArtNetController::Output, output, this);
    m_IOmapping[output].controller = controller;
    return true;
}

void ArtNetPlugin::closeOutput(quint32 output)
{
    if (output >= (quint32)m_IOmapping.length())
        return;
    ArtNetController *controller = m_IOmapping.at(output).controller;
    if (controller != NULL)
    {
        controller->changeReferenceCount(ArtNetController::Output, -1);
        // if a ArtNetController is also open as input
        // then just remove the output capability
        if (controller->type() & ArtNetController::Input)
        {
            controller->setType(ArtNetController::Input);
        }

        if (controller->referenceCount(ArtNetController::Input) == 0 &&
            controller->referenceCount(ArtNetController::Output) == 0)
        {
            delete m_IOmapping[output].controller;
            m_IOmapping[output].controller = NULL;
        }
    }
}

void ArtNetPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data)
{
    if (output >= (quint32)m_IOmapping.count())
        return;

    ArtNetController *controller = m_IOmapping[output].controller;
    if (controller != NULL)
        controller->sendDmx(universe, data);
}

/*************************************************************************
  * Inputs
  *************************************************************************/
QStringList ArtNetPlugin::inputs()
{
    QStringList list;
    int j = 0;
    if (m_IOmapping.count() < 2)
        init();
    foreach (ArtNetIO line, m_IOmapping)
    {
        list << QString(tr("%1: %2")).arg(j + 1).arg(line.IPAddress);
        j++;
    }
    return list;
}

bool ArtNetPlugin::openInput(quint32 input)
{
    if (m_IOmapping.count() < 2)
        init();

    if (input >= (quint32)m_IOmapping.length())
        return false;

    qDebug() << "Open input with address :" << m_IOmapping.at(input).IPAddress;

    // already open ? Just add the type flag
    if (m_IOmapping[input].controller != NULL)
    {
        m_IOmapping[input].controller->setType(
                    (ArtNetController::Type)(m_IOmapping[input].controller->type() | ArtNetController::Input));
        m_IOmapping[input].controller->changeReferenceCount(ArtNetController::Input, +1);
        return true;
    }

    // not open ? Create a new ArtNetController
    ArtNetController *controller = new ArtNetController(m_IOmapping.at(input).IPAddress,
                                                        m_netInterfaces, m_IOmapping.at(input).MACAddress,
                                                        ArtNetController::Input, input, this);
    connect(controller, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)),
            this, SIGNAL(valueChanged(quint32,quint32,quint32,uchar)));
    m_IOmapping[input].controller = controller;

    return true;
}

void ArtNetPlugin::closeInput(quint32 input)
{
    if (input >= (quint32)m_IOmapping.length())
        return;
    ArtNetController *controller = m_IOmapping.at(input).controller;
    if (controller != NULL)
    {
        controller->changeReferenceCount(ArtNetController::Input, -1);
        // if a ArtNetController is also open as output
        // then just remove the input capability
        if (controller->type() & ArtNetController::Output)
        {
            controller->setType(ArtNetController::Output);
        }

        if (controller->referenceCount(ArtNetController::Input) == 0 &&
            controller->referenceCount(ArtNetController::Output) == 0)
        {
            delete m_IOmapping[input].controller;
            m_IOmapping[input].controller = NULL;
        }
    }
}

QString ArtNetPlugin::inputInfo(quint32 input)
{
    if (m_IOmapping.count() < 2)
        init();
    if (input >= (quint32)m_IOmapping.length())
        return QString();

    QString str;

    str += QString("
%1 %2
").arg(tr("Input")).arg(inputs()[input]);
    str += QString("
");
    ArtNetController *ctrl = m_IOmapping.at(input).controller;
    if (ctrl == NULL || ctrl->type() == ArtNetController::Output)
        str += tr("Status: Not open");
    else
    {
        str += tr("Status: Open");
        str += QString("
");
        str += tr("Packets received: ");
        str += QString("%1").arg(ctrl->getPacketReceivedNumber());
    }
    str += QString("
");
    str += QString("");
    str += QString("");

    return str;
}

/*********************************************************************
 * Configuration
 *********************************************************************/
void ArtNetPlugin::configure()
{
    ConfigureArtNet conf(this);
    conf.exec();
}

bool ArtNetPlugin::canConfigure()
{
    return true;
}

QList<QNetworkAddressEntry> ArtNetPlugin::interfaces()
{
    return m_netInterfaces;
}

QList<ArtNetIO> ArtNetPlugin::getIOMapping()
{
    return m_IOmapping;
}

/*****************************************************************************
 * Plugin export
 ****************************************************************************/
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
Q_EXPORT_PLUGIN2(artnetplugin, ArtNetPlugin)
#endif
<|endoftext|>


<commit_before>// Copyright (c) 2013, Michael Boyle
// See LICENSE file for details

#include "SWSHs.hpp"
#include "Quaternions.hpp"

#define InfinitelyManySolutions 1
#define NotEnoughPointsForDerivative 2
#define VectorSizeNotUnderstood 3
#define VectorSizeMismatch 4
// #define  7

using namespace SphericalFunctions;
using Quaternions::Quaternion;
using std::vector;
using std::complex;
using std::cerr;
using std::endl;

/// Class to create an object returning the factorial of an argument.
vector<double> FactorialTableCalculator() {
  /// Note that because a double is returned, only values up to 28!
  /// will be exact; higher values will be accurate to machine
  /// precision.  Values up to 170! only are allowed because higher
  /// values overflow.
  vector<double> FactorialTable(171);
  FactorialTable[0] = 1.0;
  for (int i=1;i<171;i++) {
    FactorialTable[i] = i*FactorialTable[i-1];
  }
  return FactorialTable;
}
const std::vector<double> FactorialFunctor::FactorialTable = FactorialTableCalculator();

vector<double> BinomialCoefficientCalculator() {
  /// We need (n+1) coefficients for each value of n from 0 (for
  /// completeness) up to 2*ellMax (hard coded in the header file).
  /// That's a total of
  ///   >>> from sympy import summation, symbols
  ///   >>> ellMax, n, k = symbols('ellMax n k', integer=True)
  ///   >>> summation(n+1, (n, 0, 2*ellMax))
  ///  2*ellMax**2 + 3*ellMax + 1
  /// With a similar calculation, we can see that the associated access
  /// operator needs element (n*(n+1)/2 + k) of the array.
  vector<double> BinomialCoefficientTable(2*ellMax*ellMax + 3*ellMax + 1);
  unsigned int i=0;
  FactorialFunctor Factorial;
  for(unsigned int n=0; n<=2*ellMax; ++n) {
    for(unsigned int k=0; k<=n; ++k) {
      BinomialCoefficientTable[i++] = std::floor(0.5+Factorial(n)/(Factorial(k)*Factorial(n-k)));
    }
  }
  return BinomialCoefficientTable;
}
const std::vector<double> BinomialCoefficientFunctor::BinomialCoefficientTable = BinomialCoefficientCalculator();

vector<double> LadderOperatorFactorCalculator() {
  /// We need (2*ell+1) coefficients for each value of ell from 0 (for
  /// completeness) up to ellMax (hard coded in the header file).
  /// That's a total of
  ///   >>> from sympy import summation, symbols
  ///   >>> ell, ellMax, m, mp = symbols('ell ellMax m mp', integer=True)
  ///   >>> summation(2*ell+1, (ell, 0, ellMax))
  ///   ellMax**2 + 2*ellMax + 1
  /// With a similar calculation, we can see that the associated access
  /// operator needs element
  ///   >>> summation(2*ell+1, (ell, 0, ell-1)) + ell + m
  ///   ell**2 + ell + m
  std::vector<double> FactorTable(ellMax*ellMax + 2*ellMax + 1);
  unsigned int i=0;
  for(int ell=0; ell<=ellMax; ++ell) {
    for(int m=-ell; m<=ell; ++m) {
      FactorTable[i++] = std::sqrt(ell*(ell+1)-m*(m+1));
    }
  }
  return FactorTable;
}
const std::vector<double> LadderOperatorFactorFunctor::FactorTable = LadderOperatorFactorCalculator();

std::vector<double> WignerCoefficientCalculator() {
  /// We need (2*ell+1)*(2*ell+1) coefficients for each value of ell
  /// from 0 (for completenes) up to ellMax (hard coded in the header
  /// file).  That's a total of
  ///   >>> from sympy import summation, symbols, simplify
  ///   >>> from sympy.polys.polyfuncs import horner
  ///   >>> ell, ellMax, m, mp = symbols('ell ellMax m mp', integer=True)
  ///   >>> horner(simplify(summation((2*ell+1)**2, (ell, 0, ellMax))))
  ///   ellMax*(ellMax*(4*ellMax/3 + 4) + 11/3) + 1
  /// With a similar calculation, we can see that the associated access
  /// operator needs element
  ///   >>> horner(summation((2*ell+1)**2, (ell, 0, ell-1)) + (2*ell+1)*(ell+mp) + ell + m)
  ///   ell*(ell*(4*ell/3 + 2) + 5/3) + mp*(2*ell + 1) + m
  /// of the array.
  std::vector<double> CoefficientTable(int(ellMax*(ellMax*(1.3333333333333333*ellMax + 4) + 3.6666666666666667) + 1 + 0.5));
  FactorialFunctor Factorial;
  unsigned int i=0;
  for(int ell=0; ell<=ellMax; ++ell) {
    for(int mp=-ell; mp<=ell; ++mp) {
      for(int m=-ell; m<=ell; ++m) {
	CoefficientTable[i++] =
	  std::sqrt( Factorial(ell+m)*Factorial(ell-m)
		     / double(Factorial(ell+mp)*Factorial(ell-mp)) );
      }
    }
  }
  return CoefficientTable;
}
const std::vector<double> WignerCoefficientFunctor::CoefficientTable = WignerCoefficientCalculator();

/// Construct the D matrix object given the (optional) rotor.
WignerDMatrix::WignerDMatrix(const Quaternion& R)
  : BinomialCoefficient(), WignerCoefficient(),
    Ra(R[0], R[3]), Rb(R[2], R[1]),
    absRa(abs(Ra)), absRb(abs(Rb)), absRRatioSquared(absRb*absRb/(absRa*absRa))
{ }

/// Reset the rotor for this object to the given value.
WignerDMatrix& WignerDMatrix::SetRotation(const Quaternion& R) {
  Ra = std::complex<double>(R[0], R[3]);
  Rb = std::complex<double>(R[2], R[1]);
  absRa = abs(Ra);
  absRb = abs(Rb);
  absRRatioSquared = absRb*absRb/(absRa*absRa);
  return *this;
}

/// Evaluate the D matrix element for the given (ell, mp, m) indices.
std::complex<double> WignerDMatrix::operator()(const int ell, const int mp, const int m) const {
  if(absRa < epsilon) {
    return (mp!=-m ? 0.0 : ((ell+mp)%2==0 ? 1.0 : -1.0) * std::pow(Rb, 2*m) );
  }
  if(absRb < epsilon) {
    return (mp!=m ? 0.0 : std::pow(Ra, 2*m) );
  }
  if(absRa < 1.e-3) { // Deal with NANs in certain cases
    const std::complex<double> Prefactor =
      WignerCoefficient(ell, mp, m) * std::pow(Ra, m+mp) * std::pow(Rb, m-mp);
    const int rhoMin = std::max(0,mp-m);
    const int rhoMax = std::min(ell+mp,ell-m);
    const double absRaSquared = absRa*absRa;
    const double absRbSquared = absRb*absRb;
    double Sum = 0.0;
    for(int rho=rhoMax; rho>=rhoMin; --rho) {
      const double aTerm = std::pow(absRaSquared, ell-m-rho);
      if(aTerm != aTerm || aTerm<1.e-100) { // This assumes --fast-math is off
	Sum *= absRbSquared;
	continue;
      }
      Sum = ( (rho%2==0 ? 1 : -1) * BinomialCoefficient(ell+mp,rho) * BinomialCoefficient(ell-mp, ell-rho-m) * aTerm )
	+ ( Sum * absRbSquared );
    }
    return Prefactor * Sum * std::pow(absRbSquared, rhoMin);
  }
  const std::complex<double> Prefactor =
    (WignerCoefficient(ell, mp, m) * std::pow(absRa, 2*ell-2*m))
    * std::pow(Ra, m+mp) * std::pow(Rb, m-mp);
  const int rhoMin = std::max(0,mp-m);
  const int rhoMax = std::min(ell+mp,ell-m);
  double Sum = 0.0;
  for(int rho=rhoMax; rho>=rhoMin; --rho) {
    Sum = ( (rho%2==0 ? 1 : -1) * BinomialCoefficient(ell+mp,rho) * BinomialCoefficient(ell-mp, ell-rho-m) )
      + ( Sum * absRRatioSquared );
  }
  return Prefactor * Sum * std::pow(absRRatioSquared, rhoMin);
}
<commit_msg>Use integer arithmetic for indexing<commit_after>// Copyright (c) 2013, Michael Boyle
// See LICENSE file for details

#include "SWSHs.hpp"
#include "Quaternions.hpp"

#define InfinitelyManySolutions 1
#define NotEnoughPointsForDerivative 2
#define VectorSizeNotUnderstood 3
#define VectorSizeMismatch 4
// #define  7

using namespace SphericalFunctions;
using Quaternions::Quaternion;
using std::vector;
using std::complex;
using std::cerr;
using std::endl;

/// Class to create an object returning the factorial of an argument.
vector<double> FactorialTableCalculator() {
  /// Note that because a double is returned, only values up to 28!
  /// will be exact; higher values will be accurate to machine
  /// precision.  Values up to 170! only are allowed because higher
  /// values overflow.
  vector<double> FactorialTable(171);
  FactorialTable[0] = 1.0;
  for (int i=1;i<171;i++) {
    FactorialTable[i] = i*FactorialTable[i-1];
  }
  return FactorialTable;
}
const std::vector<double> FactorialFunctor::FactorialTable = FactorialTableCalculator();

vector<double> BinomialCoefficientCalculator() {
  /// We need (n+1) coefficients for each value of n from 0 (for
  /// completeness) up to 2*ellMax (hard coded in the header file).
  /// That's a total of
  ///   >>> from sympy import summation, symbols
  ///   >>> ellMax, n, k = symbols('ellMax n k', integer=True)
  ///   >>> summation(n+1, (n, 0, 2*ellMax))
  ///  2*ellMax**2 + 3*ellMax + 1
  /// With a similar calculation, we can see that the associated access
  /// operator needs element (n*(n+1)/2 + k) of the array.
  vector<double> BinomialCoefficientTable(2*ellMax*ellMax + 3*ellMax + 1);
  unsigned int i=0;
  FactorialFunctor Factorial;
  for(unsigned int n=0; n<=2*ellMax; ++n) {
    for(unsigned int k=0; k<=n; ++k) {
      BinomialCoefficientTable[i++] = std::floor(0.5+Factorial(n)/(Factorial(k)*Factorial(n-k)));
    }
  }
  return BinomialCoefficientTable;
}
const std::vector<double> BinomialCoefficientFunctor::BinomialCoefficientTable = BinomialCoefficientCalculator();

vector<double> LadderOperatorFactorCalculator() {
  /// We need (2*ell+1) coefficients for each value of ell from 0 (for
  /// completeness) up to ellMax (hard coded in the header file).
  /// That's a total of
  ///   >>> from sympy import summation, symbols
  ///   >>> ell, ellMax, m, mp = symbols('ell ellMax m mp', integer=True)
  ///   >>> summation(2*ell+1, (ell, 0, ellMax))
  ///   ellMax**2 + 2*ellMax + 1
  /// With a similar calculation, we can see that the associated access
  /// operator needs element
  ///   >>> summation(2*ell+1, (ell, 0, ell-1)) + ell + m
  ///   ell**2 + ell + m
  std::vector<double> FactorTable(ellMax*ellMax + 2*ellMax + 1);
  unsigned int i=0;
  for(int ell=0; ell<=ellMax; ++ell) {
    for(int m=-ell; m<=ell; ++m) {
      FactorTable[i++] = std::sqrt(ell*(ell+1)-m*(m+1));
    }
  }
  return FactorTable;
}
const std::vector<double> LadderOperatorFactorFunctor::FactorTable = LadderOperatorFactorCalculator();

std::vector<double> WignerCoefficientCalculator() {
  /// We need (2*ell+1)*(2*ell+1) coefficients for each value of ell
  /// from 0 (for completenes) up to ellMax (hard coded in the header
  /// file).  That's a total of
  ///   >>> from sympy import summation, symbols, simplify
  ///   >>> from sympy.polys.polyfuncs import horner
  ///   >>> ell, ellMax, m, mp = symbols('ell ellMax m mp', integer=True)
  ///   >>> horner(simplify(summation((2*ell+1)**2, (ell, 0, ellMax))))
  ///   ellMax*(ellMax*(4*ellMax/3 + 4) + 11/3) + 1
  /// With a similar calculation, we can see that the associated access
  /// operator needs element
  ///   >>> horner(summation((2*ell+1)**2, (ell, 0, ell-1)) + (2*ell+1)*(ell+mp) + ell + m)
  ///   ell*(ell*(4*ell/3 + 2) + 5/3) + mp*(2*ell + 1) + m
  /// of the array.
  std::vector<double> CoefficientTable(ellMax*(ellMax*(4*ellMax + 12) + 11)/3 + 1);
  FactorialFunctor Factorial;
  unsigned int i=0;
  for(int ell=0; ell<=ellMax; ++ell) {
    for(int mp=-ell; mp<=ell; ++mp) {
      for(int m=-ell; m<=ell; ++m) {
	CoefficientTable[i++] =
	  std::sqrt( Factorial(ell+m)*Factorial(ell-m)
		     / double(Factorial(ell+mp)*Factorial(ell-mp)) );
      }
    }
  }
  return CoefficientTable;
}
const std::vector<double> WignerCoefficientFunctor::CoefficientTable = WignerCoefficientCalculator();

/// Construct the D matrix object given the (optional) rotor.
WignerDMatrix::WignerDMatrix(const Quaternion& R)
  : BinomialCoefficient(), WignerCoefficient(),
    Ra(R[0], R[3]), Rb(R[2], R[1]),
    absRa(abs(Ra)), absRb(abs(Rb)), absRRatioSquared(absRb*absRb/(absRa*absRa))
{ }

/// Reset the rotor for this object to the given value.
WignerDMatrix& WignerDMatrix::SetRotation(const Quaternion& R) {
  Ra = std::complex<double>(R[0], R[3]);
  Rb = std::complex<double>(R[2], R[1]);
  absRa = abs(Ra);
  absRb = abs(Rb);
  absRRatioSquared = absRb*absRb/(absRa*absRa);
  return *this;
}

/// Evaluate the D matrix element for the given (ell, mp, m) indices.
std::complex<double> WignerDMatrix::operator()(const int ell, const int mp, const int m) const {
  if(absRa < epsilon) {
    return (mp!=-m ? 0.0 : ((ell+mp)%2==0 ? 1.0 : -1.0) * std::pow(Rb, 2*m) );
  }
  if(absRb < epsilon) {
    return (mp!=m ? 0.0 : std::pow(Ra, 2*m) );
  }
  if(absRa < 1.e-3) { // Deal with NANs in certain cases
    const std::complex<double> Prefactor =
      WignerCoefficient(ell, mp, m) * std::pow(Ra, m+mp) * std::pow(Rb, m-mp);
    const int rhoMin = std::max(0,mp-m);
    const int rhoMax = std::min(ell+mp,ell-m);
    const double absRaSquared = absRa*absRa;
    const double absRbSquared = absRb*absRb;
    double Sum = 0.0;
    for(int rho=rhoMax; rho>=rhoMin; --rho) {
      const double aTerm = std::pow(absRaSquared, ell-m-rho);
      if(aTerm != aTerm || aTerm<1.e-100) { // This assumes --fast-math is off
	Sum *= absRbSquared;
	continue;
      }
      Sum = ( (rho%2==0 ? 1 : -1) * BinomialCoefficient(ell+mp,rho) * BinomialCoefficient(ell-mp, ell-rho-m) * aTerm )
	+ ( Sum * absRbSquared );
    }
    return Prefactor * Sum * std::pow(absRbSquared, rhoMin);
  }
  const std::complex<double> Prefactor =
    (WignerCoefficient(ell, mp, m) * std::pow(absRa, 2*ell-2*m))
    * std::pow(Ra, m+mp) * std::pow(Rb, m-mp);
  const int rhoMin = std::max(0,mp-m);
  const int rhoMax = std::min(ell+mp,ell-m);
  double Sum = 0.0;
  for(int rho=rhoMax; rho>=rhoMin; --rho) {
    Sum = ( (rho%2==0 ? 1 : -1) * BinomialCoefficient(ell+mp,rho) * BinomialCoefficient(ell-mp, ell-rho-m) )
      + ( Sum * absRRatioSquared );
  }
  return Prefactor * Sum * std::pow(absRRatioSquared, rhoMin);
}
<|endoftext|>