#include <gsl/gsl_math.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_complex.h>
#include <gsl/gsl_complex_math.h>

#include "oct.h"

void
print_octave(const gsl_matrix *m, const char *str)
{
  FILE *fp;
  size_t i, j;
  const size_t N = m->size1;
  const size_t M = m->size2;

  if (str == NULL)
    fp = stdout;
  else
    fp = fopen(str, "w");

  if (!fp)
    return;

  fprintf(fp, "%% Created by Octave 2.1.73, Tue Aug 01 15:00:27 2006 MDT <blah@blah>\n");
  fprintf(fp, "%% name: %s\n", str);
  fprintf(fp, "%% type: matrix\n");
  fprintf(fp, "%% rows: %zu\n", N);
  fprintf(fp, "%% columns: %zu\n", M);

  for (i = 0; i < N; ++i)
    {
      for (j = 0; j < M; ++j)
        {
          fprintf(fp,
                  "%10.12e%s",
                  gsl_matrix_get(m, i, j),
                  (j < M - 1) ? " " : "\n");
        }
    }

  if (str != NULL)
    fclose(fp);
}

void
printv_octave(const gsl_vector *v, const char *str)
{
  FILE *fp;
  size_t i;
  const size_t N = v->size;

  fp = fopen(str, "w");

  if (!fp)
    return;

  fprintf(fp, "%% Created by Octave 2.1.73, Tue Aug 01 15:00:27 2006 MDT <blah@blah>\n");
  fprintf(fp, "%% name: %s\n", str);
  fprintf(fp, "%% type: matrix\n");
  fprintf(fp, "%% rows: %zu\n", N);
  fprintf(fp, "%% columns: %u\n", 1);

  for (i = 0; i < N; ++i)
    {
      fprintf(fp, "%10.12e\n", gsl_vector_get(v, i));
    }

  fclose(fp);
}

void
printc_octave(const gsl_matrix_complex *m, const char *str)
{
  FILE *fp;
  size_t i, j;
  const size_t N = m->size1;
  const size_t M = m->size2;

  fp = fopen(str, "w");

  if (!fp)
    return;

  fprintf(fp, "%% Created by Octave 2.1.73, Tue Aug 01 15:00:27 2006 MDT <blah@blah>\n");
  fprintf(fp, "%% name: %s\n", str);
  fprintf(fp, "%% type: complex matrix\n");
  fprintf(fp, "%% rows: %zu\n", N);
  fprintf(fp, "%% columns: %zu\n", M);

  for (i = 0; i < N; ++i)
    {
      for (j = 0; j < M; ++j)
        {
          gsl_complex z = gsl_matrix_complex_get(m, i, j);
          fprintf(fp,
                  "(%.12e,%.12e)%s",
                  GSL_REAL(z),
                  GSL_IMAG(z),
                  (j < M - 1) ? " " : "\n");
        }
    }

  fclose(fp);
}

/* print symmetric matrix, using upper triangle */
void
printsym_octave(const gsl_matrix *m, const char *str)
{
  FILE *fp;
  size_t i, j;
  const size_t N = m->size1;
  const size_t M = m->size2;

  fp = fopen(str, "w");

  if (!fp)
    return;

  fprintf(fp, "%% Created by Octave 2.1.73, Tue Aug 01 15:00:27 2006 MDT <blah@blah>\n");
  fprintf(fp, "%% name: %s\n", str);
  fprintf(fp, "%% type: matrix\n");
  fprintf(fp, "%% rows: %zu\n", N);
  fprintf(fp, "%% columns: %zu\n", M);

  for (i = 0; i < N; ++i)
    {
      for (j = 0; j < M; ++j)
        {
          double z;

          if (j >= i)
            z = gsl_matrix_get(m, i, j);
          else
            z = gsl_matrix_get(m, j, i);

          fprintf(fp,
                  "%.12e%s",
                  z,
                  (j < M - 1) ? " " : "\n");
        }
    }

  fclose(fp);
}

/* print Hermitian matrix, using upper triangle */
void
printherm_octave(const gsl_matrix_complex *m, const char *str)
{
  FILE *fp;
  size_t i, j;
  const size_t N = m->size1;
  const size_t M = m->size2;

  fp = fopen(str, "w");

  if (!fp)
    return;

  fprintf(fp, "%% Created by Octave 2.1.73, Tue Aug 01 15:00:27 2006 MDT <blah@blah>\n");
  fprintf(fp, "%% name: %s\n", str);
  fprintf(fp, "%% type: complex matrix\n");
  fprintf(fp, "%% rows: %zu\n", N);
  fprintf(fp, "%% columns: %zu\n", M);

  for (i = 0; i < N; ++i)
    {
      for (j = 0; j < M; ++j)
        {
          gsl_complex z;

          if (j >= i)
            z = gsl_matrix_complex_get(m, i, j);
          else
            z = gsl_complex_conjugate(gsl_matrix_complex_get(m, j, i));

          fprintf(fp,
                  "(%.12e,%.12e)%s",
                  GSL_REAL(z),
                  GSL_IMAG(z),
                  (j < M - 1) ? " " : "\n");
        }
    }

  fclose(fp);
}

void
printcv_octave(const gsl_vector_complex *v, const char *str)
{
  FILE *fp;
  size_t i;
  const size_t N = v->size;

  fp = fopen(str, "w");

  if (!fp)
    return;

  fprintf(fp, "%% Created by Octave 2.1.73, Tue Aug 01 15:00:27 2006 MDT <blah@blah>\n");
  fprintf(fp, "%% name: %s\n", str);
  fprintf(fp, "%% type: complex matrix\n");
  fprintf(fp, "%% rows: %zu\n", N);
  fprintf(fp, "%% columns: %u\n", 1);

  for (i = 0; i < N; ++i)
    {
      gsl_complex z = gsl_vector_complex_get(v, i);
      fprintf(fp, "(%.12e,%.12e)\n", GSL_REAL(z), GSL_IMAG(z));
    }

  fclose(fp);
}

/* print triangular matrix, using upper triangle */
void
printtri_octave(const gsl_matrix *m, const char *str)
{
  FILE *fp;
  size_t i, j;
  const size_t N = m->size1;
  const size_t M = m->size2;

  if (str == NULL)
    fp = stdout;
  else
    fp = fopen(str, "w");

  if (!fp)
    return;

  fprintf(fp, "%% Created by Octave 2.1.73, Tue Aug 01 15:00:27 2006 MDT <blah@blah>\n");
  fprintf(fp, "%% name: %s\n", str);
  fprintf(fp, "%% type: matrix\n");
  fprintf(fp, "%% rows: %zu\n", N);
  fprintf(fp, "%% columns: %zu\n", M);

  for (i = 0; i < N; ++i)
    {
      for (j = 0; j < M; ++j)
        {
          double mij;

          if (j >= i)
            mij = gsl_matrix_get(m, i, j);
          else
            mij = 0.0;

          fprintf(fp,
                  "%10.12e%s",
                  mij,
                  (j < M - 1) ? " " : "\n");
        }
    }

  if (str != NULL)
    fclose(fp);
}


#pragma once

#include <gsl/span>

#include "halley/text/halleystring.h"
#include "halley/resources/resource.h"
#include "halley/maths/range.h"
#include "halley/maths/vector4.h"
#include "halley/data_structures/maybe.h"
#include <map>
#include <vector>

#include "halley/utils/type_traits.h"

#if defined(DEV_BUILD)
#define STORE_CONFIG_NODE_PARENTING
#endif

namespace Halley {
	class Serializer;
	class Deserializer;
	class ConfigNode;

	template<typename T>
	struct HasToConfigNode
	{
	private:
		typedef std::true_type yes;
		typedef std::false_type no;
		template<typename U> static auto test(int) -> decltype(std::declval<U>().toConfigNode(), yes());
		template<typename> static no test(...);

	public:
		static constexpr bool value = std::is_same<decltype(test<T>(0)),yes>::value;
	};

	template<typename T>
	struct HasConfigNodeConstructor
	{
	private:
		typedef std::true_type yes;
		typedef std::false_type no;
		template<typename U> static auto test(int) -> decltype(U(std::declval<ConfigNode>()), yes());
		template<typename> static no test(...);

	public:
		static constexpr bool value = std::is_same<decltype(test<T>(0)),yes>::value;
	};

	enum class ConfigNodeType
	{
		Undefined,
		String,
		Sequence,
		Map,
		Int,
		Float,
		Int2,
		Float2,
		Bytes,
		DeltaSequence, // For delta coding
		DeltaMap, // For delta coding
		Noop, // For delta coding
		Idx, // For delta coding
		Del // For delta coding
	};

	template <>
	struct EnumNames<ConfigNodeType> {
		constexpr std::array<const char*, 13> operator()() const {
			return{{
				"undefined",
				"string",
				"sequence",
				"map",
				"int",
				"float",
				"int2",
				"float2",
				"bytes",
				"deltaSequence",
				"deltaMap",
				"noop",
				"idx"
				"del"
			}};
		}
	};

	class ConfigFile;

	class ConfigNode
	{
		friend class ConfigFile;

	public:
		using MapType = std::map<String, ConfigNode, std::less<>>;
		using SequenceType = std::vector<ConfigNode>;

		struct NoopType {};
		struct DelType {};
		struct IdxType {
			int start;
			int len;
			IdxType() = default;
			IdxType(int start, int len) : start(start), len(len) {}
		};

		ConfigNode();
		explicit ConfigNode(const ConfigNode& other);
		ConfigNode(ConfigNode&& other) noexcept;
		ConfigNode(MapType entryMap);
		ConfigNode(SequenceType entryList);
		explicit ConfigNode(String value);
		explicit ConfigNode(const std::string_view& value);
		explicit ConfigNode(bool value);
		explicit ConfigNode(int value);
		explicit ConfigNode(float value);
		explicit ConfigNode(Vector2i value);
		explicit ConfigNode(Vector2f value);
		explicit ConfigNode(Bytes value);
		explicit ConfigNode(NoopType value);
		explicit ConfigNode(DelType value);
		explicit ConfigNode(IdxType value);

		template <typename T>
		explicit ConfigNode(const std::vector<T>& sequence)
		{
			*this = sequence;
		}

		template <typename K, typename V>
		explicit ConfigNode(const std::map<K, V>& values)
		{
			*this = values;
		}

		~ConfigNode();

		ConfigNode& operator=(const ConfigNode& other);
		ConfigNode& operator=(ConfigNode&& other) noexcept;
		ConfigNode& operator=(bool value);
		ConfigNode& operator=(int value);
		ConfigNode& operator=(float value);
		ConfigNode& operator=(Vector2i value);
		ConfigNode& operator=(Vector2f value);

		ConfigNode& operator=(MapType entryMap);
		ConfigNode& operator=(SequenceType entryList);
		ConfigNode& operator=(String value);
		ConfigNode& operator=(Bytes value);
		ConfigNode& operator=(gsl::span<const gsl::byte> bytes);

		ConfigNode& operator=(const char* value);
		ConfigNode& operator=(const std::string_view& value);

		ConfigNode& operator=(NoopType value);
		ConfigNode& operator=(DelType value);
		ConfigNode& operator=(IdxType value);

		template <typename T>
		ConfigNode& operator=(const std::vector<T>& sequence)
		{
			SequenceType seq;
			seq.reserve(sequence.size());
			for (const auto& e: sequence) {
				if constexpr (HasToConfigNode<T>::value) {
					seq.push_back(e.toConfigNode());
				} else {
					seq.push_back(ConfigNode(e));
				}
			}
			return *this = seq;
		}

		template <typename K, typename V>
		ConfigNode& operator=(const std::map<K, V>& values)
		{
			MapType map;
			for (const auto& [k, v]: values) {
				String key = toString(k);

				if constexpr (HasToConfigNode<V>::value) {
					map[std::move(key)] = v.toConfigNode();
				} else {
					map[std::move(key)] = ConfigNode(v);
				}
			}
			return *this = map;
		}

		bool operator==(const ConfigNode& other) const;
		bool operator!=(const ConfigNode& other) const;

		ConfigNodeType getType() const;

		void serialize(Serializer& s) const;
		void deserialize(Deserializer& s);

		int asInt() const;
		float asFloat() const;
		bool asBool() const;
		Vector2i asVector2i() const;
		Vector2f asVector2f() const;
		Vector3i asVector3i() const;
		Vector3f asVector3f() const;
		Vector4i asVector4i() const;
		Vector4f asVector4f() const;
		Range<float> asFloatRange() const;
		String asString() const;
		const Bytes& asBytes() const;

		int asInt(int defaultValue) const;
		float asFloat(float defaultValue) const;
		bool asBool(bool defaultValue) const;
		String asString(const std::string_view& defaultValue) const;
		Vector2i asVector2i(Vector2i defaultValue) const;
		Vector2f asVector2f(Vector2f defaultValue) const;
		Vector3i asVector3i(Vector3i defaultValue) const;
		Vector3f asVector3f(Vector3f defaultValue) const;
		Vector4i asVector4i(Vector4i defaultValue) const;
		Vector4f asVector4f(Vector4f defaultValue) const;

		template <typename T>
		std::vector<T> asVector() const
		{
			if (type == ConfigNodeType::Sequence) {
				std::vector<T> result;
				result.reserve(asSequence().size());
				for (const auto& e : asSequence()) {
					if constexpr (HasConfigNodeConstructor<T>::value) {
						result.emplace_back(T(e));
					} else {
						result.emplace_back(e.convertTo(Tag<T>()));
					}
				}
				return result;
			} else if (type == ConfigNodeType::Undefined) {
				return {};
			} else {
				throw Exception("Can't convert " + getNodeDebugId() + " from " + toString(getType()) + " to std::vector<T>.", HalleyExceptions::Resources);
			}
		}

		template <typename T>
		std::vector<T> asVector(const std::vector<T>& defaultValue) const
		{
			if (type == ConfigNodeType::Sequence) {
				return asVector<T>();
			} else {
				return defaultValue;
			}
		}

		template <typename K, typename V>
		std::map<K, V> asMap() const
		{
			if (type == ConfigNodeType::Map) {
				std::map<K, V> result;
				for (const auto& [k, v] : asMap()) {
					const K key = fromString<K>(k);

					if constexpr (HasConfigNodeConstructor<V>::value) {
						result[std::move(key)] = V(v);
					} else {
						result[std::move(key)] = v.convertTo(Tag<V>());
					}
				}
				return result;
			} else if (type == ConfigNodeType::Undefined) {
				return {};
			} else {
				throw Exception("Can't convert " + getNodeDebugId() + " from " + toString(getType()) + " to std::map<K, V>.", HalleyExceptions::Resources);
			}
		}

		const SequenceType& asSequence() const;
		const MapType& asMap() const;
		SequenceType& asSequence();
		MapType& asMap();

		void ensureType(ConfigNodeType type);

		bool hasKey(const String& key) const;
		void removeKey(const String& key);

		ConfigNode& operator[](const std::string_view& key);
		ConfigNode& operator[](size_t idx);

		const ConfigNode& operator[](const std::string_view& key) const;
		const ConfigNode& operator[](size_t idx) const;

		SequenceType::iterator begin();
		SequenceType::iterator end();

		SequenceType::const_iterator begin() const;
		SequenceType::const_iterator end() const;

		void reset();
		void setOriginalPosition(int line, int column);
		void setParent(const ConfigNode* parent, int idx);
		void propagateParentingInformation(const ConfigFile* parentFile);

		inline void assertValid() const
		{
			Expects(intData != 0xCDCDCDCD);
			Expects(intData != 0xDDDDDDDD);
		}

		struct BreadCrumb {
			const BreadCrumb* prev = nullptr;
			String key;
			OptionalLite<int> idx;
			int depth = 0;

			BreadCrumb() = default;
			BreadCrumb(const BreadCrumb& prev, String key) : prev(&prev), key(std::move(key)), depth(prev.depth + 1) {}
			BreadCrumb(const BreadCrumb& prev, int index) : prev(&prev), idx(index), depth(prev.depth + 1) {}

			bool hasKeyAt(const String& key, int depth) const;
			bool hasIndexAt(int idx, int depth) const;
		};

		class IDeltaCodeHints {
		public:
			virtual ~IDeltaCodeHints() = default;

			virtual std::optional<size_t> getSequenceMatch(const SequenceType& seq, const ConfigNode& newValue, size_t curIdx, const BreadCrumb& breadCrumb) const = 0;
			virtual bool doesSequenceOrderMatter(const BreadCrumb& breadCrumb) const { return true; }
			virtual bool canDeleteKey(const String& key, const BreadCrumb& breadCrumb) const { return true; }
			virtual bool canDeleteAnyKey() const { return true; }
			virtual bool shouldBypass(const BreadCrumb& breadCrumb) const { return false; }
			virtual bool areNullAndEmptyEquivalent(const BreadCrumb& breadCrumb) const { return false; }
		};

		static ConfigNode createDelta(const ConfigNode& from, const ConfigNode& to, const IDeltaCodeHints* hints = nullptr);
		static ConfigNode applyDelta(const ConfigNode& from, const ConfigNode& delta);
		void applyDelta(const ConfigNode& delta);
		void decayDeltaArtifacts();

	private:
		template <typename T>
		class Tag {};

		union {
			String* strData;
			MapType* mapData;
			SequenceType* sequenceData;
			Bytes* bytesData;
			void* rawPtrData;
			int intData;
			float floatData;
			Vector2i vec2iData;
			Vector2f vec2fData;
		};
		ConfigNodeType type = ConfigNodeType::Undefined;
		int auxData = 0; // Used by delta coding

#if defined(STORE_CONFIG_NODE_PARENTING)
		struct ParentingInfo {
			int line = 0;
			int column = 0;
			int idx = 0;
			const ConfigNode* node = nullptr;
			const ConfigFile* file = nullptr;
		};
		std::unique_ptr<ParentingInfo> parent;
#endif

		static ConfigNode undefinedConfigNode;
		static String undefinedConfigNodeName;

		template <typename T> void deserializeContents(Deserializer& s)
		{
			T v;
			s >> v;
			*this = std::move(v);
		}

		String getNodeDebugId() const;
		String backTrackFullNodeName() const;

		int convertTo(Tag<int> tag) const;
		float convertTo(Tag<float> tag) const;
		bool convertTo(Tag<bool> tag) const;
		Vector2i convertTo(Tag<Vector2i> tag) const;
		Vector2f convertTo(Tag<Vector2f> tag) const;
		Vector3i convertTo(Tag<Vector3i> tag) const;
		Vector3f convertTo(Tag<Vector3f> tag) const;
		Vector4i convertTo(Tag<Vector4i> tag) const;
		Vector4f convertTo(Tag<Vector4f> tag) const;
		Range<float> convertTo(Tag<Range<float>> tag) const;
		String convertTo(Tag<String> tag) const;
		const Bytes& convertTo(Tag<Bytes&> tag) const;

		bool isNullOrEmpty() const;

		static ConfigNode doCreateDelta(const ConfigNode& from, const ConfigNode& to, const BreadCrumb& breadCrumb, const IDeltaCodeHints* hints);
		static ConfigNode createMapDelta(const ConfigNode& from, const ConfigNode& to, const BreadCrumb& breadCrumb, const IDeltaCodeHints* hints);
		static ConfigNode createSequenceDelta(const ConfigNode& from, const ConfigNode& to, const BreadCrumb& breadCrumb, const IDeltaCodeHints* hints);
		void applyMapDelta(const ConfigNode& delta);
		void applySequenceDelta(const ConfigNode& delta);

		bool isEquivalent(const ConfigNode& other) const;
		bool isEquivalentStrictOrder(const ConfigNode& other) const;
	};
}


#ifndef SmtkMatcher_h
#define SmtkMatcher_h

/**
 * @file
 * $Revision: 1.1 $
 * $Date: 2009/09/09 23:42:41 $
 *
 *   Unless noted otherwise, the portions of Isis written by the USGS are
 *   public domain. See individual third-party library and package descriptions
 *   for intellectual property information, user agreements, and related
 *   information.
 *
 *   Although Isis has been used by the USGS, no warranty, expressed or
 *   implied, is made by the USGS as to the accuracy and functioning of such
 *   software and related material nor shall the fact of distribution
 *   constitute any such warranty, and no responsibility is assumed by the
 *   USGS in connection therewith.
 *
 *   For additional information, launch
 *   $ISISROOT/doc//documents/Disclaimers/Disclaimers.html
 *   in a browser or see the Privacy & Disclaimers page on the Isis website,
 *   http://isis.astrogeology.usgs.gov, and the USGS privacy and disclaimers on
 *   http://www.usgs.gov/privacy.html.
 */

#include <memory>

#include <QSharedPointer>
#include "SmtkStack.h"
#include "Gruen.h"
#include "SmtkPoint.h"

#include "GSLUtility.h"
#include <gsl/gsl_rng.h>

namespace Isis {

/**
 * @brief Workhorse of stereo matcher
 *
 * This class provides stereo matching functionality to the SMTK toolkit.  It
 * registers points, clones them by adjusting parameters to nearby point
 * locations and manages point selection processes.
 *
 * The Gruen algorithm is initialized here and maintained for use in the
 * stereo matching process.
 *
 * @author 2011-05-28 Kris Becker
 *
 * @internal
 *   @history 2012-12-20 Debbie A. Cook - Removed unused Projection.h
 *                           References #775.
 *   @history 2017-08-18 Summer Stapleton, Ian Humphrey, Tyler Wilson -
 *                           Changed auto_ptr reference to QSharedPointer
 *                           so this class compiles under C++14.
 *                           References #4809.
 */
class SmtkMatcher {
  public:
    SmtkMatcher();
    SmtkMatcher(const QString &regdef);
    SmtkMatcher(const QString &regdef, Cube *lhImage, Cube *rhImage);
    SmtkMatcher(Cube *lhImage, Cube *rhImage);
    ~SmtkMatcher();

    void setImages(Cube *lhImage, Cube *rhImage);
    void setGruenDef(const QString &regdef);

    bool isValid(const Coordinate &pnt);
    bool isValid(const SmtkPoint &spnt);

    /** Return pattern chip */
    Chip *PatternChip() const {
      validate();
      return (m_gruen->PatternChip());
    }

    /** Return search chip */
    Chip *SearchChip() const {
      validate();
      return (m_gruen->SearchChip());
    }

    /** Returns the fit chip */
    Chip *FitChip() const {
      validate();
      return (m_gruen->FitChip());
    }

    void setWriteSubsearchChipPattern(const QString &fileptrn = "SmtkMatcher");

    SmtkQStackIter FindSmallestEV(SmtkQStack &stack);
    SmtkQStackIter FindExpDistEV(SmtkQStack &stack, const double &seedsample,
                                 const double &minEV, const double &maxEV);

    SmtkPoint Register(const Coordinate &lpnt,
                       const AffineRadio &affrad = AffineRadio());
    SmtkPoint Register(const PointPair &pnts,
                       const AffineRadio &affrad = AffineRadio());
    SmtkPoint Register(const SmtkPoint &spnt,
                       const AffineRadio &affrad = AffineRadio());
    SmtkPoint Register(const PointGeometry &lpg, const PointGeometry &rpg,
                       const AffineRadio &affrad  = AffineRadio());

    SmtkPoint Create(const Coordinate &left, const Coordinate &right);
    SmtkPoint Clone(const SmtkPoint &point, const Coordinate &left);

    inline BigInt OffImageErrorCount() const { return (m_offImage);  }
    inline BigInt SpiceErrorCount() const { return (m_spiceErr);  }

    /** Return Gruen template parameters */
    PvlGroup RegTemplate() { return (m_gruen->RegTemplate());  }
    /** Return Gruen registration statistics */
    Pvl RegistrationStatistics() { return (m_gruen->RegistrationStatistics()); }

  private:
    SmtkMatcher &operator=(const SmtkMatcher &matcher); // Assignment disabled
    SmtkMatcher(const SmtkMatcher &matcher);            // Copy const disabled

    Cube     *m_lhCube;                // Left image cube (not owned)
    Cube     *m_rhCube;                // Right image cube (not owned)
    QSharedPointer<Gruen> m_gruen;     // Gruen matcher
    BigInt  m_offImage;                // Offimage counter
    BigInt  m_spiceErr;                // SPICE distance error
    bool    m_useAutoReg;              // Select AutoReg features
    const gsl_rng_type * T;            // GSL random number type
    gsl_rng * r;                       // GSL random number generator

    void randomNumberSetup();
    bool validate(const bool &throwError = true) const;

    inline Camera &lhCamera() { return (*m_lhCube->camera());   }
    inline Camera &rhCamera() { return (*m_rhCube->camera());   }

    Coordinate getLineSample(Camera &camera, const Coordinate &geom);
    Coordinate getLatLon(Camera &camera, const Coordinate &pnt);

    bool inCube(const Camera &camera, const Coordinate &point) const;

    SmtkPoint makeRegisteredPoint(const PointGeometry &left,
                                  const PointGeometry &right, Gruen *gruen);
};
} // namespace Isis

#endif


/*
 * fvector.h
 *
 *  Created on: Feb 4, 2014
 *      Author: vbonnici
 */

#ifndef FVECTOR_H_
#define FVECTOR_H_

#include <limits>
#include <vector>
#include "data_ts.h"

#include <gsl/gsl_rng.h>

using namespace dolierlib;

namespace dolierlib{

/*
 * shuffle sequences... remember to swap also their lengths
 */
void shuffle(
		std::vector<dna5_t*> &f_sequences,
		std::vector<usize_t> &f_lengths,
		int ntimes)
{
	srand( (unsigned)time(NULL) );
	size_t f,s;
	//dna5_t *tmp_s; usize_t tmp_l;
	for(int i=0; i<ntimes; i++){
		f = static_cast<size_t>(ceil((rand()/(RAND_MAX +1.0))*(static_cast<double>(f_sequences.size() -1))));
		s = static_cast<size_t>(ceil((rand()/(RAND_MAX +1.0))*(static_cast<double>(f_sequences.size() -1))));
//		std::cout<<f<<"\t"<<s<<"\n";
		std::swap(f_sequences[f], f_sequences[s]);
		std::swap(f_lengths[f], f_lengths[s]);
	}
}

/*
 * swap randomly the first leading_possequences with a forward position
 */
void leading_shuffle(
		std::vector<dna5_t*> &f_sequences,
		std::vector<usize_t> &f_lengths,
		size_t leading_pos)
{
	srand( (unsigned)time(NULL) );

	size_t f;
	for(size_t i=0; i<leading_pos; i++){
		f = static_cast<size_t>(ceil((rand()/(RAND_MAX))*(static_cast<double>(f_sequences.size()- leading_pos -1)))) + leading_pos;
		//f = static_cast<size_t>((gsl_rng_uniform (r) * static_cast<double>(f_sequences.size() - leading_pos))) + leading_pos
//		std::cout<<i<<"\t"<<f<<"\n";

//		print_dna5(f_sequences[i], f_lengths[i]);std::cout<<"\t";print_dna5(f_sequences[f], f_lengths[f]);std::cout<<"\n";

		std::swap(f_sequences[i], f_sequences[f]);
		std::swap(f_lengths[i], f_lengths[f]);

//		print_dna5(f_sequences[i], f_lengths[i]);std::cout<<"\t";print_dna5(f_sequences[f], f_lengths[f]);std::cout<<"\n";
	}

}

/*
 * Get kmers spectra.
 * Only non-zero columns are reported.
 * Let X to be the length of the longest input sequences, then theoretically we have sum_{i=1}^{i<=X}(4^i) features (possible kmers)
 * but some of these theoretical kmers do not appear in the input sequences, so they are excluded from the output spectra.
 * Moreover, if sequences are all of the same length, the working X is X-1.
 *
 */
void fvector(
		std::vector<dna5_t*> &f_sequences,
		std::vector<usize_t> &f_lengths,
		double ***vectors,  //output spectra, it's pointer
		size_t *vector_length, //length of spectra, it's a pointer
		double **weights
		)
{

	DNA5MS_t f_ms(true);
	concat(f_ms, f_sequences, f_lengths, false);
	f_ms.areUndefTeminated = true;

	CsFullyContainer f_ds(true);
		build_fully_ds(f_ms, f_ds);
		//build_strict_ds(f_ms, f_ds);
	Cs5DLIndex f_dlindex(f_ms.seq, f_ms.seq_length, f_ds.SA, f_ms.lengths, f_ms.nof_seqs);
	SASearcher sas(f_ms.seq, f_ds.SA, f_ms.seq_length);

	size_t max_length = 0;
	for(size_t i=0; i<f_lengths.size(); i++){
		if(f_lengths[i] > max_length)
			max_length = f_lengths[i];
	}

	size_t nof_features = 0;
	size_t *nof_kfeatures = new size_t[max_length+1];
	for(size_t i=1; i<=max_length; i++){
		NSAIterator it = NSAIterator::begin(f_ms, f_ds, i);
		nof_kfeatures[i] = nof(it);
		nof_features += nof_kfeatures[i];

		std::cout<<i<<"\t"<<nof_kfeatures[i]<<"\t"<<f_sequences.size()<<"\n";

		//if sequences are all of the same length, the working X is X-1.
		if(nof_kfeatures[i] == f_sequences.size()  &&  i<=max_length){
			if(i>1){
				std::cout<<"#\n";
				max_length = i-1;
				nof_features -= nof_kfeatures[i];
			}
			else{
				max_length = i;
			}
			break;
		}
	}

	//double **table = new double*[f_sequences.size()];
	(*vectors) =  new double*[f_sequences.size()];
	for(size_t i=0; i<f_sequences.size(); i++){
		(*vectors)[i] =  new double[nof_features];
		for(size_t j=0; j<nof_features; j++)
			(*vectors)[i][j] = 0;
	}
	(*weights) =  new double[nof_features];

	size_t c_nf = 0;
	size_t ii;
	for(size_t k=1; k<=max_length; k++){
		NSAIterator it = NSAIterator::begin(f_ms, f_ds, k);
		dna5_t *kmer  = new dna5_t[k];
		while(it.next()){
			it.get_kmer(kmer);
//			std::cout<<c_nf<<"\t";print_dna5(kmer,k); std::cout<<"\n";

			for(ii=it.i_start; ii<it.i_end; ii++){
				(*vectors)[f_dlindex.ss_ids[ii]][c_nf]++;
				(*weights)[c_nf] = k;
			}
			c_nf++;
		}
	}

	*vector_length = nof_features;
}

/*
 * normalize vectors by column, from [0,max] to [min,max]/(max-min)
 */
void normalize(double **matrix, size_t size1, size_t size2)
{
	for(size_t i=0; i<size2; i++){
		double min = std::numeric_limits<double>::max();
		double max = std::numeric_limits<double>::min();

		for(size_t j=0; j<size1; j++){
			if(matrix[j][i]< min)
				min = matrix[j][i];
			if(matrix[j][i] > max)
				max = matrix[j][i];
		}

		double f = max - min;
		for(size_t j=0; j<size1; j++){
			matrix[j][i] = (matrix[j][i] - min) / f;
		}
	}
}

/*
 * normalize vectors by column, from [0,max] to [0,max]/(max)
 */
void normalize_by_sum(double **matrix, size_t size1, size_t size2)
{
	for(size_t i=0; i<size2; i++){
		double sum = 0;
		for(size_t j=0; j<size1; j++){
			sum += matrix[j][i];
		}
		for(size_t j=0; j<size1; j++){
			matrix[j][i] /= sum;
		}
	}
}

/*
 * Keep only those columns having at least one element >= min_value.
 * Unselected columns are putted at the end of the matrix.
 */
void keep_only(
		double **matrix, //input matrix
		size_t size1, //number of rows
		size_t size2, //number of columns
		double min_value, //min value for selection
		size_t *new_size2 //result number of columns
		)
{
	size_t to_size = size2;
	size_t to_pos = 0;
	for(size_t j=0; j<size2; j++){
		bool keep = false;
		for(size_t i=0; i<size1; i++){
			if(matrix[i][j] >= min_value){
				keep = true;
				break;
			}
		}

		if(keep){
			if(j != to_pos){
				for(size_t i=0; i<size1; i++){
					matrix[i][to_pos] = matrix[i][j];
				}
			}
			to_pos++;
		}
		else{
			to_size--;
		}
	}
	*new_size2 = to_size;
}

/*
 * Same behaviour of keep_only, but it swap the weights matrix, too.
 */
void keep_only(double **matrix, size_t size1, size_t size2, double min_value, size_t *new_size2, double *weights)
{
	size_t to_size = size2;
	size_t to_pos = 0;
	for(size_t j=0; j<size2; j++){
		bool keep = false;
		for(size_t i=0; i<size1; i++){
			if(matrix[i][j] >= min_value){
				keep = true;
				break;
			}
		}

		if(keep){
			if(j != to_pos){
				for(size_t i=0; i<size1; i++){
					matrix[i][to_pos] = matrix[i][j];
				}
				weights[to_pos] = weights[j];
			}
			to_pos++;
		}
		else{
			to_size--;
		}
	}
	*new_size2 = to_size;
}

/*
 * Theoretical spectra are vectors if length sum_{i=1}^{i<=X}(4^i).
 * Thus, weights[i] correspond to the specific length i of the releated theoretical kmer.
 *
 */
void get_fvectors_weights(double *weights, size_t length, std::vector<dna5_t*> &f_sequences, std::vector<usize_t> &f_lengths){
//	size_t base = 0;
//	size_t ebase = 0;
//	size_t cbase = 0;
//	for(size_t i=0;i<length; i++){
//		if(cbase == ebase){
//			base++;
//			ebase = static_cast<size_t>(pow(4, base));
//			cbase = 0;
//		}
//		weights[i] = base;
//		cbase++;
//	}

	DNA5MS_t f_ms(true);
	concat(f_ms, f_sequences, f_lengths, false);
	f_ms.areUndefTeminated = true;

	CsFullyContainer f_ds(true);
		build_fully_ds(f_ms, f_ds);
		//build_strict_ds(f_ms, f_ds);
	Cs5DLIndex f_dlindex(f_ms.seq, f_ms.seq_length, f_ds.SA, f_ms.lengths, f_ms.nof_seqs);
	SASearcher sas(f_ms.seq, f_ds.SA, f_ms.seq_length);

	size_t max_length = 0;
	for(size_t i=0; i<f_lengths.size(); i++){
		if(f_lengths[i] > max_length)
			max_length = f_lengths[i];
	}

	size_t nof_features = 0;
	size_t *nof_kfeatures = new size_t[max_length+1];
	for(size_t i=1; i<=max_length; i++){
		NSAIterator it = NSAIterator::begin(f_ms, f_ds, i);
		nof_kfeatures[i] = nof(it);
		nof_features += nof_kfeatures[i];

		std::cout<<i<<"\t"<<nof_kfeatures[i]<<"\t"<<f_sequences.size()<<"\n";

		//if sequences are all of the same length, the working X is X-1.
		if(nof_kfeatures[i] == f_sequences.size()  &&  i<=max_length){
			if(i>1){
				std::cout<<"#\n";
				max_length = i-1;
				nof_features -= nof_kfeatures[i];
			}
			else{
				max_length = i;
			}
			break;
		}
	}

	size_t c_nf = 0;
	size_t ii;
	for(size_t k=1; k<=max_length; k++){
		NSAIterator it = NSAIterator::begin(f_ms, f_ds, k);
		dna5_t *kmer  = new dna5_t[k];
		while(it.next()){
			it.get_kmer(kmer);
//			std::cout<<c_nf<<"\t";print_dna5(kmer,k); std::cout<<"\n";
			for(ii=it.i_start; ii<it.i_end; ii++){
				weights[c_nf] = k;
			}
			c_nf++;
		}
	}
}

void print_matrix(double **matrix, size_t size1, size_t size2){
	for(size_t i=0; i<size1; i++){
		for(size_t j=0; j<size2; j++){
			std::cout<<matrix[i][j]<<" ";
		}
		std::cout<<"\n";
	}
}

}

#endif /* FVECTOR_H_ */


// MIT License Copyright (c) 2020 Jarrett Wendt

#pragma once

#include "Macros.h"
#include "Memory.h"
#include "Util.h"

#include <algorithm>			// std::min/max
#include <compare>				// std::strong_ordering
#include <functional>
#include <initializer_list>		// std::initializer/list
#include <iterator>				// std::random_access_iterator

#include <gsl/gsl>				// gsl::owner

// Some macros to save on typing.
// These get #undef-ed at the end of the .inl file.
// Also pretty convenient in case I ever decide I want to change/rename the template args.
// The only downside is that intellisense thinks my methods are unimplemented.
#define TEMPLATE template<typename T, Concept::ReserveStrategy ReserveStrategy>
#define ARRAY Array<T, ReserveStrategy>

namespace Library
{
	// TODO: Array<bool> can be specialized as a bit-array to save memory.

	/**
	 * A contiguous-memory dynamically resizing array.
	 * Internally uses std::vector and provides more convenience methods to it.
	 * Name chosen to disambiguate between vector the data container and vector the mathematical concept.
	 *
	 * @param <T>					The type for this container to store.
	 * @param <ReserveStrategy>		A callable type for determining a new capacity given the current memory usage.
	 */
	template<typename T, Concept::ReserveStrategy ReserveStrategy = Util::DefaultReserveStrategy>
	class Array final
	{
	public:
		using value_type = T;
		using reference = T&;
		using const_reference = const T&;
		using pointer = T*;
		using const_pointer = const T*;
		using size_type = size_t;
		using difference_type = ptrdiff_t;
		using reserve_strategy = ReserveStrategy;

	private:
		using internal_size_type = uint32_t;

		T* array{ nullptr };
		internal_size_type size{ 0 };
		internal_size_type capacity{ 0 };

	public:
#pragma region Special Members
		/**
		 * default ctor
		 * creates an empty container owning no memory
		 */
		Array() = default;

		/**
		 * explicit ctor
		 *
		 * @param capacity		how many elements worth of space to reserve
		 */
		explicit Array(size_type capacity);

		/**
		 * explicit ctor
		 *
		 * @param count			how many elements of space to reserve and construct
		 * @param prototype		prototypical value to be copy-constructed into each element
		 */
		Array(size_type count, const T& prototype);

		/**
		 * explicit ctor
		 * Fills the container with values read through the iterators.
		 *
		 * @param <It>			the iterator type
		 * @param first			the beginning iterator, inclusive
		 * @param last			the last iterator, exclusive
		 */
		template<std::forward_iterator It>
		Array(It first, It last);

		/**
		 * explicit ctor
		 * Fills the container with values read through the iterators.
		 *
		 * @param <It>			the iterator type
		 * @param first			the beginning iterator, inclusive
		 * @param last			the last iterator, exclusive
		 */
		template<std::random_access_iterator It>
		Array(It first, It last);

		/**
		 * typecast ctor which accepts any range
		 * will at most resize once
		 *
		 * @param range			the range to construct from
		 */
		template<Concept::RangeOf<T> Range>
		Array(const Range& range);

		/**
		 * typecast ctor which accepts any range
		 * will at most resize once
		 *
		 * @param range			the range to construct from
		 */
		template<Concept::RangeOf<T> Range>
		Array& operator=(const Range& range);

		/**
		 * initializer_list ctor
		 *
		 * @param list		the initializer_list to construct this container from
		 */
		Array(std::initializer_list<T> list);

		/**
		 * initializer_list operator=
		 *
		 * @param list		the initializer_list to assign this container to
		 * @return			this container after assignment
		 */
		Array& operator=(std::initializer_list<T> list);

		/**
		 * copy ctor
		 *
		 * @param other		the container to copy
		 */
		Array(const Array& other);

		/**
		 * move ctor
		 *
		 * @param other		the container to move, will be made empty after this operation
		 */
		Array(Array&& other) noexcept;

		/**
		 * copy operator=
		 *
		 * @param other		the container to move, will be made empty after this operation
		 * @returns			this container after assignment
		 */
		Array& operator=(const Array& other);

		/**
		 * move operator=
		 *
		 * @param other		the container to move, will be made empty after this operation
		 * @returns			this container after assignment
		 */
		Array& operator=(Array&& other) noexcept;

		/**
		 * templated copy ctor accepting containers with other ReserveStrategies
		 * this container will keep its original reserve strategy
		 *
		 * @param other		the container to move, will be made empty after this operation
		 * @returns			this container after assignment
		 */
		template<Concept::ReserveStrategy OtherReserveStrategy>
		Array(const Array<T, OtherReserveStrategy>& other);

		/**
		 * templated move ctor accepting containers with other ReserveStrategies
		 * this container will keep its original reserve strategy
		 *
		 * @param other		the container to move, will be made empty after this operation
		 * @returns			this container after assignment
		 */
		template<Concept::ReserveStrategy OtherReserveStrategy>
		Array(Array<T, OtherReserveStrategy>&& other) noexcept;

		/**
		 * templated copy operator= accepting containers with other ReserveStrategies
		 * this container will keep its original reserve strategy
		 *
		 * @param other		the container to move, will be made empty after this operation
		 * @returns			this container after assignment
		 */
		template<Concept::ReserveStrategy OtherReserveStrategy>
		Array& operator=(const Array<T, OtherReserveStrategy>& other);

		/**
		 * templated move operator= accepting containers with other ReserveStrategies
		 * this container will keep its original reserve strategy
		 *
		 * @param other		the container to move, will be made empty after this operation
		 * @returns			this container after assignment
		 */
		template<Concept::ReserveStrategy OtherReserveStrategy>
		Array& operator=(Array<T, OtherReserveStrategy>&& other) noexcept;

		/**
		 * dtor
		 * frees all associated memory
		 */
		~Array();
#pragma endregion

#pragma region iterator
		class iterator final
		{
			friend class Array;
			friend class const_iterator;

		private:
			T* data{ nullptr };
#ifdef _DEBUG
			Array* owner{ nullptr };
#endif

		public:
			using iterator_category = std::random_access_iterator_tag;
			using value_type = T;
			using difference_type = ptrdiff_t;
			using pointer = T*;
			using reference = T&;

		private:
			iterator(T* data, [[maybe_unused]] Array* owner) noexcept :
				data(data)
#ifdef _DEBUG
				,owner(owner)
#endif
			{}

		public:
			/**
			 * explicit constructor
			 *
			 * @param index		index for this iterator to start at
			 * @param owner		the container this iterator references
			 */
			iterator(const size_type index, Array& owner) noexcept :
				iterator(owner.array + index, &owner) {}

			SPECIAL_MEMBERS(iterator, default)

			/**
			 * Difference of two iterators.
			 *
			 * @param left		lhs of the operator
			 * @param right		rhs of the operator
			 * @returns			The difference between the indices of the two iterators.
			 *
			 * @asserts			The iterators are initialized and they belong to the same container.
			 */
			[[nodiscard]] friend difference_type operator-(const iterator left, const iterator right) noexcept
			{
				// No need to AssertInitialize anything because it will be called by operator +=
				// AssertInitialize is therefore implicitly called on right since we're comparing it to left's owner
				assertm(left.owner == right.owner, "iterators must belong to the same container");
				return left.data - right.data;
			}

			/**
			 * Will not go beyond end().
			 *
			 * @param i		How much to increment the iterator by.
			 * @returns		A new iterator at that index.
			 *
			 * @asserts		this iterator is initialized
			 */
			[[nodiscard]] iterator operator+(difference_type i) const noexcept;

			/**
			 * @returns		The value of the index the iterator is at.
			 *
			 * @asserts		this iterator is initialized and not at end().
			 */
			[[nodiscard]] reference operator*() const;

			RANDOM_ITER_OPS(iterator)

		private:
			/**
			 * @asserts		this iterator is initialized.
			 */
			constexpr void AssertInitialized() const noexcept;
		};

		class const_iterator final
		{
			friend class Array;
			CONST_RANDOM_ACCESS_ITERATOR(const_iterator, iterator)

			/**
			 * explicit constructor
			 *
			 * @param index		index for this iterator to start at
			 * @param owner		the container this iterator references
			 */
			const_iterator(const size_t index, const Array& owner) noexcept :
				it{ index, const_cast<Array&>(owner) } {};
		};

		BEGIN_END(iterator, const_iterator, Array)
		MAKE_REVERSE_BEGIN_END
#pragma endregion

#pragma region Properties
		/**
		 * @returns		true if the container is empty, false otherwise
		 */
		[[nodiscard]] constexpr bool IsEmpty() const noexcept;

		/**
		 * @returns		true if the container is full, false otherwise
		 */
		[[nodiscard]] constexpr bool IsFull() const noexcept;

		/**
		 * @returns		How many elements are in the container.
		 */
		[[nodiscard]] constexpr size_type Size() const noexcept;

		/**
		 * @returns		The number of elements this container can hold without resizing.
		 */
		[[nodiscard]] constexpr size_type Capacity() const noexcept;

		/**
		 * @param it	an iterator
		 * @returns		the index represented by the iterator
		 */
		[[nodiscard]] constexpr size_type IndexOf(const_iterator it) const noexcept;
#pragma endregion

#pragma region Element Access
		/**
		 * Returns a reference to the given index with bounds checking.
		 *
		 * @param index		position of the element to return
		 * @returns			reference to the requested element
		 *
		 * @throws std::out_of_range	if index >= Size()
		 */
		[[nodiscard]] T& At(size_type index);

		/**
		 * Returns a reference to the given index with bounds checking.
		 *
		 * @param index		position of the element to return
		 * @returns			reference to the requested element
		 *
		 * @throws std::out_of_range	if index >= Size()
		 */
		[[nodiscard]] const T& At(size_type index) const;

		/**
		 * Returns a reference to the given index with bounds checking.
		 *
		 * @param index		position of the element to return
		 * @returns			reference to the requested element
		 *
		 * @throws std::out_of_range	if index >= Size()
		 */
		[[nodiscard]] T& operator[](size_type index);

		/**
		 * Returns a reference to the given index with bounds checking.
		 *
		 * @param index		position of the element to return
		 * @returns			reference to the requested element
		 *
		 * @throws std::out_of_range	if index >= Size()
		 */
		[[nodiscard]] const T& operator[](size_type index) const;

		/**
		 * @returns		reference to the first element.
		 *
		 * @throws std::out_of_range	if the container is empty
		 */
		[[nodiscard]] T& Front();

		/**
		 * @returns		reference to the first element.
		 *
		 * @throws std::out_of_range	if the container is empty
		 */
		[[nodiscard]] const T& Front() const;

		/**
		 * @returns		reference to the last element.
		 *
		 * @throws std::out_of_range	if the container is empty
		 */
		[[nodiscard]] T& Back();

		/**
		 * @returns		reference to the last element.
		 *
		 * @throws std::out_of_range	if the container is empty
		 */
		[[nodiscard]] const T& Back() const;
#pragma endregion

#pragma region Insert
		/**
		 * @param index		where the new element will be constructed
		 * @param t			value to insert
		 */
		void Insert(size_t index, const T& t);

		/**
		 * @param index		where the new element will be constructed
		 * @param t			value to insert
		 */
		void Insert(size_t index, T&& t);

		/**
		 * @param index		where the new element will be constructed
		 * @param count		how many copies of the value to insert
		 * @param t			value to insert
		 * @returns			iterator at the position of the last element inserted
		 */
		iterator Insert(size_t index, size_t count, const T& t);

		/**
		 * @param index		where the new element will be constructed
		 * @param list		initializer_list of values to insert
		 * @returns			iterator at the position of the last element inserted
		 */
		iterator Insert(size_t index, std::initializer_list<T> list);

		/**
		 * Insert elements through the values read from the iterators in the range [first, last).
		 *
		 * @param index		where the first new element will be constructed
		 * @param first		beginning iterator, inclusive
		 * @param last		ending iterator, exclusive
		 * @returns			iterator at the position of the last element inserted
		 *
		 * @throws			std::invalid_argument if the iterators belong to this container
		 */
		template<std::forward_iterator It>
		iterator Insert(size_t index, It first, It last);

		/**
		 * @param it		position of where to insert
		 * @param t			prototypical value to insert
		 *
		 * @throws std::invalid_argument	if the iterator does not belong to this container
		 */
		void Insert(const_iterator it, const T& t);

		/**
		 * @param it		position of where to insert
		 * @param t			prototypical value to insert
		 *
		 * @throws std::invalid_argument	if the iterator does not belong to this container
		 */
		void Insert(const_iterator it, T&& t);

		/**
		 * @param it		initial position of where to insert
		 * @param count		how many copies of the prototype to insert
		 * @param t			prototypical value to insert
		 * @returns			iterator at the last element inserted
		 *
		 * @throws std::invalid_argument	if the iterator does not belong to this container
		 */
		iterator Insert(const_iterator it, size_t count, const T& t);

		/**
		 * @param it		initial position of where to insert
		 * @param list		initializer_list of values to insert
		 * @returns			iterator at the last element inserted
		 *
		 * @throws std::invalid_argument	if the iterator does not belong to this container
		 */
		iterator Insert(const_iterator it, std::initializer_list<T> list);

		/**
		 * Insert elements through the values read from the iterators in the range [first, last).
		 *
		 * @param it		initial position of where to insert
		 * @param first		beginning iterator, inclusive
		 * @param last		ending iterator, exclusive
		 * @returns			iterator at the position of the last element inserted
		 *
		 * @throws			std::invalid_argument if the iterators do not belong to this container
		 */
		template<std::forward_iterator It>
		iterator Insert(const_iterator it, It first, It last);

		/**
		 * Inserts a new element into the container directly at index.
		 *
		 * @param index		Position in the container to put this element.
		 * @param args		Arguments to forward to the constructor of the element.
		 *
		 * @throws std::out_of_range	if index > Size().
		 */
		template<typename... Args>
		reference Emplace(size_t index, Args&&... args);

		/**
		 * Inserts a new element into the container directly at index.
		 * The passed iterator is guaranteed to be valid and pointing at the position of the newly emplaced element after the operation.
		 *
		 * @param it		Position in the container to put this element.
		 * @param args		Arguments to forward to the constructor of the element.
		 *
		 * @throws std::out_of_range	if index > Size().
		 */
		template<typename... Args>
		reference Emplace(const_iterator it, Args&&... args);

		/**
		 * Appends a new element to the end of the container.
		 * O(1) most cases.
		 * O(n) if the operation requires a Resize().
		 *
		 * @param args		Arguments to forward to the constructor of the element.
		 */
		template<typename... Args>
		reference EmplaceBack(Args&&... args);

		/**
		 * Appends a new element to the front of the container.
		 * O(1) most cases.
		 * O(n) if the operation requires a Resize().
		 *
		 * @param args		Arguments to forward to the constructor of the element.
		 */
		template<typename... Args>
		reference EmplaceFront(Args&&... args);

		/**
		 * Appends the element to the end of the container.
		 * O(1) most cases.
		 * O(n) if the operation requires a Resize().
		 *
		 * @param t		The value of the element to append.
		 */
		void PushBack(const T& t);

		/**
		 * Appends the element to the end of the container.
		 * O(n) where n is Size().
		 *
		 * @param t		The value of the element to append.
		 */
		void PushBack(T&& t);

		/**
		 * Appends the element to the front of the container.
		 * O(n) where n is Size().
		 *
		 * @param t		The value of the element to prepend.
		 */
		void PushFront(const T& t);

		/**
		 * Appends the element to the front of the container.
		 * O(n) where n is Size().
		 *
		 * @param t		The value of the element to prepend.
		 */
		void PushFront(T&& t);

		/**
		 * Insert elements through the values read from the iterators in the range [first, last).
		 * Only resizes once (this is most efficient if It is a random access iterator).
		 *
		 * @param it		initial position of where to insert
		 * @param first		beginning iterator, inclusive
		 * @param last		ending iterator, exclusive
		 * @returns			iterator at the position of the last element inserted
		 */
		template<std::forward_iterator It>
		void PushBack(It first, It last);

		/**
		 * Insert elements through the values read from the iterators in the range [first, last).
		 * Only resizes once (this is most efficient if It is a random access iterator).
		 *
		 * @param it		initial position of where to insert
		 * @param first		beginning iterator, inclusive
		 * @param last		ending iterator, exclusive
		 * @returns			iterator at the position of the last element inserted
		 */
		template<std::forward_iterator It>
		void PushFront(It first, It last);

		/**
		 * Copies the passed prototypical value into the remaining empty slots.
		 * O(n) where n is Capacity() - Size().
		 *
		 * @param prototype		The prottype to fill the container with.
		 */
		void Fill(const T& prototype);
#pragma endregion

#pragma region Remove
		/**
		 * Removes the first instance of the passed value from the container.
		 * Will only return false if the value did not exist in the container.
		 * A return value of true does not imply that no duplicate values do not still exist in the container.
		 * All elements after the removed one get shifted to the left in memory.
		 * O(n)
		 *
		 * @param t		The value to remove.
		 * @returns		Whether or not a removal operation was performed.
		 */
		bool Remove(const T& t);

		/**
		 * Removes the first element matching the passed predicate.
		 * Will only return false if the value did not exist in the container.
		 * A return value of true does not imply that no duplicate values do not still exist in the container.
		 * All elements after the removed one get shifted to the left in memory.
		 * O(n)
		 *
		 * @param predicate		The predicate to query with.
		 * @returns				Whether or not a removal was performed.
		 */
		template<std::predicate<T> Predicate>
		bool Remove(Predicate predicate);

		/**
		 * Removes the index specified and shift values to the left over.
		 * O(n) where n is Size() - index;
		 *
		 * @param index		The index to remove.
		 */
		void RemoveAt(size_t index);

		/**
		 * Removes all elements matching the specified value.
		 * O(n)
		 *
		 * @param t		The value to remove all instances of.
		 * @returns		How many removals were performed.
		 */
		size_t RemoveAll(const T& t);

		/**
		 * Removes all elements matching the passed predicate.
		 * O(n)
		 *
		 * @param predicate		The predicate to query with.
		 * @returns				How many elements were removed.
		 */
		template<std::predicate<T> Predicate>
		size_t RemoveAll(Predicate predicate);

		template<std::predicate<T> Predicate>
		friend size_t erase_if(Array& arr, const Predicate predicate)
		{
			return arr.RemoveAll(predicate);
		}

		/**
		 * Removes the range [first, last) elements between the two passed indices.
		 * If last is beyond Size() then all elements from first to the end will be removed safely.
		 * Does nothing if the indices are both beyond Size().
		 *
		 * @param first		index of the first value to remove, inclusive.
		 * @param last		index of the last value to remove, exclusive.
		 * @returns			The number of elements removed, equal to first - last unless last > Size().
		 *
		 * @throws std::invalid_argument	if first > last.
		 */
		size_t Remove(size_t first, size_t last);

		/**
		 * Removes the range [first, last) elements between the two passed iterators.
		 * O(n) where n = Size() - first
		 *
		 * @param first		iterator at the first element to remove, inclusive
		 * @param last		iterator at the last element to remove, exclusive
		 * @returns			The number of elements removed.
		 */
		size_t Remove(const_iterator first, const_iterator last);

		/**
		 * O(1)
		 * Removes the last element of the container.
		 * Does nothing if the container is empty.
		 */
		void PopBack();

		/**
		 * O(n) where n = Size() - 1 before the operation.
		 * Removes the first element of the container.
		 * Does nothing if the container is empty.
		 */
		void PopFront();

		/**
		 * Erases all elements from the container by calling the destructors on each of them.
		 * Afterwards Size() == 0.
		 */
		void Clear() noexcept;

		/**
		 * Erases all memory held by this container.
		 */
		void Empty() noexcept;
#pragma endregion

#pragma region Query
		/**
		 * Does an O(n) search for the first occurrence of the passed element.
		 *
		 * @param t				the element to query for.
		 * @returns				the index of the first element found, or Size() if it was not found.
		 */
		size_type IndexOf(const T& t) const;

		/**
		 * Does an O(n) search for the first element matching the passed predicate.
		 *
		 * @param predicate		the predicate to query with.
		 * @returns				the index of the first element found, or Size() if it was not found.
		 */
		template<std::predicate<T> Predicate>
		size_type IndexOf(Predicate predicate) const;
#pragma endregion

#pragma region Memory
		/**
		 * Warning! Use at your own risk! Manipulating this memory can invalidate the state of the Array.
		 *
		 * @returns		pointer to the underlying data. nullptr if the container is empty.
		 */
		[[nodiscard]] T* Data() noexcept;

		/**
		 * Warning! Use at your own risk! Manipulating this memory can invalidate the state of the Array.
		 *
		 * @returns		pointer to the underlying data. nullptr if the container is empty.
		 */
		[[nodiscard]] const T* Data() const noexcept;

		/**
		 * Gets the underlying data and empties this container without deallocating any memory.
		 * Guaranteed to cause a memory leak if the caller discards this return value.
		 * Caller assumes all responsibility for managing this data.
		 *
		 * @returns		pointer, size, capacity
		 */
		[[nodiscard]]
		std::tuple<gsl::owner<T*>, size_t, size_t> TakeData() noexcept;

		/**
		 * Force this container to take on the given memory.
		 * Will free all currently associated memory and destruct all objects.
		 * The container will manage the given data as if it had allocated it itself,
		 * possibly reallocating it in the event of an operation that would do such a thing.
		 *
		 * @param data	pointer, size, capacity
		 */
		void SetData(std::tuple<T*, size_t, size_t> data) noexcept;

		/**
		 * Force this container to take on the given memory.
		 * Will free all currently associated memory and destruct all objects.
		 * The container will manage the given data as if it had allocated it itself,
		 * possibly reallocating it in the event of an operation that would do such a thing.
		 *
		 * @param array		pointer to array of T
		 * @param size		how many elements array contains
		 * @param capacity	how many elements array can store
		 */
		void SetData(T* array, size_t size, size_t capacity) noexcept;

		/**
		 * O(n) where n is the current size of the container.
		 * Does nothing if newCapacity <= Capacity().
		 *
		 * @param newCapacity	The new capacity for the container.
		 *
		 * @throws std::length_error	if newCapacity > MaxSize()
		 * @throws std::bad_alloc		if memory allocation fails.
		 */
		void Reserve(size_t newCapacity);

		/**
		 * O(n) where n = newSize - Size(), or n = newSize if the operation requires a Reserve().
		 * If the current Size() is greater than newSize, the container is reduced to its first newSize elements.
		 *
		 * @param newSize		The new size for the container.
		 * @param prototype		The value to initialize each new element with.
		 */
		void Resize(size_t newSize, const T& prototype = T());

		/**
		 * Reduces memory usage such that Size() == Capacity().
		 * O(n) where n = Size().
		 * Does nothing if IsFull()
		 */
		void ShrinkToFit();

		/**
		 * Reduces memory usage such that Capacity() == std::max(count, Size()).
		 * O(n) where n = Size().
		 * Does nothing if Capacity() already == std::max(count, Size()).
		 */
		void ShrinkToFit(size_t count);

		/**
		 * TODO: It might be that the STL expects swap to be lowercase and a non-member function (so a friend function will do).
		 * O(1)
		 *
		 * @param <OtherReserveStrategy>	template parameter allowing swapping Arrays with different ReserveStrategies.
		 * @param other						container to exchange contents with.
		 */
		template<Concept::ReserveStrategy OtherReserveStrategy>
		void Swap(Array<T, OtherReserveStrategy>& other) noexcept;

		/**
		 * Reverses the order of the elements in the container.
		 * O(n)
		 */
		void Reverse() noexcept;
#pragma endregion

#pragma region Operators
		/**
		 * templated operator== to allow for comparing containers with different reserve strategies
		 *
		 * @param <OtherReserveStrategy>	reserve strategy for the other container
		 * @param left						lhs container
		 * @param right						rhs container
		 * @returns							whether or not the two containers are equal.
		 */
		template<Concept::ReserveStrategy OtherReserveStrategy>
		[[nodiscard]] friend bool operator==(const Array& left, const Array<T, OtherReserveStrategy>& right)
		{
			return &left == reinterpret_cast<const Array*>(&right) || left.Size() == right.Size() && std::equal(left.begin(), left.end(), right.begin(), right.end());
		}

		/**
		 * templated operator== to allow for comparing containers with different reserve strategies
		 *
		 * @param <OtherReserveStrategy>	reserve strategy for the other container
		 * @param left						lhs container
		 * @param right						rhs container
		 * @returns							whether or not the two containers are equal.
		 */
		template<Concept::ReserveStrategy OtherReserveStrategy>
		[[nodiscard]] friend bool operator!=(const Array& left, const Array<T, OtherReserveStrategy>& right)
		{
			return !operator==(left, right);
		}

		friend std::ostream& operator<<(std::ostream& stream, const ARRAY& array) noexcept
		{
			Util::StreamTo(stream, array.begin(), array.end());
			return stream;
		}
#pragma endregion

#pragma region Helpers
	private:
		/**
		 * assumes Capacity() is sufficient
		 *
		 * @param first		the beginning iterator to read from (inclusive)
		 * @param last		the ending iterator to read from (exclusive)
		 */
		template<std::forward_iterator It>
		void PushBackNoResize(It first, It last);

		/**
		 * Helper for methods that accept iterators.
		 *
		 * @param it		The iterator who's owner to compare against this.
		 *
		 * @asserts			owner == this
		 */
		void AssertOwner(iterator it) const;

		/**
		 * @param it		The iterator who's owner to compare against this.
		 *
		 * @asserts			owner == this
		 */
		void AssertOwner(const_iterator it) const;

		/**
		 * Calls the ReserveStrategy template argument and sanity checks its return value.
		 * @returns		A safe new Capacity() for this container.
		 */
		[[nodiscard]] size_type InvokeReserveStrategy() const noexcept;

		/**
		 * Calls destructor on all elements.
		 */
		void DestructAll() noexcept;

		/**
		 * Calls the destructor on all elements in range [first, last).
		 *
		 * @param first		Index of first element to be deconstructed, inclusive.
		 * @param last		Index of last element to be decondstructed, exclusive.
		 */
		void DestructAll(const size_type first, const size_type last) noexcept;

		/**
		 * Shifts the internal array to the right starting at the passed index.
		 * Moves memory as if calling the destructor on elements before they're overwritten and after they're moved.
		 *
		 * @param startIndex	The index to start the shifts at.
		 * @param shiftAmount	How much to shift by.
		 */
		void ShiftRight(size_type startIndex = 0, size_type shiftAmount = 1) noexcept;

		/**
		 * Shifts the internal array to the left starting at the passed index.
		 * Moves memory as if calling the destructor on elements before they're overwritten and after they're moved.
		 *
		 * @param startIndex	The index to start the shifts at.
		 * @param shiftAmount	How much to shift by.
		 */
		void ShiftLeft(size_type startIndex = 0, size_type shiftAmount = 1) noexcept;

		/**
		 * Helper called from constructors.
		 * Assumes size is 0 and capacity is sufficient.
		 *
		 * @param list		list to copy elements from.
		 */
		void Copy(std::initializer_list<T> list);

		/**
		 * Helper called from constructors.
		 * Assumes size is 0 and capacity is sufficient.
		 *
		 * @param list		list to copy elements from.
		 */
		template<typename OtherReserveStrategy>
		void Copy(const Array<T, OtherReserveStrategy>& other);

		/**
		 * Helper called from copy assignment operators.
		 *
		 * @param other		The array being copied.
		 * @returns			this after the copy.
		 */
		template<typename OtherReserveStrategy>
		[[nodiscard]] Array& CopyAssign(const Array<T, OtherReserveStrategy>& other);

		/**
		 * Helper called from move assignment operators.
		 *
		 * @param other		The array being moved.
		 * @returns			this after the move.
		 */
		template<typename OtherReserveStrategy>
		[[nodiscard]] Array& MoveAssign(Array<T, OtherReserveStrategy>&& other);

		/**
		 * Helper called from constructors.
		 * Sets this Array's state after std::move.
		 */
		void SetPostMoveState();
#pragma endregion
	};
}

#include "Array.inl"


#ifdef __GNUC__
	#define LIBKLR_API
#else
	#ifdef LIBKLR_EXPORTS
	#define LIBKLR_API __declspec(dllexport)
	#else
	#define LIBKLR_API __declspec(dllimport)
	#endif
#endif

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#ifdef __APPLE__
#include <Accelerate/Accelerate.h>
#else
#include <cblas.h>
#endif

/* Matrix manipulation */
#define MAT(X, row, i, j) ((X)[(i)+(j)*row])

class LIBKLR_API Clibklr {
  int class_num;        /* Number of classes */
  int sample_num;       /* Number of tatal MFCC's */
  int itrKLR0;          /* KLR0 iteration*/
  int itrCG;            /* Conjugate Gradient iteration */
  int itrNewton;        /* Newton method iteration*/
  double tparam;        /* if the error of gradient is less than tparam, stop CG*/
  double *weight;       /* Importance Weight*/
  double delta;         /* Generalization parameter for KLR*/
  double *Vparam;       /* KLR parameter*/
public:
	Clibklr(int c, int n);

	void set_class_num(int temp){class_num = temp;}
	void set_sample_num(int temp){sample_num = temp;}
	void set_itrKLR0(int temp){itrKLR0 = temp;}
	void set_itrCG(int temp){itrCG = temp;}
	void set_itrNewton(int temp){itrNewton = temp;}
	void set_tparam(double temp){tparam = temp;}
	void set_delta(double temp){delta = temp;}

	int get_class_num(void){return class_num;}
	int get_sample_num(void){return sample_num;}
	int get_itrKLR0(void){return itrKLR0;}
	int get_itrCG(void){return itrCG;}
	int get_itrNewton(void){return itrNewton;}
	double get_tparam(void){return tparam;}
	double get_delta(void){return delta;}

	void set_weight(double *inweight){
	  for(int i = 0; i < sample_num; i++){
	    weight[i] = inweight[i];
	  }
	}

	void get_V(double* Vexp){
	  for(int i = 0; i < class_num; i++){
	    for(int j = 0; j < sample_num; j++){
	      Vexp[(i)+(j)*class_num] = Vparam[(i)+(j)*class_num];
			}
		}
	}

	/*  Train Kernel Logistic Regression */
	void train(double *Ktrain, int *label);
	/* Test KLR (vector kernel)*/
	double* test(double *Ktest, int n_test, double* V, int c, int n_train);
	/* Conjugate Gradient Method*/
	void CG(double*,double*, double*,double*,int,int,double*,int);
	/* Label matrix generation*/
	void yconst(int*, double*, int, int);
	/* Logistic Transform*/
	void mlogistic(double*, int, int);
	/* Norm computation */
	double norm(double *input, int c, int n);
	int* malloc_int(int n);
	double* malloc_double(int n);
	int display_array(double*, const int, const int);
};

extern LIBKLR_API int nlibklr;

LIBKLR_API int fnlibklr(void);


#pragma once

#include "halley/text/halleystring.h"
#include <thread>
#include <gsl/span>
#include <atomic>

#include "halley/data_structures/hash_map.h"
#include "halley/time/halleytime.h"

namespace Halley {
	enum class ProfilerEventType {
	    CorePumpEvents,
		CoreDevConClient,
		CorePumpAudio,
		CoreFixedUpdate,
		CoreVariableUpdate,
		CoreUpdateSystem,
		CoreUpdatePlatform,
		CoreUpdate,
		CoreStartRender,
		CoreRender,
		CoreVSync,

		PainterDrawCall,
		PainterEndRender,
		PainterUpdateProjection,

		WorldVariableUpdate,
		WorldFixedUpdate,
		WorldRender,
		WorldSystemUpdate,
		WorldSystemRender,

		AudioGenerateBuffer,

		DiskIO,

		StatsView,

		Game
    };

    class ProfilerData {
    public:
        using TimePoint = std::chrono::steady_clock::time_point;
    	using Duration = std::chrono::duration<int64_t, std::nano>;

		class Event {
        public:
	        String name;
        	std::thread::id threadId;
			ProfilerEventType type;
			int depth;
        	uint64_t id;
        	TimePoint startTime;
        	TimePoint endTime;
        };

    	class ThreadInfo {
    	public:
    		std::thread::id id;
    		int maxDepth = 0;
    		String name;
    		TimePoint startTime;
    		TimePoint endTime;
    		Duration totalTime;

    		bool operator< (const ThreadInfo& other) const;
    	};

    	ProfilerData() = default;
    	ProfilerData(TimePoint frameStartTime, TimePoint frameEndTime, std::vector<Event> events);

    	TimePoint getStartTime() const;
    	TimePoint getEndTime() const;
    	const std::vector<Event>& getEvents() const;
    	Duration getTotalElapsedTime() const;
		Duration getElapsedTime(ProfilerEventType eventType) const;

    	gsl::span<const ThreadInfo> getThreads() const;

    private:
    	TimePoint frameStartTime;
    	TimePoint frameEndTime;
    	std::vector<Event> events;

    	std::vector<ThreadInfo> threads;

    	void processEvents();
    };

    class ProfilerCapture {
    public:
        using EventId = uint64_t;

        ProfilerCapture(size_t maxEvents = 16384);

    	[[nodiscard]] static ProfilerCapture& get();

    	[[nodiscard]] EventId recordEventStart(ProfilerEventType type, std::string_view name);
    	void recordEventEnd(EventId id);

    	[[nodiscard]] bool isRecording() const;

    	void startFrame(bool record);
    	void endFrame();
		ProfilerData getCapture();

    	Time getFrameTime() const;

    private:
    	enum class State {
    		Idle,
    		FrameStarted,
    		FrameEnded
    	};

    	std::atomic<bool> recording;
    	std::atomic<uint64_t> curId;
    	uint64_t startId;
    	std::atomic<uint64_t> endId;
        State state = State::Idle;

    	std::chrono::steady_clock::time_point frameStartTime;
    	std::chrono::steady_clock::time_point frameEndTime;

    	std::vector<ProfilerData::Event> events;
    };

	class ProfilerEvent {
	public:
		ProfilerEvent(ProfilerEventType type, std::string_view name = "");
		~ProfilerEvent() noexcept;

		ProfilerEvent(const ProfilerEvent& other) = delete;
		ProfilerEvent(ProfilerEvent&& other) = delete;
		ProfilerEvent& operator=(const ProfilerEvent& other) = delete;
		ProfilerEvent& operator=(ProfilerEvent&& other) = delete;

	private:
		ProfilerCapture::EventId id;
	};
}


#include "jfftw_Wisdom.h"
#include <fftw.h>

/*
 * Class:     jfftw_Wisdom
 * Method:    get
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jfftw_Wisdom_get( JNIEnv * env, jclass clazz )
{
	char* cwisdom = fftw_export_wisdom_to_string();

	jstring wisdom = (*env)->NewStringUTF( env, cwisdom );
	fftw_free( cwisdom );

	return wisdom;
}
/*
 * Class:     jfftw_Wisdom
 * Method:    add
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_jfftw_Wisdom_add( JNIEnv * env, jclass clazz, jstring wisdom )
{
	const char* cwisdom = (*env)->GetStringUTFChars( env, wisdom, NULL );

	fftw_status s = fftw_import_wisdom_from_string( cwisdom );

	(*env)->ReleaseStringUTFChars( env, wisdom, cwisdom );

	if( s == FFTW_FAILURE )
	{
		(*env)->ThrowNew( env, (*env)->FindClass( env, "java/lang/IllegalArgumentException" ), "unable to parse wisdom" );
	}
}
/*
 * Class:     jfftw_Wisdom
 * Method:    clear
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_jfftw_Wisdom_clear( JNIEnv * env, jclass clazz )
{
	fftw_forget_wisdom();
}


#pragma once

#include "BoundingVolume.h"
#include "Library.h"
#include "TileContentLoadResult.h"
#include "TileContentLoader.h"
#include "TileID.h"
#include "TileRefine.h"

#include <CesiumGltf/GltfReader.h>

#include <glm/mat4x4.hpp>
#include <gsl/span>
#include <spdlog/fwd.h>

#include <cstddef>

namespace Cesium3DTilesSelection {

class Tileset;

/**
 * @brief Creates {@link TileContentLoadResult} from glTF data.
 */
class CESIUM3DTILESSELECTION_API GltfContent final : public TileContentLoader {
public:
  /**
   * @copydoc TileContentLoader::load
   *
   * The result will only contain the `model`. Other fields will be
   * empty or have default values.
   */
  CesiumAsync::Future<std::unique_ptr<TileContentLoadResult>>
  load(const TileContentLoadInput& input) override;

  /**
   * @brief Create a {@link TileContentLoadResult} from the given data.
   *
   * (Only public to be called from `Batched3DModelContent`)
   *
   * @param pLogger Only used for logging
   * @param url The URL, only used for logging
   * @param data The actual glTF data
   * @return The {@link TileContentLoadResult}
   */
  static std::unique_ptr<TileContentLoadResult> load(
      const std::shared_ptr<spdlog::logger>& pLogger,
      const std::string& url,
      const gsl::span<const std::byte>& data);

  /**
   * @brief Creates texture coordinates for mapping {@link RasterOverlay} tiles
   * to {@link Tileset} tiles.
   *
   * Generates new texture coordinates for the `gltf` using the given
   * `projection`. The first new texture coordinate (`u` or `s`) will be 0.0 at
   * the `minimumX` of the given `rectangle` and 1.0 at the `maximumX`. The
   * second texture coordinate (`v` or `t`) will be 0.0 at the `minimumY` of
   * the given `rectangle` and 1.0 at the `maximumY`.
   *
   * Coordinate values for vertices in between these extremes are determined by
   * projecting the vertex position with the `projection` and then computing the
   * fractional distance of that projected position between the minimum and
   * maximum.
   *
   * Projected positions that fall outside the `rectangle` will be clamped to
   * the edges, so the coordinate values will never be less then 0.0 or greater
   * than 1.0.
   *
   * These texture coordinates are stored in the provided glTF, and a new
   * primitive attribute named `_CESIUMOVERLAY_n`, where `n` is the
   * `textureCoordinateID` passed to this function, is added to each primitive.
   *
   * @param gltf The glTF model.
   * @param transform The transformation of this glTF to ECEF coordinates.
   * @param textureCoordinateID The texture coordinate ID.
   * @param projection The projection. There is a linear relationship between
   * the coordinates of this projection and the generated texture coordinates.
   * @param rectangle The rectangle that all projected vertex positions are
   * expected to lie within.
   * @return The bounding region.
   */
  static CesiumGeospatial::BoundingRegion createRasterOverlayTextureCoordinates(
      CesiumGltf::Model& gltf,
      const glm::dmat4& transform,
      int32_t textureCoordinateID,
      const CesiumGeospatial::Projection& projection,
      const CesiumGeometry::Rectangle& rectangle);

private:
  static CesiumGltf::GltfReader _gltfReader;
};

} // namespace Cesium3DTilesSelection


// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once
#include "Data\Intrinsics.h"
#include "Data\Pose.h"
#include "Device/CameraCalibration.h"
#include "MageSettings.h"
#include "Device\IMUCharacterization.h"

#include "Map\Map.h"
#include "Map\ThreadSafeMap.h"
#include "Bow\BaseBow.h"

#include "Image\AnalyzedImage.h"

#include "Proxies\MapPointProxy.h"

#include <opencv2\core\core.hpp>
#include <opencv2\features2d\features2d.hpp>
#include <memory>
#include <gsl\gsl>

namespace UnitTests
{
    class MapInitializationUnitTest;
}

namespace mage
{
    class KeyframeBuilder;

    class MapInitialization
    {
    public:

        enum InitializationAttemptState
        {
            NoPose,
            ResetInit,
            FinishInit,
            Skipped
        };

        MapInitialization(const MonoMapInitializationSettings& settings, const PerCameraSettings& cameraSettings, const device::IMUCharacterization& imuCharacterization, mira::determinator& determinator);

        InitializationAttemptState TryInitializeMap(const std::shared_ptr<const AnalyzedImage>& frame,
            thread_memory memory,
            InitializationData& initializationData,
            BaseBow& bagOfWords);

        bool InitializeWithFrames(gsl::span<const cv::DMatch> matches,
            const std::shared_ptr<const AnalyzedImage>& frame0,
            const std::shared_ptr<const AnalyzedImage>& frame1,
            InitializationData& initializationData,
            thread_memory memory);

        struct BundlerSettings
        {
            float HuberWidth;
            float MaxOutlierError;
            float MaxOutlierErrorScaleFactor;
            float MinMeanSquareError;
            bool FixMapPoints;
            uint32_t NumStepsPerRun;
            uint32_t NumSteps;
            uint32_t MinSteps;
        };

        static void BundleAdjustInitializationData(InitializationData& initializationData, mira::determinator& determinator, bool cullOutliers, const BundlerSettings& bundlerSettings, thread_memory memory);
        static bool ValidateInitializationData(const InitializationData& initializationData, float maxZContribution, float amountBACanChangePose, size_t minFeatureCount);

        // Frame1Points and Frame2Points are indexed to match each other
        static void CollectMatchPoints(const std::shared_ptr<const AnalyzedImage>& referenceFrame,
                const std::shared_ptr<const AnalyzedImage>& currentFrame,
                gsl::span<const cv::DMatch> matches,
                std::vector<cv::Point2f>& frame1Points,
                std::vector<cv::Point2f>& frame2Points);

        static void TriangulatePoints(const std::shared_ptr<const AnalyzedImage>& referenceFrame, const std::shared_ptr<const AnalyzedImage>& currentFrame,
            const Pose & referencePose, const Pose & currentPose,
            gsl::span<const cv::DMatch> matches,
            gsl::span<const cv::Point2f> frame1_points, gsl::span<const cv::Point2f> frame2_points,
            float pixelMaxEpipolarDistance, float minAcceptanceDistanceRatio,
            std::vector<std::pair<cv::DMatch, cv::Point3f>>& initial3DPoints);

    private:

        struct MatchedImage
        {
            std::shared_ptr<const AnalyzedImage>    Image;
            std::vector<cv::DMatch>                 Matches;

            MatchedImage(std::shared_ptr<const AnalyzedImage> image, std::vector<cv::DMatch> matches)
                : Image{ image },
                Matches( std::move(matches) )
            {};

            MatchedImage(std::shared_ptr<const AnalyzedImage> image)
                : Image{ image }
            {};
        };

        struct PointAssociation
        {
            size_t      PointIndex2d;
            size_t      PointIndex3d;

            PointAssociation(size_t twoD, size_t threeD)
                : PointIndex2d { twoD },
                PointIndex3d { threeD }
            {};
        };

        struct InitializationPose
        {
            std::shared_ptr<const AnalyzedImage>    Image;
            mage::Pose                              Pose;
            std::vector<PointAssociation>           Associations;

            InitializationPose(std::shared_ptr<const AnalyzedImage> image, mage::Pose pose, std::vector<PointAssociation> associations)
                : Image( image ),
                Pose( pose ),
                Associations(std::move(associations))
            {};
        };

        void ResetMapInitialization();

        bool TryIntializeMapWithProvidedFrames(
            const MatchedImage& referenceFrame,
            MatchedImage& currentFrame,
            thread_memory memory,
            InitializationData& initializationData,
            BaseBow& bagOfWords);

        std::vector<Pose> FindEssentialPotientialPoses(
            const cv::Matx33f& essentialMat);

        //Frame1Points and Frame2Points are indexed to match each other
        float ScoreFundamentalMatrix(gsl::span<const cv::Point2f> frame1Points,
            gsl::span<const cv::Point2f> frame2Points,
            const cv::Matx33f& fundamentalMat1To2,
            const cv::Matx33f& fundamentalMat2To1);

        //Frame1Points and Frame2Points are indexed to match each other
        std::vector<Pose> FindPossiblePoses(gsl::span<const cv::Point2f> frame1Points,
            gsl::span<const cv::Point2f> frame2Points,
            const CameraCalibration& frame1Calibration,
            const CameraCalibration& frame2Calibration);

        //Frame1Points and Frame2Points are indexed to match each other
        bool FindCorrectPose(gsl::span<const cv::Point2f> frame1Points,
            gsl::span<const cv::Point2f> frame2Points,
            gsl::span<const cv::DMatch> matches,
            const CameraCalibration& frame1Calibration,
            const CameraCalibration& frame2Calibration,
            const Pose& referencePose,
            const std::vector<Pose>& poses,
            Pose& correctPose,
            std::vector<std::pair<cv::DMatch, cv::Point3f>>& correct3DPoints);

        const MonoMapInitializationSettings m_settings;
        const PerCameraSettings m_cameraSettings;
        const device::IMUCharacterization& m_imuCharacterization;
        mira::determinator& m_determinator;

        std::vector<MatchedImage> m_initializationFrames;
        std::vector<uint8_t> m_initializationDescriptorsCounters;

        //For Test
        friend class ::UnitTests::MapInitializationUnitTest;
     };
}


#if !defined(_MUSIC_GSL_)
#define _MUSIC_GSL_

#define WIN32

#include <gsl/gsl_combination.h>
#include <gsl/gsl_sf.h>

#endif


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

#pragma once

#ifndef graph_46d3170b_a19a_4663_bbf9_c7372932671b_h
#define graph_46d3170b_a19a_4663_bbf9_c7372932671b_h

#include <assert.h>
#include <gslib/std.h>

__gslib_begin__

template<class _meta>
struct _mono_rel
{
    _meta*          _next;

    _mono_rel() { _next = nullptr; }
    _meta* next() const { return _next; }
    void set_next(_meta* ptr) { _next = ptr; }
    _meta* operator+(int d) const
    {
        assert(d >= 0);
        _meta* p = static_cast<_meta*>(this);
        for( ; d > 0; d --)
            p = p->_next;
        return p;
    }
    int operator-(const _meta& that) const
    {
        int c = 0;
        _meta* p = static_cast<_meta*>(this);
        for( ; p->_next; p = p->_next, c ++) {
            if(p == &that)
                return c;
        }
        assert(!"unexpected.");
        return -1;
    }
    int count_to_tail() const
    {
        int c = 0;
        for(_meta* p = static_cast<_meta*>(this); p->_next; p = p->_next, c ++);
        return c;
    }
    static void shake(_meta* p1, _meta* p2) { p1->_next = p2; }
};

template<class _meta>
struct _dual_rel
{
    _meta           *_prev, *_next;

    _dual_rel() { _prev = _next = nullptr; }
    _meta* prev() const { return _prev; }
    _meta* next() const { return _next; }
    void set_prev(_meta* ptr) { _prev = ptr; }
    void set_next(_meta* ptr) { _next = ptr; }
    _meta* operator+(int d) const
    {
        if(d < 0)
            return operator-(-d);
        _meta* p = static_cast<_meta*>(this);
        for( ; d > 0; d --)
            p = p->_next;
        return p;
    }
    _meta* operator-(int d) const
    {
        if(d < 0)
            return operator+(-d);
        _meta* p = static_cast<_meta*>(this);
        for( ; d > 0; d --)
            p = p->_prev;
        return p;
    }
    int operator-(const _meta& that) const
    {
        int c = 0;
        _meta* p = static_cast<_meta*>(this);
        for( ; p->_next; p = p->_next, c ++) {
            if(p == &that)
                return c;
        }
        assert(!"unexpected.");
        return -1;
    }
    int count_to_head() const
    {
        int c = 0;
        for(_meta* p = static_cast<_meta*>(this); p->_next; p = p->_prev, c ++);
        return c;
    }
    int count_to_tail() const
    {
        int c = 0;
        for(_meta* p = static_cast<_meta*>(this); p->_next; p = p->_next, c ++);
        return c;
    }
    static void shake(_meta* p1, _meta* p2) { p1->_next = p2, p2->_prev = p1; }
};

template<class _meta>
struct _pack_rel
{
    _meta       *_from, *_to;
    int         _size;

    _pack_rel() { _from = _to = nullptr, _size = 0; }
    _pack_rel(_meta* from, _meta* to) { _from = from, _to = to, _size = 0; }
    _meta* from() const { return _from; }
    _meta* to() const { return _to; }
    bool empty() const { return !_from; }
    int size() const { return _size; }
    void set_from(_meta* ptr) { _from = ptr; }
    void set_to(_meta* ptr) { _to = ptr; }
    void increase_size() { _size ++; }
    void decrease_size() { _size --; }
    void set_size(int s) { _size = s; }
    bool check_mono() const
    {
        if(!_from)
            return false;
        _meta* p = static_cast<_meta*>(_from);
        for( ; p->_next; p = p->_next);
        return p == _to;
    }
    bool check_dual() const
    {
        if(!_from || _from->_prev)
            return false;
        _meta* p = static_cast<_meta*>(_from);
        for( ; p->_next; p = p->_next) {
            if(p->_prev && p != p->_prev->_next)
                return false;
        }
        return p == _to;
    }
    bool is_belong_to(const _meta* ptr) const
    {
        _meta* p = static_cast<_meta*>(_from);
        for( ; p; p = p->_next) {
            if(ptr == p)
                return true;
        }
        return false;
    }
};

class _graph_ortho_rel:
    public _dual_rel<_graph_ortho_rel>
{
public:
    typedef _pack_rel<_graph_ortho_rel> hierarchic_rel;
    typedef _dual_rel<_graph_ortho_rel> peer_rel;
    typedef hierarchic_rel upper_rel;
    typedef hierarchic_rel lower_rel;

protected:
    hierarchic_rel  _parents, _children;

public:
    hierarchic_rel& parents() { return _parents; }
    hierarchic_rel& children() { return _children; }
    peer_rel& siblings() { return static_cast<peer_rel&>(*this); }
    const hierarchic_rel& const_parents() const { return _parents; }
    const hierarchic_rel& const_children() const { return _children; }
    const peer_rel& const_siblings() const { return static_cast<const peer_rel&>(*this); }
    int parents_count() const { return _parents.size(); }
    int children_count() const { return _children.size(); }
};

struct _graph_trait_copy {};
struct _graph_trait_detach {};

template<class _ty,
    class _rel = _graph_ortho_rel
    >
struct _graph_ortho_cpy_wrapper:
    public _ty,
    public _rel
{
    typedef _ty value;
    typedef _rel relation;
    typedef _graph_ortho_cpy_wrapper<_ty, _rel> wrapper;
    typedef _graph_trait_copy tsf_behavior;

    value* get_ptr() { return static_cast<value*>(this); }
    const value* const_ptr() const { return static_cast<const value*>(this); }
    value& get_ref() { return static_cast<value&>(*this); }
    const value& const_ref() const { return static_cast<const value&>(*this); }
    template<class _cst>
    void born() {}  /* maybe a problem. */
    void born() {}
    void kill() {}
    void copy(const wrapper* a) { assign<value>(a); }
    void attach(wrapper* a) { !!error!! }
    int get_parents_count() const { return parents_count(); }
    int get_children_count() const { return children_count(); }
    template<class c>
    void assign(const wrapper* a) { static_cast<c&>(*this) = static_cast<c&>(*a); }
};

template<class _ty,
    class _rel = _graph_ortho_rel
    >
struct _graph_ortho_wrapper:
    public _rel
{
    typedef _ty value;
    typedef _rel relation;
    typedef _graph_ortho_wrapper<_ty, _rel> wrapper;
    typedef _graph_trait_detach tsf_behavior;

    value*          _value;

    wrapper() { _value = nullptr; }
    value* get_ptr() { return _value; }
    const value* const_ptr() const { return _value; }
    value& get_ref() { return *_value; }
    const value& const_ref() const { return *_value; }
    relation& get_relation() { return static_cast<relation&>(*this); }
    const relation& const_relation() const { return static_cast<const relation&>(*this); }
    template<class _cst>
    void born() { _value = new _cst; }
    void born() { _value = new value; }
    void kill() { delete _value; }
    void copy(const wrapper* a) { assert(!"prevent."); }
    void attach(wrapper* a)
    {
        assert(a && a->_value);
        kill();
        _value = a->_value;
        a->_value = nullptr;
    }
    int get_parents_count() const { return parents_count(); }
    int get_children_count() const { return children_count(); }
};

template<class _wrapper>
struct _graph_allocator
{
    typedef _wrapper wrapper;
    static wrapper* born() { return new wrapper; }
    static void kill(wrapper* w) { delete w; }
};

template<class _val>
struct _graph_ortho_val
{
    typedef _val value;
    typedef const _val const_value;
    union
    {
        value*          _vptr;
        const_value*    _cvptr;
    };
    value* get_wrapper() const { return _vptr; }
    value* trace_front(int d) const { return _vptr->siblings() + d; }
    value* trace_back(int d) const { return _vptr->siblings() - d; }
    value* trace_up(int d) const
    {
        if(d < 0)
            return trace_down(-d);
        value* p = _vptr;
        for( ; d > 0; d --) {
            assert(!p->parents().empty());
            p = p->parents().from();
        }
        return p;
    }
    value* trace_down(int d) const
    {
        if(d < 0)
            return trace_up(-d);
        value* p = _vptr;
        for( ; d > 0; d --) {
            assert(!p->children().empty());
            p = p->children().from();
        }
        return p;
    }
    value* trace_front() const
    {
        assert(_vptr);
        return _vptr->next();
    }
    value* trace_back() const
    {
        assert(_vptr);
        return _vptr->prev();
    }
};

template<class _ty,
    class _wrapper = _graph_ortho_cpy_wrapper<_ty>
    >
class _graph_ortho_const_iterator:
    public _graph_ortho_val<_wrapper>
{
public:
    typedef _ty value;
    typedef _wrapper wrapper;
    typedef _graph_ortho_const_iterator<_ty, _wrapper> iterator;

public:
    iterator(const wrapper* w = nullptr) { _cvptr = w; }
    bool is_valid() const { return _cvptr != nullptr; }
    const value* get_ptr() const { return _cvptr->const_ptr(); }
    const value* operator->() const { return _cvptr->const_ptr(); }
    const value& operator*() const { return _cvptr->const_ref(); }
    iterator operator+(int d) const { return iterator(trace_front(d)); }
    iterator operator-(int d) const { return iterator(trace_back(d)); }
    iterator operator<<(int d) const { return iterator(trace_up(d)); }
    iterator operator>>(int d) const { return iterator(trace_down(d)); }
    int operator-(iterator i) const { return _cvptr->peer_rel::operator-(*(i._cvptr)); }
};

template<class _ty,
    class _wrapper = _graph_ortho_cpy_wrapper<_ty>
    >
class _graph_ortho_iterator:
    public _graph_ortho_const_iterator<_ty, _wrapper>
{
public:
    typedef _ty value;
    typedef _wrapper wrapper;
    typedef _graph_ortho_const_iterator<_ty, _wrapper> superref;
    typedef _graph_ortho_const_iterator<_ty, _wrapper> const_iterator;
    typedef _graph_ortho_iterator<_ty, _wrapper> iterator;

public:
    iterator(wrapper* w = nullptr) { _vptr = w; }
    value* get_ptr() const { return _vptr->get_ptr(); }
    value* operator->() const { return _vptr->get_ptr(); }
    value& operator*() const { return _vptr->get_ref(); }
    bool operator==(const iterator& that) const { return _vptr == that._vptr; }
    bool operator!=(const iterator& that) const { return _vptr != that._vptr; }
    bool operator==(const const_iterator& that) const { return _vptr == that._vptr; }
    bool operator!=(const const_iterator& that) const { return _vptr != that._vptr; }
    void operator++() { _vptr = trace_front(); }
    void operator--() { _vptr = trace_back(); }
    void operator+=(int d) { _vptr = trace_front(d); }
    void operator-=(int d) { _vptr = trace_back(d); }
    void operator<<=(int d) { _vptr = trace_up(d); }
    void operator>>=(int d) { _vptr = trace_down(d); }
    operator const_iterator() { return const_iterator(_cvptr); }
};

template<class _ty,
    class _wrapper = _graph_ortho_wrapper<_ty>,
    class _alloc = _graph_allocator<_wrapper>
    >
class ortho_graph:
    public _graph_ortho_val<_wrapper>
{
public:
    typedef _ty value;
    typedef _wrapper wrapper;
    typedef _alloc alloc;
    typedef ortho_graph<_ty, _wrapper, _alloc> myref;
    typedef _graph_ortho_const_iterator const_iterator;
    typedef _graph_ortho_iterator iterator;

public:
    ortho_graph() { _vptr = nullptr; }
    ~ortho_graph() { destroy(); }
    void destroy()
    {
        for(wrapper* i = _vptr; i; ) {
            wrapper* n = i->next();
            _erase(i);
            i = n;
        }
    }
    void clear() { destroy(); }
    iterator get_root() { return iterator(_vptr); }
    const_iterator const_root() const { return const_iterator(_cvptr); }
    bool is_valid() const { return !_cvptr; }
    iterator insert_before(iterator i) { return insert_before<value>(i); }
    iterator insert_after(iterator i) { return insert_after<value>(i); }
    iterator create_child(iterator from, iterator to = from) { return create_child<value>(from, to); }
    iterator erase(iterator i)
    {
        iterator r(i->next());
        _erase(i.get_wrapper());
        return r;
    }

public:
    template<class _cst>
    iterator insert_before(iterator i)
    {
        if(!i.is_valid())
            return _cvptr ? get_root() : _init<_cst>();
        wrapper* n = alloc::born();
        n->born<_cst>();
        _fix_peer_rel(i->prev(), n, *i);
        _fix_upper_rel(n, *i);
        _fix_lower_rel(n, *i);
        /* fix root, keep the root always be the top left corner. */
        if(i.get_wrapper() == _vptr)
            _vptr = n;
        return iterator(n);
    }
    template<class _cst>
    iterator insert_after(iterator i)
    {
        if(!i.is_valid())
            return _cvptr ? get_root() : _init<_cst>();
        wrapper* n = alloc::born();
        n->born<_cst>();
        _fix_peer_rel(*i, n, i->next());
        _fix_upper_rel(n, *i);
        _fix_lower_rel(n, *i);
        return iterator(n);
    }
    template<class _cst>
    iterator create_child(iterator from, iterator to = from)
    {
        assert(from.is_valid() && from->children().empty());
        wrapper* n = alloc::born();
        n->born<_cst>();
        _fix_upper_rel(n, *from, *to);
        return iterator(n);
    }
    template<class _lambda>
    void _for_range(iterator from, iterator to, _lambda lam)
    {
        for(iterator i = from; ; i ++) {
            lam(i->get_wrapper());
            if(i == to)
                break;
        }
    }
    template<class _lambda>
    void for_range(iterator from, iterator to, _lambda lam)
    {
        for(iterator i = from; ; i ++) {
            lam(*i);
            if(i == to)
                break;
        }
    }

protected:
    template<class _cst>
    iterator _init()
    {
        _vptr = alloc::born();
        _vptr->born<_cst>();
        return iterator(_vptr);
    }
    void _erase(wrapper* w)
    {
        assert(w);
        _fix_upper_rel(w);
        _fix_lower_rel(w);
        peer_rel::shake(w->prev(), w->next());
        lower_rel& rel = w->children();
        if(!rel.empty())
            _for_range(rel.from(), rel.to(), [](wrapper* w) { _erase(w); });
        if(w == _vptr)
            _vptr = w->next();
        w->kill();
        alloc::kill(w);
    }
    void _set_range_upper(wrapper* from, wrapper* to, wrapper* r1, wrapper* r2, int size)
    {
        _for_range(iterator(from), iterator(to), [&r1, &r2, &size](wrapper* w) {
            upper_rel& rel = w->parents();
            rel.set_from(r1);
            rel.set_to(r2);
            rel.set_size(size);
        });
    }
    void _set_range_lower(wrapper* from, wrapper* to, wrapper* r1, wrapper* r2, int size)
    {
        _for_range(iterator(from), iterator(to), [&r1, &r2, &size](wrapper* w) {
            lower_rel& rel = w->children();
            rel.set_from(r1);
            rel.set_to(r2);
            rel.set_size(size);
        });
    }
    void _fix_peer_rel(wrapper* w1, wrapper* w2, wrapper* w3)
    {
        assert(w2 && (w1 || w3));
        if(w1 != nullptr)
            peer_rel::shake(w1, w2);
        if(w3 != nullptr)
            peer_rel::shake(w2, w3);
    }
    void _fix_upper_rel(wrapper* n, wrapper* w)
    {
        assert(n && w);
        upper_rel& urel = w->parents();
        wrapper* u1 = urel.from();
        if(u1 == nullptr)
            return;
        wrapper* u2 = urel.to();
        lower_rel& lrel = u1->children();
        wrapper* l1 = lrel.from();
        wrapper* l2 = lrel.to();
        assert(l1 && l2);
        wrapper *from = l1, *to = l2;
        if(l1 == w && n == w->prev())
            from = n;
        else if(l2 == w && n == w->next())
            to = n;
        _set_range_lower(u1, u2, from, to, lrel.size() + 1);
        upper_rel& u = n->parents();
        u.set_from(u1);
        u.set_to(u2);
    }
    void _fix_lower_rel(wrapper* n, wrapper* w)
    {
        assert(n && w);
        lower_rel& lrel = w->children();
        wrapper* l1 = lrel.from();
        if(l1 == nullptr)
            return;
        wrapper* l2 = lrel.to();
        upper_rel& urel = l1->parents();
        wrapper* u1 = urel.from();
        wrapper* u2 = urel.to();
        assert(u1 && u2);
        wrapper *from = u1, *to = u2;
        if(u1 == w && n == w->prev())
            from = n;
        else if(u2 == w && n == w->next())
            to = n;
        _set_range_upper(l1, l2, from, to, urel.size() + 1);
        lower_rel& l = n->children();
        l.set_from(l1);
        l.set_to(l2);
    }
    void _fix_upper_rel(wrapper* w)
    {
        assert(w);
        upper_rel& urel = w->parents();
        wrapper* u1 = urel.from();
        if(u1 == nullptr)
            return;
        wrapper* u2 = urel.to();
        assert(u2);
        lower_rel& lrel = u1->children();
        wrapper* l1 = lrel.from();
        wrapper* l2 = lrel.to();
        assert(l1 && l2);
        wrapper *from = l1, *to = l2;
        if(l1 == w && l2 == w)
            from = to = nullptr;
        else if(l1 == w)
            from = w->next();
        else if(l2 == w)
            to = w->prev();
        _set_range_lower(u1, u2, from, to, lrel.size() - 1);
        urel.set_from(nullptr);
        urel.set_to(nullptr);
        urel.set_size(0);
    }
    void _fix_lower_rel(wrapper* w)
    {
        assert(w);
        lower_rel& lrel = w->children();
        wrapper* l1 = lrel.from();
        if(l1 == nullptr)
            return;
        wrapper* l2 = lrel.to();
        assert(l2);
        upper_rel& urel = l1->parents();
        wrapper* u1 = urel.from();
        wrapper* u2 = urel.to();
        assert(u1 && u2);
        wrapper *from = u1, *to = u2;
        if(u1 == w && u2 == w);
        else if(u1 == w || u2 == w) {
            u1 == w ? from = w->next() : to = w->prev();
            lrel.set_from(nullptr);
            lrel.set_to(nullptr);
            lrel.set_size(0);
        }
        _set_range_upper(l1, l2, from, to, urel.size() - 1);
    }
    void _fix_upper_rel(wrapper* w, wrapper* u1, wrapper* u2)
    {
        assert(w && u1 && u2);
        upper_rel& urel = w->parents();
        urel.set_from(u1);
        urel.set_to(u2);
        urel.set_size(u1->count_to_tail(u2) + 1);
        _set_range_lower(u1, u2, w, w, 1);
    }
};

__gslib_end__

#endif


/*
 * Copyright (c) 2011-2013  University of Texas at Austin. All rights reserved.
 *
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * This file is part of PerfExpert.
 *
 * PerfExpert is free software: you can redistribute it and/or modify it under
 * the terms of the The University of Texas at Austin Research License
 *
 * PerfExpert 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.
 *
 * Authors: Leonardo Fialho and Ashay Rane
 *
 * $HEADER$
 */

#ifndef ANALYSIS_DEFS_H_
#define ANALYSIS_DEFS_H_

#include <vector>
#include <gsl/gsl_histogram.h>

#include "tools/macpo/common/avl_tree.h"
#include "tools/macpo/common/generic_defs.h"
#include "tools/macpo/common/macpo_record.h"
#include "tools/macpo/common/macpo_record_cxx.h"

typedef gsl_histogram histogram_t;

typedef std::vector<mem_info_list_t> mem_info_bucket_t;
typedef std::vector<trace_info_list_t> trace_info_bucket_t;
typedef std::vector<vector_stride_info_list_t> vector_stride_info_bucket_t;

typedef std::vector<histogram_t*> histogram_list_t;
typedef std::vector<histogram_list_t> histogram_matrix_t;

typedef std::vector<avl_tree*> avl_tree_list_t;

typedef std::pair<size_t, size_t> pair_t;
typedef std::vector<pair_t> pair_list_t;

typedef std::vector<double> double_list_t;
typedef std::vector<int> int_list_t;

typedef struct {
    size_t size, line_size, associativity, count;
} cache_data_t;

typedef struct {
    cache_data_t l1_data, l2_data, l3_data;
    name_list_t stream_list;
    mem_info_bucket_t mem_info_bucket;
    trace_info_bucket_t trace_info_bucket;
    vector_stride_info_bucket_t vector_stride_info_bucket;
} global_data_t;

typedef struct {
    size_t count, l1_conflicts, l2_conflicts;
} stream_list_t;

/* Different kinds of analyses options. */
#define ANALYSIS_REUSE_DISTANCE     (1 << 0)
#define ANALYSIS_CACHE_CONFLICTS    (1 << 1)
#define ANALYSIS_PREFETCH_STREAMS   (1 << 2)
#define ANALYSIS_STRIDES            (1 << 3)
#define ANALYSIS_VECTOR_STRIDES     (1 << 4)

#define ANALYSIS_ALL                (~0)

/* Number of reuse distance entries to display for each stream. */
#define REUSE_DISTANCE_COUNT        3

#define ADDR_TO_CACHE_LINE(x)   (x >> 6)

#endif /* ANALYSIS_DEFS_H_ */