#pragma once

#include "halleystring.h"
#include "halley/data_structures/vector.h"
#include <optional>
#include <gsl/span>

namespace Halley {
    class FuzzyTextMatcher {
    public:
		class Score {
		public:
			int curJumps = 0;
			int jumpLength = 0;
			int sections = 0;
			int sectionLens = 0;
			int sectionPos = 0xFFFF;
			Vector<std::pair<uint16_t, uint16_t>> matchPositions;

			bool operator<(const Score& other) const;

			Score advance(int jumpLen, int sectionPos, int newSectionLen) const;
			void makeMatchPositions(const Vector<int>& breadcrumbs);
		};

        class Result {
        public:
        	Result() = default;
            Result(String str, String id, Score score);

            bool operator<(const Result& other) const;

        	const String& getString() const;
        	const String& getId() const;
        	gsl::span<const std::pair<uint16_t, uint16_t>> getMatchPositions() const;

        private:
        	String str;
        	String id;
        	Vector<std::pair<uint16_t, uint16_t>> matchPositions;
        	Score score;
        };

        FuzzyTextMatcher(bool caseSensitive, std::optional<size_t> resultsLimit);

    	void addStrings(Vector<String> strings);
    	void addString(String string, String id = "");
    	void clear();

    	Vector<Result> match(const String& query) const;

    private:
        struct Entry {
	        String string;
            String id;
        };

    	Vector<Entry> strings;
    	bool caseSensitive;
    	std::optional<size_t> resultsLimit;

    	std::optional<Result> match(const String& str, const String& id, const StringUTF32& query) const;
    };
}


#ifndef CollectorMap_h
#define CollectorMap_h
/** This is free and unencumbered software released into the public domain.
The authors of ISIS do not claim copyright on the contents of this file.
For more details about the LICENSE terms and the AUTHORS, you will
find files of those names at the top level of this repository. **/

/* SPDX-License-Identifier: CC0-1.0 */
#include <cctype>
#include <QString>
#include <map>
#include <algorithm>
#include <functional>
#include <sstream>
#include <iomanip>
#include <exception>
#include "IException.h"
#include "IString.h"
#include <gsl/gsl_math.h>

namespace Isis {

  /**
   * @brief Provides a simple comparison between two values
   *
   * This simple comparison function object is provided with no special frills
   * that does pretty much exactly what std::less does.
   */
  template <typename K> struct SimpleCompare {

    /**
     * Returns true if v1 is less than v2
     *
     * @param v1 Input constant
     * @param v2 Input constant
     *
     * @return bool Returns true if v1 is less than v2
     */
    bool operator()(const K &v1, const K &v2) const {
      return (v1 < v2);
    }

  };

  /**
  * @brief Provides a case insensitive string comparison
  *
  * This string comparison functor object compares two strings ignoring case. Use
  * this policy when your key into the collector map is a string and you want to
  * ignore case when finding elements in the collection.
  */
  template <typename K> struct NoCaseStringCompare {

    /**
     * Compares v1 and v2 as case insensitive strings, and returns true of v1 is
     * less than v2 (as those strings).
     *
     * @param v1 Input constant
     * @param v2 Input constant
     *
     * @return bool Returns true if v1 is less than v2 in string format
     */
    bool operator()(const K &v1, const K &v2) const {
      return (IString::DownCase(v1) < IString::DownCase(v2));
    }

  };

  /**
   * @brief Provides a robust comparison of double/float values
   *
   * This functor compares floating point values using a default epsilon of
   * 1.0e-6. It can be used for doubles or floats, however floats will be promoted
   * to double for the comparison.
   */
  template <typename K> struct RobustFloatCompare {

    /**
     * Compares v1 and v2 as floating point values.
     *
     * @param v1
     * @param v2
     *
     * @return bool
     */
    bool operator()(const K &v1, const K &v2) const {
      return (gsl_fcmp(v1, v2, -1.0E-6) < 0);
    }

  };

  /**
   * @brief Supplies a NOOP default for removal of a CollectorMap entry
   *
   * This simple declaration is basically a NOOP that implements removal
   * of a CollectionMap entry.  It is most useful (and the default behavior)
   * when the storage element of the CollectorMap is anything but a pointer.
   * Pointers that require deletion should use the PointerRemoval policy unless
   * the pointers are owned by another "entity".
   */
  template <typename T> struct NoopRemoval {

    // defines used for cross platform suppression of unused parameter warning. This will prevent
    // distroy() from causing unused parameter warnings in clang. This is done for the purpose
    // of porting ISIS to OSX 10.11
    // #define MON_Internal_UnusedStringify(macro_arg_string_literal) #macro_arg_string_literal
    // #define MONUnusedParameter(macro_arg_parameter) _Pragma(MON_Internal_UnusedStringify(unused(macro_arg_parameter)))

    protected:

      /**
       * Destroys the CollectorMap entry
       *
       * @param element The CollectorMap to be destroyed
       */
      void destroy(T *element) {
        // arbitrary cast type to suppress unused parameter warnings generated
        // by clang when building on mac
        (void)element;

        return;
      }

  };

  /**
   * @brief Supplies a policy for deleting pointers that CollectorMap owns
   *
   * Defines a method to delete pointers when removed from a CollectorMap.
   * This is necessary to prevent memory leaks and defer the deletion to
   * removal from CollectorMap class.
   */
  template <typename T> struct PointerRemoval {
    protected:

      /**
       * Destroys the CollectorMap pointer's CollectorMap
       *
       * @param element The pointer pointing to the CollectorMap to be destroyed
       */
      void destroy(T *element) {
        delete(*element);
        return;
      }

  };

  /**
   * @brief Policy for deleting arrays that CollectorMap owns
   *
   * Defines a method to delete arrays when removed from a CollectorMap.
   * This is necessary to prevent memory leaks and defer the deletion to
   * removal from CollectorMap class.
   */
  template <typename T> struct ArrayRemoval {
    protected:

      /**
       * Destroys the array of CollectorMaps
       *
       * @param element The array of CollectorMaps to be destroyed
       */
      void destroy(T *element) {
        delete [](*element);
        return;
      }

  };

  /**
   * @brief (Default) Policy for copying map elements
   *
   * Defines a method to copy simple elements from an existing map to a
   * destination map.  This policy just makes a direct copy of the element to the
   * destination.
   *
   * This policy assumes the assignment operator handles the proper copying of
   * each element T in the collection.
   */
  template <typename T> struct DefaultCopy {
    protected:

      /**
       * Returns a copy of the input
       *
       * @param src The map element to be copied
       *
       * @return const T& The copy of the input
       */
      const T &copy(const T &src) const {
        return (src);
      }

  };

  /**
   * @brief Pointer to object policy for copying map elements
   *
   * Defines a copy method to properly handle pointers to objects (assumed) when
   * copying the complete CollectorMap.  This implementation assumes the copy
   * constructor properly handles the creation of a new element from a different
   * one.
   *
   * This policy assumes the assignment operator handles the proper copying of
   * each element T* in the collection.
   *
   * This employs an intersting technique of redirection.  Because the type T is
   * actually T*, the templated allocate() method exists to get down to the T
   * class base level.  Looks strange but it works.
   */
  template <typename T> struct PointerCopy {
    protected:
      /**
       * @brief Allocate new object using copy construtor and new pointer
       *
       * This copy method takes a pointer to a pointer (T is actually a T*) and
       * allocates a new object using the copy constructor.
       *
       * @param src  Pointer to pointer of new class to allocate
       *
       * @return T  Pointer to new object type T
       */
      T copy(const T &src)  const {
        return (allocate(*(src)));
      }

    private:
      /**
       * @brief Allocate new object using copy constructor
       *
       * @param obj Source object to create new one from
       *
       * @return P* Pointer to newly allocated object
       */
      template <typename P>
      P *allocate(const P &obj) const {
        return (new P(obj));
      }
  };

  /**
   *  @brief Collector/container for arbitrary items
   *
   *  Used to contain types with iterators of const and non-const conditions.
   *  This is a multimap that contains arbitrary keys with arbitrary elements.  It
   *  is intended to be used for pointers and copyable objects.  They should be
   *  rather efficient in the copy out operation so large objects may not be
   *  suitable or classes that do not have a good copy operator.  During testing
   *  it was noted that an object is copied up to four times and destroyed three
   *  times upon an add() operation.
   *
   *  This class is implemented using policies.  The ComparePolicy is used to test
   *  key elements such as strings and double values.  The NoCaseStringCompare
   *  policy is provided that expedites case insensitive string key comparisons.
   *  The RobustFloatCompare implements the comparison of double or float key
   *  types.  Direct comparisons of floats can be problematic due to round off and
   *  storage manifestations of these values in conputers.   The default policy,
   *  SimpleCompare, does a simple parameter to key equality test.
   *
   *  The RemovalPolicy is provided when a map value is removed from the list.
   *  This allows pointers and arrays to be stored in the map as well.  To store
   *  pointers, use PointerRemoval and for arrays there is the ArrayRemoval
   *  policy.  The default is the NoopRemoval policy which simply lets the
   *  destructor handle removals.
   *
   *  The CopyPolicy is necessary to properly handle the copying of elements.
   *  This is especially important for pointers and arrays.  In order to minimize
   *  difficult passing strategies, map elements are passed by address and the
   *  return type is the element type.  DefaultCopy simply copies the elements as
   *  is relying on the element T assigment operator to do the right thing. For
   *  pointers to objects, the PointerCopy allocates the object using the copy
   *  constructor.  One could provide a similar operator assuming a clone()
   *  method existed for the type T element. The ArrayCopy policy is left to the
   *  user to provide their own as it cannot support arrays of varying length.
   *  (One should use std::vector instead!) Users can supply their own CopyPolicy
   *  that need only expose a copy(cont T *src) method.
   *
   *  Here are some examples that demonstrate how this policy-based template class
   *  can be used:
   *
   *  @code
   * // Create a unique string key list that stores double floating point values.
   * // Use the default removal and copy policies but allow testing for character
   * // keys without regard to case via the NoCaseStringCompare.
   *  #include "CollectorMap.h"
   *
   *  CollectorMap<QString, double, NoCaseStringCompare > dmap;
   *  cout << "\nSize of double map = " << dmap.size() << endl;
   *  dmap.add("one", 1.0);
   *  dmap.add("two", 2.0);
   *  cout << "Size of double map = " << dmap.size() << endl;
   *
   *  cout << "One = " << dmap.get("one") << endl;
   *  cout << "Two = " << dmap.get("Two") << endl;
   *
   *  const double &one = dmap.get("one");
   *  cout << "\nTest Const one = " << one << endl;
   *
   *  dmap.remove("one");
   *  @endcode
   *
   *  Using this class internal to classes is perhaps where it may be applied more
   *  frequently.  The example below shows how to declare an integer key using
   *  pointers to classes:
   *
   *  @code
   * #include "CollectorMap.h"
   *
   *  class ClassTest {
   *   public:
   *    ClassTest(int n = 0) : _n(n) {  }
   *    ~ClassTest() {  }
   *    int Ident() const { return (_n); }
   *   private:
   *    int _n;
   *  };
   *
   *
   * //  Typedefs are sometimes convenient in these cases
   *  typedef CollectorMap<int, ClassTest *, SimpleCompare,
   *                       PointerRemoval, PointerCopy> PointerMap;
   *
   *  PointerMap ctest2;
   *  ctest2.add(4,new ClassTest(4));
   *  ctest2.add(5,new ClassTest(5));
   *  ctest2.add(6,new ClassTest(6));
   *  ctest2.add(7,new ClassTest(7));
   *
   *  cout << "Remove ClassTest 6\n";
   *  ctest2.remove(6);
   *
   * //  Creates a copy of ctest2 using the PointerCopy policy
   *  PointerMap map2(ctest2);
   *
   *  cout << "Find element 7: "  << map2.find(7)->Ident() << endl;
   *
   *  @endcode
   *
   *  And, finally, an example of how to use duplicate keys:
   *
   *  @code
   *  #include "CollectorMap.h"
   *
   *  typedef CollectorMap<int,QString> IntStr;
   *  IntStr dupstr(IntStr::DuplicateKeys);
   *  dupstr.add(1,"One");
   *  dupstr.add(1, "One #2");
   *  dupstr.add(1,"One #3");
   *  dupstr.add(2,"Two");
   *  dupstr.add(2,"Two #2");
   *  dupstr.add(3,"Three");
   *
   *  cout << "Size of Dup object: " << dupstr.size() << endl;
   *  cout << "Number Ones:   " << dupstr.count(1) << endl;
   *  cout << "Number Twos:   " << dupstr.count(2) << endl;
   *  cout << "Number Threes: " << dupstr.count(3) << endl;
   *  cout << "Number Fours:  " << dupstr.count(4) << endl;
   *
   *  IntStr::CollectorConstIter isIter;
   *  int j = 0;
   *  for (isIter = dupstr.begin() ; isIter != dupstr.end() ; ++isIter, j++) {
   *       cout << "IntStr[" << j << "] = {" << isIter->first << ", "
   *            << isIter->second << "}, Index: " << dupstr.index(isIter->first)
   *            << endl;
   *       cout << "Nth Test Ident       = " << dupstr.getNth(j) << endl;
   *  }
   *  @endcode
   *
   *  The output of the above example is:
   *  @code
   * Size of Dup object: 6
   * Number Ones:   3
   * Number Twos:   2
   * Number Threes: 1
   * Number Fours:  0
   * IntStr[0] = {1, One}, Index: 0
   * Nth Test Ident       = One
   * IntStr[1] = {1, One #2}, Index: 0
   * Nth Test Ident       = One #2
   * IntStr[2] = * {1, One #3}, Index: 0
   * Nth Test Ident       = One #3
   * IntStr[3] * = {2, Two}, Index: 3
   * Nth Test Ident       = Two
   * IntStr[4] = * {2, Two #2}, Index: 3
   * Nth Test Ident       = Two #2
   * IntStr[5] = * {3, Three}, Index: 5
   * Nth Test Ident = Three
   *  @endcode
   *
   *  @ingroup Utility
   *
   *  @author 2006-06-21 Kris Becker
   *
   *  @internal
   *    @history 2006-07-03 Kris Becker Added the ability to stored duplicate keys
   *             if needed (using a multimap instead of a map).  Initial default
   *             behavior of unique keys is retained.  See KeyPolicy.
   *    @history 2006-07-28 Kris Becker Fixed a bug in the NoCaseStringCompare
   *             implementation.  Prior to this fix, it would not function
   *             properly at all for case-insenstive keys.
   *    @history 2006-08-30 Kris Becker Fixed bug in copy constructors that
   *             attempted to use a virtual method in the object being created
   *             when it *must* use the method from the one it is being created
   *             from. (g++ 4.1 on Suse 10.1 didn't like this bug at all!)
   *    @history 2008-06-18 Christopher Austin Fixed Documentation
   *    @history 2017-08-30 Summer Stapleton - Updated documentation. References #4807.
   *
   */
  template < typename K, typename T,
           template <class> class ComparePolicy = SimpleCompare,
           template <class> class RemovalPolicy = NoopRemoval,
           template <class> class CopyPolicy = DefaultCopy
           >
  class CollectorMap : public RemovalPolicy<T>, public CopyPolicy<T> {
    public:
      typedef T                                      CollectorType; //!< Data type
      /** A multimap attacking a key to a CollectorType and a ComparePolicy<CollectorType>*/
      typedef std::multimap<K, CollectorType, ComparePolicy<K> > CollectorList;
      //! CollectorList iterator type declaration
      typedef typename CollectorList::iterator       CollectorIter;
      //! CollectorList constant iterator type declaration
      typedef typename CollectorList::const_iterator CollectorConstIter;

      /**
       * @brief Enumerated selection of key behaviour
       *
       * Using this enumeration during construction allows the user of this class
       * to specify if the keys used to identify elements are unique or can be
       * duplicated.
       */
      enum KeyPolicy { UniqueKeys,     //!<  Constrain keys to be unique
                       DuplicateKeys   //!<  Allow duplication of keys
                     };

      /** Constructor */
      CollectorMap() : _keyPolicy(UniqueKeys) { }

      /**
       * @brief Allows the user to choose if keys can be duplicated
       *
       * This constructor is provided to the user that wants to explicity define how
       * the keys, namely insertions are managed.  The default is unique keys in the
       * noop constructor...this one allows instantiation of either policy.
       *
       * @param keyPolicy   Can be UniqueKeys or DuplicateKeys
       */
      CollectorMap(const KeyPolicy &keyPolicy) : _keyPolicy(keyPolicy) { }

      /**  Destructor handles removal of the elements within the collection
       *
       * This must take into account the removal strategy and apply to any
       * remaining elements.
       */
      virtual ~CollectorMap() {
        selfDestruct();
      }

      /**
       * @brief Copy constructor invokes the copy policy as provided by the users
       *
       * This copy constructor will transfer the map of an incoming CollectorMap to
       * a newly created one.  This process employs the user selectable CopyPolicy.
       * It invokes the copy() method exposed in the copy policy.
       *
       * @param cmap The CollectorMap to be copied
       */
      CollectorMap(const CollectorMap &cmap) {
        _keyPolicy = cmap._keyPolicy;
        CollectorConstIter cItr;
        for(cItr = cmap._list.begin() ; cItr != cmap._list.end() ; cItr++) {
          _list.insert(std::make_pair(cItr->first, cmap.copy(cItr->second)));
        }
      }

      /**
       * @brief Assignment operator for the CollectorMap class object
       *
       * This object assignment operator is provided to properly handle the copying
       * of CollectorMap elements to a new instantiation.  This implements the
       * CopyPolicy for each element in the @b cmap object to the current one.  This
       * is a two step operation:  first destroy any elements that exist in the
       * destination object (using the RemovalPolicy) and then copy all elements
       * from the @b cmap object to the current one using the copy() method exposed
       * in the CopyPolicy.
       *
       * @param cmap The CollectorMap to be copied
       *
       * @returns Pointer to this CollectorMap
       */
      CollectorMap &operator=(const CollectorMap &cmap) {
        if(&cmap != this) {
          selfDestruct();
          _keyPolicy = cmap._keyPolicy;
          CollectorConstIter cItr;
          for(cItr = cmap._list.begin() ; cItr != cmap._list.end() ; cItr++) {
            _list.insert(std::make_pair(cItr->first, cmap.copy(cItr->second)));
          }
        }
        return (*this);
      }

      /**
       * Returns the size of the collection
       *
       * @return int Number of elements in collection
       */
      int size() const {
        return (_list.size());
      }

      /**
       * @brief Returns the number of keys found in the list
       *
       * For unique keys, this will always be 1.  If duplicate keys are allowed,
       * this will return the number of keys in the container.
       *
       * @param key  Key to return count for
       *
       * @return int Number keys in container
       */
      int count(const K &key) const {
        return (_list.count(key));
      }

      /**
       * Adds the element to the list.
       *
       * If the element exists and the key policy is restricted to uniqueness, it is
       * replaced after the removal strategy is applied.  If it doesn't exist, it is
       * inserted into the list.  For duplicate keys, it is simply inserted.
       *
       * @param key  Key in the associative map for the value
       * @param value Value to be associated with the key
       */
      void add(const K &key, const T &value) {
        if(_keyPolicy == UniqueKeys) remove(key);
        _list.insert(std::make_pair(key, value));
        return;
      }

      /**
       * Checks the existance of a particular key in the list
       * @param key Key to search for in the list
       * @return bool True if the key exists, false otherwise
       */
      bool exists(const K &key) const {
        CollectorConstIter cItr = _list.find(key);
        return (cItr != _list.end());
      }

      /**
       * @brief Returns the value associated with the name provided
       *
       * If the specifed name and value does not exist in the list, an out_of_range
       * exception is thrown.  Use @b exists to predetermine of the value is in the
       * list.
       *
       * @param key Key to fetch the value for
       * @return T Value associated with name
       * @throws IException if the value is not found
       */
      T &get(const K &key) {
        CollectorIter cItr = _list.find(key);
        if(cItr == _list.end()) {
          QString mess = "Requested value does not exist!";
          throw IException(IException::Programmer, mess, _FILEINFO_);
        }
        return (cItr->second);
      }

      /**
       * @brief Const version returning the value associated with the given name
       *
       * @param key Key to fetch the value for
       *
       * @returns The value associated with the given name
       */
      const T &get(const K &key) const {
        CollectorConstIter cItr = _list.find(key);
        if(cItr == _list.end()) {
          QString mess = "Requested value does not exist!";
          throw IException(IException::Programmer, mess, _FILEINFO_);
        }
        return (cItr->second);
      }

      /**
       * @brief Returns the index of the first occuring element in the list
       *
       * This returns the index such that the getNth() methods would retrieve the
       * element with key.  For duplicate keys, it is garaunteed to return the first
       * element.  It will return -1 if the element is not in the list.
       *
       * @param key Key to fetch the value for
       *
       * @return int Zero-based index of (first) element with key.  If it doesn't
       *         exist, -1 is returned.
       */
      int index(const K &key) const {
        CollectorConstIter cItr = _list.lower_bound(key);
        if(cItr == _list.end()) {
          return (-1);
        }
        else {
          return (std::distance(_list.begin(), cItr));
        }
      }

      /**
       * @brief Returns the nth value in the collection
       *
       * If the specifed value does not exist in the list, an out_of_range exception
       * is thrown.  Use @b size() to predetermine if the range is valid.
       *
       * @param nth Return the Nth value in the list
       *
       * @return T Value associated with name
       */
      T &getNth(int nth) {
        CollectorIter cItr;
        int i;
        for(cItr = _list.begin(), i = 0 ; cItr != _list.end() ; ++cItr, i++) {
          if(i == nth) break;
        }

        if(cItr == _list.end()) {
          std::ostringstream mess;
          mess << "Requested index (" << nth << ") out of range" << std::endl;
          throw IException(IException::Programmer, mess.str(), _FILEINFO_);
        }
        return (cItr->second);
      }

      /**
        * @brief Returns the nth value in the collection
        *
        * If the specifed value does not exist in the list, an out_of_range exception
        * is thrown.  Use @b size() to predetermine if the range is valid.
        *
        * @param nth Return the Nth value in the list
        *
        * @return T Value associated with name
        */
      const T &getNth(int nth) const {
        CollectorConstIter cItr;
        int i;
        for(cItr = _list.begin(), i = 0 ; cItr != _list.end() ; ++cItr, i++) {
          if(i == nth) break;
        }
        if(cItr == _list.end()) {
          std::ostringstream mess;
          mess << "Requested index (" << nth << ") out of range" << std::endl;
          throw IException(IException::Programmer, mess.str(), _FILEINFO_);
        }
        return (cItr->second);
      }

      /**
        * @brief Returns the nth key in the collection
        *
        * If the specifed key does not exist in the list, an out_of_range exception
        * is thrown.  Use @b size() to predetermine if the range is valid.
        *
        * @param nth Return the Nth key in the list
        *
        * @return K Key associated with name
        */
      const K &key(int nth) const {
        CollectorConstIter cItr;
        int i;
        for(cItr = _list.begin(), i = 0 ; cItr != _list.end() ; ++cItr, i++) {
          if(i == nth) break;
        }
        if(cItr == _list.end()) {
          std::ostringstream mess;
          mess << "Requested key index (" << nth << ") out of range" << std::endl;
          throw IException(IException::Programmer, mess.str(), _FILEINFO_);
        }
        return (cItr->first);
      }

      /**
       * Removes and entry from the list
       *
       * @param key Name of key/value pair to remove from the list
       *
       * @return int Number of elements erased
       */
      int remove(const K &key) {
        CollectorIter Itr1 = _list.lower_bound(key);
        if(Itr1 == _list.end()) return (0);

        CollectorIter Itr2 = _list.upper_bound(key);
        while(Itr1 != Itr2) {
          this->destroy(&Itr1->second);
          ++Itr1;
        }
        return (_list.erase(key));
      }

      /**
       * Const iterator into list
       *
       * @return CollectorConstIter Returns a const iterator to the list
       */
      CollectorConstIter begin() const {
        return _list.begin();
      }

      /**
       * Const iterator to end of list
       *
       * @return CollectorConstIter  Returns the const end of the list
       */
      CollectorConstIter end() const {
        return _list.end();
      }

      /**
       * Returns the start of the list for iterating purposes
       *
       * @return CollectorIter Returns an iterator on the collection
       */
      CollectorIter begin() {
        return _list.begin();
      }

      /**
       * Returns the end of the list
       *
       * @return CollectorIter Returns the end of the list for determining the end
       *         of the iteration loop
       */
      CollectorIter end() {
        return _list.end();
      }

    private:
      KeyPolicy      _keyPolicy;  //!<  Unique or duplicate key constraint
      CollectorList  _list;       //!< The list

      /**
       * @brief Thourough destruction of list
       *
       * This method iterates through each element in the list applying the
       * RemovalPolicy to each value in the map.  It then clears the internal list
       * for subsequent reuse if needed.
       */
      void selfDestruct() {
        CollectorIter itr;
        for(itr = _list.begin() ; itr != _list.end() ; itr++) {
          this->destroy(&itr->second);
        }
        _list.clear();
      }

  };

};
#endif


/*  Copyright 2017 International Business Machines Corporation

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

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

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

#pragma once

#include "kernelpp/config.h"

#include <gsl.h>
#include <memory>

#if defined(kernelpp_WITH_CUDA)
#   define checkCudaErrors(err)  __checkCudaErrors (err, __FILE__, __LINE__)
#   define checkCudaLastError()  __checkLastCudaError (__FILE__, __LINE__)
#endif

namespace kernelpp
{
    /*  Initialize the cuda runtime, and ensure at least
     *  one device is available.
     */
    bool init_cudart();

    /*  `device_ptr<T>` is a smart pointer which owns and manages one
     *  or more objects of type `T` in contiguous memory on a CUDA device,
     *  and disposes of these objects when it goes out of scope.
     */
    template<typename T> class device_ptr final
    {
        struct cuda_deleter;
        std::unique_ptr<T, cuda_deleter> m_ptr;
        size_t m_size;

      public:
        device_ptr();
        device_ptr(size_t n);
        device_ptr(device_ptr<T>&& other);
        device_ptr(gsl::span<T> span);

        gsl::span<T> span();
        const gsl::span<T> span() const;

        T* get();

        void copy_from(const gsl::span<T> from);
        void copy_to(gsl::span<T> to) const;
    };
}

#if defined(kernelpp_WITH_CUDA) && defined(__CUDACC__)
#   include "cuda_util-inl.cuh"
#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 uuid_93a5858f_7040_4f67_9767_808d2a4f7ff9_h
#define uuid_93a5858f_7040_4f67_9767_808d2a4f7ff9_h

#include <gslib/type.h>
#include <gslib/string.h>

__gslib_begin__

struct uuid_timestamp
{
    byte        tm_sec;
    byte        tm_min;
    byte        tm_hour;
    byte        tm_mday;    /* day of month */
    byte        tm_mon;
    byte        tm_wday;    /* day of week */
    int16       tm_year;
    int16       tm_yday;    /* day of year */
    int32       tm_fraction;
};

enum uuid_version
{
    uuid_v1,                /* date-time & MAC address */
    uuid_v2,                /* DCE security */
    uuid_v3,                /* MD5 hash & namespace */
    uuid_v4,                /* random */
    uuid_v5,                /* SHA-1 hash & namespace */
    uuid_ver_default = uuid_v4,
    uuid_ver_invalid = uuid_v2,
};

struct uuid_raw
{
    uint        data1;
    uint16      data2;
    uint16      data3;
    byte        data4[8];
};

class uuid:
    protected uuid_raw
{
public:
    uuid();
    uuid(uuid_version ver) { generate(ver); }
    uuid(uuid_version ver, const gchar* name, int len) { generate(ver, name, len); }
    uuid(const string& str) { from_string(str.c_str(), str.length()); }
    uuid(const gchar* str, int len) { from_string(str, len); }
    bool operator==(const uuid& that) const;
    bool is_valid() const;
    void generate(uuid_version ver);
    void generate(uuid_version ver, const gchar* name, int len);
    void from_string(const gchar* str, int len);
    const gchar* to_string(string& str) const;
    void get_timestamp(uuid_timestamp& ts) const;
};

__gslib_end__

#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.
 */

#ifndef rasm_150d55c1_d63d_40ea_8fd2_9bc280975fe0_h
#define rasm_150d55c1_d63d_40ea_8fd2_9bc280975fe0_h

#include <gslib\pool.h>
#include <gslib\string.h>
#include <gslib\std.h>
#include <rathen\config.h>

/*
 * @ 2013.02.26
 * New assemble tool for rathen. This version we gonna solve the problem that last version of rasm could not support labeled jump.
 * In this version, every chuck of assembly code would be a separate and independent node, linked to a whole code segment, and the
 * label would be a particular node in the tree. In this version, we would not need vslen to do the linkage stuff any more.
 * And the short jump would be available.
 * Plus, in this way, we could see the machine code correspond to every single assemble instruction, and yet we've been very close
 * to a disassemble toolkit, but there were still some problems. The rathen didn't use all the x86 machine code. This assemble tool
 * just concerned about a subset of all the x86 machine codes. I didn't intend to provide such a tool to decode all the machine codes
 * into assemble, or rathen source code, either. As the optimize work we might probably did in the future, this decode works would
 * be a hard problem, which was considered unnecessary.
 */

__rathen_begin__

enum reg
{
    _regzr = 0,
    _eax = 1,
    _ecx = 2,
    _edx,
    _ebx,
    _esp,
    _ebp,
    _esi,
    _edi,
    /* more to come... */
};

#ifdef _GS_X86

inline byte get_register_index(reg r)
{
    switch(r)
    {
    case _eax:      return 0;
    case _ecx:      return 1;
    case _edx:      return 2;
    case _ebx:      return 3;
    case _esp:      return 4;   /* why "no index" */
    case _ebp:      return 5;
    case _esi:      return 6;
    case _edi:      return 7;
    default:        assert(0);
    }
    return -1;
}

typedef const gchar*    lab;

#define __rasm_make_type(tname, dname) \
    struct tname \
    { \
        dname       _data; \
        \
        explicit tname(dname d = (dname)0) { _data = d; } \
        bool operator == (const tname& d) const { return _data == d._data; } \
        dname inner_data() const { return _data; } \
    }

__rasm_make_type(_bias_, int);
__rasm_make_type(_bias8_, int8);
__rasm_make_type(_bias16_, int16);
__rasm_make_type(_mem_, int);
__rasm_make_type(_reg_, reg);

enum cccc
{
    cccc_o = 0,
    cccc_no,
    cccc_c,
    cccc_nc,
    cccc_e,
    cccc_ne,
    cccc_be,
    cccc_a,
    cccc_s,
    cccc_ns,
    cccc_p,
    cccc_np,
    cccc_l,
    cccc_ge,
    cccc_le,
    cccc_g,
};

struct __gs_novtable rasm_instr abstract
{
    virtual ~rasm_instr() {}
    virtual const gchar* get_name() const = 0;
    virtual bool finalize(vessel& vsl) = 0;
    virtual void to_string(string& str) const = 0;
    virtual bool compare(const rasm_instr* that) const = 0;
};

inline const gchar* nameofreg(reg r)
{
    switch(r)
    {
    case _eax:  return _t("eax");
    case _ecx:  return _t("ecx");
    case _edx:  return _t("edx");
    case _ebx:  return _t("ebx");
    case _esp:  return _t("esp");
    case _ebp:  return _t("ebp");
    case _esi:  return _t("esi");
    case _edi:  return _t("edi");
    }
    return 0;
}

template<class _in>
struct rasm_instr0:
    public _in
{
    virtual void to_string(string& str) const override { print(str, get_name(), _t(";")); }
    virtual bool compare(const rasm_instr* that) const override { return strtool::compare(get_name(), that->get_name()) == 0; }
    inline void print(string& str, const gchar* p, const gchar* a) const
    {
        if(p) { str += p; }
        if(a) { str += a; }
    }
    inline void print(string& str, int n, const gchar* a) const
    {
        string s;
        s.format(_t("%08x"), n);
        str += s;
        if(a) { str += a; }
    }
    inline void print(string& str, reg n, const gchar* a) const
    {
        string s;
        s.format(_t("[%s]"), nameofreg(n));
        str += s;
        if(a) { str += a; }
    }
    inline void print(string& str, _bias8_ n, const gchar* a) const
    {
        string s;
        s.format(_t("[s8:%02x]"), (int)n.inner_data());
        str += s;
        if(a) { str += a; }
    }
    inline void print(string& str, _bias16_ n, const gchar* a) const
    {
        string s;
        s.format(_t("[s16:%04x]"), (int)n.inner_data());
        str += s;
        if(a) { str += a; }
    }
    inline void print(string& str, _bias_ n, const gchar* a) const
    {
        string s;
        s.format(_t("[s:%08x]"), (int)n.inner_data());
        str += s;
        if(a) { str += a; }
    }
    inline void print(string& str, _mem_ n, const gchar* a) const
    {
        string s;
        s.format(_t("[m:%08x]"), (int)n.inner_data());
        str += s;
        if(a) { str += a; }
    }
    inline void print(string& str, _reg_ n, const gchar* a) const
    {
        string s;
        s.format(_t("[%s]"), nameofreg(n.inner_data()));
        str += s;
        if(a) { str += a; }
    }
};

template<class c1>
struct rasm_instr1:
    public rasm_instr0<rasm_instr>
{
    typedef c1  type1;
    typedef rasm_instr1<c1> myref;

    c1              _data1;

    rasm_instr1(c1 a) { _data1 = a; }
    virtual void to_string(string& str) const override
    {
        print(str, get_name(), _t("  "));
        print(str, _data1, _t(";"));
    }
    virtual bool compare(const rasm_instr* that) const override
    {
        if(strtool::compare(get_name(), that->get_name()) != 0)
            return false;
        const myref* ptr = static_cast<const myref*>(that);
        return _data1 == ptr->_data1;
    }
};

struct __gs_novtable rasm_link abstract:
    public rasm_instr
{
    virtual bool is_linker() const = 0;
    virtual const string& get_label() const = 0;
};

struct rasm_linker;

struct rasm_label:
    public rasm_instr0<rasm_link>
{
    typedef rasm_linker linker;
    typedef gs::vector<linker*> linker_list;

    string          _label;
    linker_list     _linkers;

public:
    rasm_label(lab l) { _label.assign(l); }
    virtual const gchar* get_name() const override { return _t("label"); }
    virtual bool finalize(vessel& vsl) override { return true; }
    virtual void to_string(string& str) const override;
    virtual bool compare(const rasm_instr* that) const override;
    virtual bool is_linker() const override { return false; }
    virtual const string& get_label() const override { return _label; }

public:
    int get_linker_ctr() const { return (int)_linkers.size(); }
    rasm_linker* get_linker(int i) { return _linkers.at(i); }
    void add_linker(linker* p) { _linkers.push_back(p); }
};

struct rasm_linker:
    public rasm_instr0<rasm_link>
{
    typedef lab type1;
    typedef rasm_linker linker;
    typedef gs::vector<linker*> linker_list;

    string          _label;
    int             _linkeeid;
    linker_list     _affects;

public:
    rasm_linker(lab l): _linkeeid(0) { _label.assign(l); }
    virtual void to_string(string& str) const override;
    virtual bool compare(const rasm_instr* that) const override;
    virtual bool is_linker() const override { return true; }
    virtual const string& get_label() const override { return _label; }

public:
    void set_linkee_index(int i) { _linkeeid = i; }
    int get_linkee_index() const { return _linkeeid; }
    void add_affects(linker* p) { _affects.push_back(p); }
};

template<class c1, class c2>
struct rasm_instr2:
    public rasm_instr1<c1>
{
    typedef c2  type2;
    typedef rasm_instr2<c1, c2> myref;

    c2              _data2;

    rasm_instr2(c1 a, c2 b): rasm_instr1(a) { _data2 = b; }
    virtual void to_string(string& str) const override
    {
        print(str, get_name(), _t("  "));
        print(str, _data1, _t(","));
        print(str, _data2, _t(";"));
    }
    virtual bool compare(const rasm_instr* that) const override
    {
        if(strtool::compare(get_name(), that->get_name()) != 0)
            return false;
        const myref* ptr = static_cast<const myref*>(that);
        return _data1 == ptr->_data1 && _data2 == ptr->_data2;
    }
};

template<class c1, class c2, class c3>
struct rasm_instr3:
    public rasm_instr2<c1, c2>
{
    typedef c3  type3;
    typedef rasm_instr3<c1, c2, c3> myref;

    c3              _data3;

    rasm_instr3(c1 a, c2 b, c3 c): rasm_instr2(a, b) { _data3 = c; }
    virtual void to_string(string& str) const override
    {
        print(str, get_name(), _t("  "));
        print(str, _data1, _t(","));
        print(str, _data2, _t(","));
        print(str, _data3, _t(";"));
    }
    virtual bool compare(const rasm_instr* that) const override
    {
        if(strtool::compare(get_name(), that->get_name()) != 0)
            return false;
        const myref* ptr = static_cast<const myref*>(that);
        return _data1 == ptr->_data1 && _data2 == ptr->_data2 && _data3 == ptr->_data3;
    }
};

#define rasm_reginstr(in) \
    static rasm_instr* make_##in(); \
    void in() { _list.push_back(make_##in()); }

#define rasm_reginstr_i_begin(in, c1) \
    static rasm_instr* make_##in(c1); \
    void in(c1 a) {

#define rasm_reginstr_i_end(in) \
    _list.push_back(make_##in(a)); }

#define rasm_reginstr_i(in, c1) \
    rasm_reginstr_i_begin(in, c1) \
    rasm_reginstr_i_end(in)

#define rasm_reginstr_ii_begin(in, c1, c2) \
    static rasm_instr* make_##in(c1, c2); \
    void in(c1 a, c2 b) {

#define rasm_reginstr_ii_end(in) \
    _list.push_back(make_##in(a, b)); }

#define rasm_reginstr_ii(in, c1, c2) \
    rasm_reginstr_ii_begin(in, c1, c2) \
    rasm_reginstr_ii_end(in)

#define rasm_reginstr_iii_begin(in, c1, c2, c3) \
    static rasm_instr* make_##in(c1, c2, c3); \
    void in(c1 a, c2 b, c3 c) {

#define rasm_reginstr_iii_end(in) \
    _list.push_back(make_##in(a, b, c)); }

#define rasm_reginstr_iii(in, c1, c2, c3) \
    rasm_reginstr_iii_begin(in, c1, c2, c3) \
    rasm_reginstr_iii_end(in)

#define rasm_reginstr_lab(in, c1) \
    static rasm_instr* make_##in(c1); \
    void in(c1 a) \
    { \
        rasm_label* ptr = static_cast<rasm_label*>(make_##in(a)); \
        assert(ptr && "alloc failed."); \
        _list.push_back(ptr); \
        verify(_label.insert(std::make_pair(ptr->get_label(), get_last_instruction_index())).second && "insert failed."); \
    }

#define rasm_reginstr_lnk(in, c1) \
    static rasm_instr* make_##in(c1); \
    void in(c1 a) \
    { \
        rasm_linker* ptr = static_cast<rasm_linker*>(make_##in(a)); \
        assert(ptr && "alloc failed."); \
        _list.push_back(ptr); \
        _link.push_back(ptr); \
    }

/*
 * Only the 8 - bit offsets need this optimization, but DONOT apply such kind of optimizations to immediate operands,
 * that might cause problems like "mov eax, 1" become "mov ah, 1", which was incorrect
 */
#define rasm_optimize_b8(calling, bparam) \
    if((int)bparam.inner_data() > (int)-0x80 && (int)bparam.inner_data() < (int)0x80) \
        return calling;

#define rasm_optimize_b16(calling, bparam) \
    if((int)bparam.inner_data() > (int)-0x8000 && (int)bparam.inner_data() < (int)0x8000) \
        return calling;

/*
 * If a instruction was operated on the eax register, it could be optimized like:
 */
#define rasm_optimize_acc(calling, rparam) \
    if(rparam == _eax) \
        return calling;

/*
 * Optimize for inc or dec
 */
#define rasm_optimize_i1(calling, iparam) \
    if(iparam == 1) \
        return calling;

/*
 * Optimize for immediate operants less than 1 byte. Instructions like loop need this optimization
 */
#define rasm_optimize_i8(calling, iparam) \
    if((int)iparam > (int)-0x80 && (int)iparam < (int)0x80) \
        return calling;

class rasm
{
public:
    typedef gs::vector<rasm_instr*> instr_list;
    typedef gs::vector<rasm_link*> link_list;
    typedef gs::unordered_map<gs::string, int> label_map;       /* label name => index in instruction list */

public:
    rasm() {}
    ~rasm();
    void print(string& str) const;
    int finalize(vessel& vsl);
    int get_last_instruction_index() const;
    //link get_label(lab l) { return _label.find(l); }
    //link no_label() { return _label.end(); }
    //const_link get_label(lab l) const { return _label.find(l); }
    //const_link no_label() const { return _label.end(); }
    //iterator get_current() { return _list.end(); }
    //const_iterator get_current() const { return _list.end(); }
    //iterator get_head() { return _list.begin(); }
    //const_iterator get_head() const { return _list.begin(); }
    //void attach(const_iterator pos, rasm& other);
    //iterator tick() { return _list.size() ? -- _list.end() : _list.end(); }
    //void tick(iterator& i) { i = tick(); }

protected:
    instr_list      _list;
    label_map       _label;
    link_list       _link;

public:
    rasm_reginstr_ii(mov, reg, int8);
    rasm_reginstr_ii(mov, reg, int);
    rasm_reginstr_ii(mov, reg, reg);
    rasm_reginstr_ii(mov, reg, _mem_);
    rasm_reginstr_ii(mov, reg, _bias8_);
    rasm_reginstr_ii_begin(mov, reg, _bias_)
        rasm_optimize_b8(mov(a, (_bias8_)b.inner_data()), b)
        rasm_reginstr_ii_end(mov);
    rasm_reginstr_ii(mov, _mem_, reg);
    rasm_reginstr_ii(mov, _bias8_, reg);
    rasm_reginstr_ii_begin(mov, _bias_, reg)
        rasm_optimize_b8(mov((_bias8_)a.inner_data(), b), a)
        rasm_reginstr_ii_end(mov);
    rasm_reginstr_ii(mov, _mem_, int);
    rasm_reginstr_ii(mov, _bias8_, int);
    rasm_reginstr_ii_begin(mov, _bias_, int)
        rasm_optimize_b8(mov((_bias8_)a.inner_data(), b), a)
        rasm_reginstr_ii_end(mov);
    rasm_reginstr_ii(mov, reg, _reg_);
    rasm_reginstr_ii(mov, _reg_, reg);
    rasm_reginstr_ii(lea, reg, _bias8_);
    rasm_reginstr_ii_begin(lea, reg, _bias_)
        rasm_optimize_b8(lea(a, (_bias8_)b.inner_data()), b)
        rasm_reginstr_ii_end(lea);
    rasm_reginstr(nop);
    rasm_reginstr(hlt);
    rasm_reginstr_i(call, int);
    rasm_reginstr_i(call, reg);
    rasm_reginstr_i(call, _bias_);
    rasm_reginstr_lnk(call, lab);
    rasm_reginstr(ret);
    rasm_reginstr_i(ret, int);
    rasm_reginstr_i(push, reg);
    rasm_reginstr_i(push, int8);
    rasm_reginstr_i(push, int);
    rasm_reginstr_i(push, _mem_);
    rasm_reginstr_i(push, _bias8_);
    rasm_reginstr_i_begin(push, _bias_)
        rasm_optimize_b8(push((_bias8_)a.inner_data()), a)
        rasm_reginstr_i_end(push);
    rasm_reginstr_i(push, _reg_);
    rasm_reginstr_i(pushw, _bias8_);
    rasm_reginstr_i_begin(pushw, _bias_)
        rasm_optimize_b8(pushw((_bias8_)a.inner_data()), a)
        rasm_reginstr_i_end(pushw);
    rasm_reginstr(pushad);
    rasm_reginstr_i(pop, reg);
    rasm_reginstr_i(pop, _mem_);
    rasm_reginstr_i(pop, _bias8_);
    rasm_reginstr_i_begin(pop, _bias_)
        rasm_optimize_b8(pop((_bias8_)a.inner_data()), a)
        rasm_reginstr_i_end(pop);
    rasm_reginstr(popad);
    rasm_reginstr_lab(label, lab);
    rasm_reginstr_i(jmp, _mem_);
    rasm_reginstr_i(jmp, _bias8_);
    rasm_reginstr_i_begin(jmp, _bias_)
        rasm_optimize_b8(jmp((_bias8_)a.inner_data()), a)
        rasm_reginstr_i_end(jmp);
    rasm_reginstr_i(jmp, reg);
    rasm_reginstr_i(jmps, _bias8_);         /* jmp bias from ss */
    rasm_reginstr_i_begin(jmps, _bias_)
        rasm_optimize_b8(jmps((_bias8_)a.inner_data()), a)
        rasm_reginstr_i_end(jmps);
    //rasm_reginstr_i(jmp, lab);
    rasm_reginstr_lnk(jmp, lab);
    rasm_reginstr_i(jes, _bias8_);
    rasm_reginstr_i_begin(jes, _bias_)
        rasm_optimize_b8(jes((_bias8_)a.inner_data()), a)
        rasm_reginstr_i_end(jes);
    rasm_reginstr_i(jnes, _bias8_);
    rasm_reginstr_i_begin(jnes, _bias_)
        rasm_optimize_b8(jnes((_bias8_)a.inner_data()), a)
        rasm_reginstr_i_end(jnes);
    rasm_reginstr_i(loop, int8);
    rasm_reginstr_i_begin(loop, int)        /* I'm not sure about this, can the loop instruction operate on a short? */
        rasm_optimize_i8(loop((int8)a), (a - 2))
        rasm_reginstr_i_end(loop);
    rasm_reginstr_lnk(loop, lab);
    rasm_reginstr(cld);
    rasm_reginstr(std);
    rasm_reginstr(movsb);
    rasm_reginstr(movsw);
    rasm_reginstr(movs);
    rasm_reginstr(rep);
    rasm_reginstr_ii(cmp, _bias_, int);
    rasm_reginstr_ii(cmp, reg, int8);
    rasm_reginstr_ii(cmp, reg, int);
    rasm_reginstr_ii(cmp, reg, _bias8_);
    rasm_reginstr_ii_begin(cmp, reg, _bias_)
        rasm_optimize_b8(cmp(a, (_bias8_)b.inner_data()), b)
        rasm_reginstr_ii_end(cmp);
    rasm_reginstr_ii(cmp, _reg_, int8);
    rasm_reginstr_ii(cmp, _reg_, int);
    rasm_reginstr_ii(cmp, reg, _reg_);
    rasm_reginstr_i(inc, reg);
    rasm_reginstr_i(inc, _mem_);
    rasm_reginstr_i(inc, _bias8_);
    rasm_reginstr_i_begin(inc, _bias_)
        rasm_optimize_b8(inc((_bias8_)a.inner_data()), a)
        rasm_reginstr_i_end(inc);
    rasm_reginstr_ii(add, reg, reg);
    rasm_reginstr_ii(add, reg, int8);
    rasm_reginstr_ii(add, reg, int);
    rasm_reginstr_i(add, int);
    rasm_reginstr_ii(add, reg, _mem_);
    rasm_reginstr_ii(add, reg, _bias8_);
    rasm_reginstr_ii(add, reg, _bias_);
    rasm_reginstr_ii(add, _mem_, int8);
    rasm_reginstr_ii(add, _mem_, int);
    rasm_reginstr_ii(add, _bias8_, int8);
    rasm_reginstr_ii(add, _bias_, int8);
    rasm_reginstr_ii(add, _bias8_, int);
    rasm_reginstr_ii(add, _bias_, int);
    rasm_reginstr_ii(add, _mem_, reg);
    rasm_reginstr_ii(add, _bias8_, reg);
    rasm_reginstr_ii(add, _bias_, reg);
    rasm_reginstr_ii(add, reg, _reg_);
    rasm_reginstr_ii(add, _reg_, reg);
    rasm_reginstr_i(dec, reg);
    rasm_reginstr_i(dec, _mem_);
    rasm_reginstr_i(dec, _bias_);
    rasm_reginstr_ii(sub, reg, reg);
    rasm_reginstr_ii(sub, reg, int);
    rasm_reginstr_i(sub, int);
    rasm_reginstr_ii(sub, reg, _mem_);
    rasm_reginstr_ii(sub, reg, _bias_);
    rasm_reginstr_ii(sub, _mem_, int);
    rasm_reginstr_ii(sub, _bias_, int);
    rasm_reginstr_ii(sub, _mem_, reg);
    rasm_reginstr_ii(sub, _bias_, reg);
    rasm_reginstr_ii(sub, reg, _reg_);
    rasm_reginstr_ii(sub, _reg_, reg);
    rasm_reginstr_iii(imul, reg, reg, int);
    rasm_reginstr_ii(imul, reg, int);
    rasm_reginstr_i(imul, reg);
    rasm_reginstr_ii(imul, reg, _mem_);
    rasm_reginstr_ii(imul, reg, _bias_);
    rasm_reginstr_iii(imul, reg, _mem_, int);
    rasm_reginstr_iii(imul, reg, _bias_, int);
    rasm_reginstr_ii(imul, reg, reg);
    rasm_reginstr_i(imul, _mem_);
    rasm_reginstr_i(imul, _bias_);
    rasm_reginstr_ii(imul, reg, _reg_);
    rasm_reginstr_i(idiv, reg);
    rasm_reginstr_i(idiv, _mem_);
    rasm_reginstr_i(idiv, _bias_);
    rasm_reginstr_i(idiv, _reg_);
    rasm_reginstr(cdq);
    rasm_reginstr_i(neg, reg);
    rasm_reginstr_i(not, reg);
    rasm_reginstr_ii(xor, reg, reg);
    rasm_reginstr_ii(xor, reg, int);
    rasm_reginstr_ii(xor, reg, _bias_);
    rasm_reginstr_ii(xor, reg, _reg_);
    rasm_reginstr_i(shl, reg);
    rasm_reginstr_ii(shl, reg, int);
    rasm_reginstr_i(shr, reg);
    rasm_reginstr_ii(shr, reg, int);
    //rasm_reginstr
    //setcc
    rasm_reginstr_ii(and, reg, int);
    rasm_reginstr_ii(and, reg, _bias_);
    rasm_reginstr_ii(and, reg, _reg_);
    rasm_reginstr_ii(or , reg, int);
    rasm_reginstr_ii(or , reg, _bias_);
    rasm_reginstr_ii(or , reg, _reg_);
};

#endif

__rathen_end__

#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 string_81448b68_30d7_4d64_a67a_cecef7c90139_h
#define string_81448b68_30d7_4d64_a67a_cecef7c90139_h

#include <string>
#include <assert.h>
#include <gslib/type.h>

__gslib_begin__

template<class _element>
class _std_string:
    public std::basic_string<_element, std::char_traits<_element>, std::allocator<_element> >
{
public:
    typedef _element protoch;
    typedef std::char_traits<protoch> prototr;
    typedef std::allocator<protoch> protoalloc;
    typedef std::basic_string<protoch, prototr, protoalloc> protoref;
    friend gs_export int _vsprintf(_std_string<char>& str, const char* fmt, va_list ap);
    friend gs_export int _vsprintf(_std_string<wchar>& str, const wchar* fmt, va_list ap);
};

template<class _element>
struct _string_tool
{
    typedef _element element;
};

template<>
struct _string_tool<char>
{
    static void copy(char* des, size_t size, const char* src) { strcpy_s(des, size, src); }
    static int length(const char* str) { return (int)strlen(str); }
    static int compare(const char* s1, const char* s2) { return (int)strcmp(s1, s2); }
    static int compare_cl(const char* s1, const char* s2) { return (int)_stricmp(s1, s2); }
    static int compare(const char* s1, const char* s2, int cnt) { return (int)strncmp(s1, s2, cnt); }
    static int compare_cl(const char* s1, const char* s2, int cnt) { return (int)_strnicmp(s1, s2, cnt); }
    static int vprintf(char* des, int size, const char* fmt, va_list ap) { return _vsprintf_s_l(des, size, fmt, 0, ap); }
    static int printf(char* des, int size, const char* fmt, ...)
    {
        va_list ptr;
        va_start(ptr, fmt);
        return _vsprintf_s_l(des, size, fmt, 0, ptr);
    }
    static int vsscanf(const char* src, const char* fmt, va_list ap) { return vsscanf_s(src, fmt, ap); }
    static int sscanf(const char* src, const char* fmt, ...)
    {
        va_list ptr;
        va_start(ptr, fmt);
        return vsscanf_s(src, fmt, ptr);
    }
    static int ctlprintf(const char* src, ...)
    {
        va_list ptr;
        va_start(ptr, src);
        return ::vprintf(src, ptr);
    }
    static int to_int(const char* src) { return atoi(src); }
    static int to_int(const char* src, int radix) { return strtol(src, 0, radix); }
    static uint to_uint(const char* src, int radix) { return strtoul(src, 0, radix); }
    static int64 to_int64(const char* src) { return _atoi64(src); }
    static int64 to_int64(const char* src, int radix) { return _strtoi64(src, 0, radix); }
    static uint64 to_uint64(const char* src, int radix) { return _strtoui64(src, 0, radix); }
    static real to_real(const char* src) { return atof(src); }
    static void from_int(int i, char* des, int size, int radix) { _itoa_s(i, des, size, radix); }
    static void from_int64(int64 i, char* des, int size, int radix) { _i64toa_s(i, des, size, radix); }
    static void from_real(char* des, int size, real d) { from_real(des, size, d, size-2); }
    static void from_real(char* des, int size, real d, int precis) { _gcvt_s(des, size, d, precis); }
    static void to_lower(char* str, int size) { _strlwr_s(str, size); }
    static void to_upper(char* str, int size) { _strupr_s(str, size); }
    static const char* find(const char* src, const char* tar) { return strstr(src, tar); }
    static int test(const char* src, const char* cpset) { return (int)strcspn(src, cpset); }
    static const char* _test(const char* src, const char* cpset) { return strpbrk(src, cpset); }
};

template<>
struct _string_tool<wchar>
{
    static void copy(wchar* des, size_t size, const wchar* src) { wcscpy_s(des, size, src); }
    static int length(const wchar* str) { return (int)wcslen(str); }
    static int compare(const wchar* s1, const wchar* s2) { return (int)wcscmp(s1, s2); }
    static int compare_cl(const wchar* s1, const wchar* s2) { return (int)_wcsicmp(s1, s2); }
    static int compare(const wchar* s1, const wchar* s2, int cnt) { return (int)wcsncmp(s1, s2, cnt); }
    static int compare_cl(const wchar* s1, const wchar* s2, int cnt) { return (int)_wcsnicmp(s1, s2, cnt); }
    static int vprintf(wchar* des, int size, const wchar* fmt, va_list ap) { return _vswprintf_s_l(des, size, fmt, 0, ap); }
    static int printf(wchar* des, int size, const wchar* fmt, ...)
    {
        va_list ptr;
        va_start(ptr, fmt);
        return _vswprintf_s_l(des, size, fmt, 0, ptr);
    }
    static int vsscanf(const wchar* src, const wchar* fmt, va_list ap) { return vswscanf_s(src, fmt, ap); }
    static int sscanf(const wchar* src, const wchar* fmt, ...)
    {
        va_list ptr;
        va_start(ptr, fmt);
        return vswscanf_s(src, fmt, ptr);
    }
    static int ctlprintf(const wchar* src, ...)
    {
        va_list ptr;
        va_start(ptr, src);
        return ::vwprintf(src, ptr);
    }
    static int to_int(const wchar* src) { return _wtoi(src); }
    static int to_int(const wchar* src, int radix) { return wcstol(src, 0, radix); }
    static uint to_uint(const wchar* src, int radix) { return wcstoul(src, 0, radix); }
    static int64 to_int64(const wchar* src) { return _wtoi64(src); }
    static int64 to_int64(const wchar* src, int radix) { return _wcstoi64(src, 0, radix); }
    static uint64 to_uint64(const wchar* src, int radix) { return _wcstoui64(src, 0, radix); }
    static real to_real(const wchar* src) { return _wtof(src); }
    static void from_int(int i, wchar* des, int size, int radix) { _itow_s(i, des, size, radix); }
    static void from_int64(int64 i, wchar* des, int size, int radix) { _i64tow_s(i, des, size, radix); }
    static void from_real(wchar* des, int size, real d) { from_real(des, size, d, size-2); }
    static void from_real(wchar* des, int size, real d, int precis)
    {
        _gcvt_s((char*)des, size, d, precis);
        int len = (int)strlen((char*)des); /* call it in asm wasn't available in /o2 */
        __asm
        {
            mov  ecx, len;
            mov  esi, des;
            add  esi, ecx;
            mov  edi, esi;
            add  edi, ecx;
            mov  word ptr[edi], 0;
            std;
        rptag:
            movsb;
            mov  byte ptr[edi], 0;
            dec  edi;
            loop rptag;
            cld;
        }
    }
    static void to_lower(wchar* str, int size) { _wcslwr_s(str, size); }
    static void to_upper(wchar* str, int size) { _wcsupr_s(str, size); }
    static const wchar* find(const wchar* src, const wchar* tar) { return wcsstr(src, tar); }
    static int test(const wchar* src, const wchar* cpset) { return (int)wcsspn(src, cpset); }
    static const wchar* _test(const wchar* src, const wchar* cpset) { return wcspbrk(src, cpset); }
};

gs_export extern const char* get_mbcs_elem(const char* str, uint& c);
gs_export extern const char* get_mbcs_elem(const char* str, uint& c, const char* end);
gs_export extern int convert_to_wide(wchar out[], int size, const char* str, int len = -1);
gs_export extern int convert_to_byte(char out[], int size, const wchar* str, int len = -1);
gs_export extern int convert_utf8_to_wide(wchar out[], int size, const char* str, int len = -1);
gs_export extern int convert_wide_to_utf8(char out[], int size, const wchar* str, int len = -1);

struct _string_caseful {};
struct _string_caseless {};

template<class _element,
    class _cpcase = _string_caseful>
class _string:
    public _std_string<_element>
{
public:
    typedef _element element;
    typedef _std_string<element> inheritref;
    typedef _string_tool<element> _strtool;
    typedef typename inheritref::protoref protoref;
    typedef _string<_element, _cpcase> myref;

    template<class _cpcase> static int comparefunc(const _string* s, const element* str);
    template<class _cpcase> static  int comparefunc(const _string* s, const element* str, int len);
    template<> static int comparefunc<_string_caseful>(const _string* s, const element* str) { return _strtool::compare(s->c_str(), str); }
    template<> static int comparefunc<_string_caseless>(const _string* s, const element* str) { return _strtool::compare_cl(s->c_str(), str); }
    template<> static int comparefunc<_string_caseful>(const _string* s, const element* str, int len) { return _strtool::compare(s->c_str(), str, len); }
    template<> static int comparefunc<_string_caseless>(const _string* s, const element* str, int len) { return _strtool::compare_cl(s->c_str(), str, len); }

protected:
#ifdef _MSC_VER
#if (_MSC_VER >= 1920)
    element* _stl_rawstr() { return const_cast<element*>(c_str()); }
    const element* _stl_rawstr() const { return c_str(); }
#elif (_MSC_VER >= 1914)
    element* _stl_rawstr() { return this->_Get_data()._Myptr(); }
    const element* _stl_rawstr() const { return this->_Get_data()._Myptr(); }
#else
    element* _stl_rawstr() { return this->_Myptr(); }
    const element* _stl_rawstr() const { return this->_Myptr(); }
#endif
#if (_MSC_VER >= 1920)
    int _stl_cap() const { return (int)capacity(); }
    void _stl_fix() { resize(_strtool::length(c_str())); }
#elif (_MSC_VER >= 1914)
    int& _stl_cap() { return (int&)this->_Get_data()._Myres; }
    int& _stl_size() { return (int&)this->_Get_data()._Mysize; }
    void _stl_fix() { this->_stl_size() = _strtool::length(this->c_str()); }
#elif ((_MSC_VER >= 1900) && (_MSC_VER < 1910))
    int& _stl_cap() { return (int&)this->_Myres(); }
    int& _stl_size() { return (int&)this->_Mysize(); }
    void _stl_fix() { this->_Mysize() = (size_t)_strtool::length(this->c_str()); }
#else
    int& _stl_cap() { return (int&)this->_Myres; }
    int& _stl_size() { return (int&)this->_Mysize; }
    void _stl_fix() { this->_Mysize = (size_t)_strtool::length(this->c_str()); }
#endif
#if (_MSC_VER >= 1920)
    void _stl_eos(int pos) { this->resize(pos); }
#else
    void _stl_eos(int pos) { this->_Eos(pos); }
#endif
#endif      /* endif _MSC_VER */

private:
    template<class _element>
    void convert_from(const wchar* str);
    template<class _element>
    void convert_from(const char* str);
    template<>
    void convert_from<wchar>(const wchar* str) { assign(str); }
    template<>
    void convert_from<char>(const wchar* str)
    {
        int len = convert_to_byte(0, 0, str);
        resize(len);
        convert_to_byte(_stl_rawstr(), len, str);
        _stl_fix();
    }
    template<>
    void convert_from<wchar>(const char* str)
    {
        int len = convert_to_wide(0, 0, str);
        resize(len);
        convert_to_wide(_stl_rawstr(), len, str);
        _stl_fix();
    }
    template<>
    void convert_from<char>(const char* str) { assign(str); }
    template<class _element>
    void convert_from(const char* str, int len);
    template<class _element>
    void convert_from(const wchar* str, int len);
    template<>
    void convert_from<wchar>(const wchar* str, int len) { assign(str, len); }
    template<>
    void convert_from<wchar>(const char* str, int len)
    {
        int l = convert_to_wide(0, 0, str, len);
        resize(l + 1);
        convert_to_wide(_stl_rawstr(), l, str, len);
        _stl_eos(l);
    }
    template<>
    void convert_from<char>(const wchar* str, int len)
    {
        int l = convert_to_byte(0, 0, str, len);
        resize(l + 1);
        convert_to_byte(_stl_rawstr(), l, str, len);
        _stl_eos(l);
    }
    template<>
    void convert_from<char>(const char* str, int len) { assign(str, len); }

public:
    _string() {}
    _string(const element* str) { if(str) this->assign(str); }
    _string(const element* str, int len) { if(str) this->assign(str, len); }
    _string(element c, int ctr) { this->assign(ctr, c); }
    void destroy() { clear(); }
    int length() const { return (int)this->size(); }
    element& front() { return this->at(0); }
    const element& front() const { return this->at(0); }
    element& back() { return this->at(this->size() - 1); }
    const element& back() const { return this->at(this->size() - 1); }
    element pop_back()
    {
        element e = back();
        resize(this->size() - 1);
        return e;
    }
    int test(const element* cpset) const
    {
        if(this->empty())
            return (int)this->npos;
        int pos = _strtool::test(this->c_str(), cpset);
        if(pos == length())
            return (int)this->npos;
        return pos;
    }
    int test(int off, const element* cpset) const
    {
        if(this->empty() || off >= length())
            return (int)this->npos;
        int pos = _strtool::test(this->c_str() + off, cpset);
        if(pos == length())
            return (int)this->npos;
        return pos;
    }
    const element* _test(const element* cpset) const
    {
        if(this->empty())
            return nullptr;
        return _strtool::_test(this->c_str(), cpset);
    }
    const element* _test(int off, const element* cpset) const
    {
        if(this->empty() || off >= length())
            return nullptr;
        return _strtool::_test(this->c_str() + off, cpset);
    }
    void format(const element* fmt, ...)
    {
        va_list ptr;
        va_start(ptr, fmt);
        _vsprintf(*this, fmt, ptr);
    }
    void formatv(const element* fmt, va_list ptr)
    {
        _vsprintf(*this, fmt, ptr);
    }
    /*
    void format(int size, const element* fmt, ...)
    {
        _stl_grow(size, false);
        va_list ptr;
        va_start(ptr, fmt);
        _stl_size() = _strtool::vprintf(_stl_rawstr(), _stl_cap(), fmt, ptr);
    }
    */
    _string& to_lower()
    {
        if(this->empty())
            return *this;
        _strtool::to_lower(_stl_rawstr(), _stl_cap());
        return *this;
    }
    _string& to_upper()
    {
        if(this->empty())
            return *this;
        _strtool::to_upper(_stl_rawstr(), _stl_cap());
        return *this;
    }
    int to_int() const { return _strtool::to_int(_stl_rawstr()); }
    int to_int(int radix) const { return _strtool::to_int(_stl_rawstr(), radix); }
    int64 to_int64() const { return _strtool::to_int64(_stl_rawstr()); }
    int64 to_int64(int radix) const { return _strtool::to_int64(_stl_rawstr(), radix); }
    real to_real() const { return _strtool::to_real(_stl_rawstr()); }
    _string& from(const char* str)
    {
        convert_from<element>(str);
        return *this;
    }
    _string& from(const wchar* str)
    {
        convert_from<element>(str);
        return *this;
    }
    _string& from(const char* str, int len)
    {
        convert_from<element>(str, len);
        return *this;
    }
    _string& from(const wchar* str, int len)
    {
        convert_from<element>(str, len);
        return *this;
    }
    _string& from_int(int i, int radix = 10)
    {
        resize(12);
        _strtool::from_int(i, _stl_rawstr(), _stl_cap(), radix);
        _stl_fix();
        return *this;
    }
    _string& from_int64(int64 i, int radix = 10)
    {
        resize(24);
        _strtool::from_int64(i, _stl_rawstr(), _stl_cap(), radix);
        _stl_fix();
        return *this;
    }
    _string& from_real(real d, int precis = 22)
    {
        resize(precis + 2);
        _strtool::from_real(_stl_rawstr(), _stl_cap(), d, precis);
        _stl_fix();
        return *this;
    }
    bool starts_with(const element* str, int len) const
    {
        assert(str);
        int s = length();
        if(len > s)
            return false;
        return compare(str, len) == 0;
    }
    bool ends_with(const element* str, int len) const
    {
        assert(str);
        int s = length();
        if(len > s)
            return false;
        return compare(str + s - len, len) == 0;
    }
    bool starts_with(const element* str) const { return starts_with(str, _strtool::length(str)); }
    bool starts_with(const myref& str) const { return starts_with(str.c_str(), str.length()); }
    bool starts_with(const myref& str, int len) const { return starts_with(str.c_str(), len); }
    bool ends_with(const element* str) const { return ends_with(str, _strtool::length(str)); }
    bool ends_with(const myref& str) const { return ends_with(str.c_str(), str.length()); }
    bool ends_with(const myref& str, int len) const { return ends_with(str.c_str(), len); }
    int compare(const element* str) const { return _strtool::compare(_stl_rawstr(), str); }
    int compare(const element* str, int len) const { return _strtool::compare(_stl_rawstr(), str, len); }
    int compare(int off, const element* str, int len) const { return _strtool::compare(_stl_rawstr() + off, str, len); }
    int compare_cl(const element* str) const { return _strtool::compare_cl(_stl_rawstr(), str); }
    int compare_cl(const element* str, int len) const { return _strtool::compare_cl(_stl_rawstr(), str, len); }
    int compare_cl(int off, const element* str, int len) const { return _strtool::compare_cl(_stl_rawstr() + off, str, len); }
    bool greater(const element* str) const { return comparefunc<_cpcase>(this, str) > 0; }
    bool greater(const element* str, int len) const { return comparefunc<_cpcase>(this, str, len) > 0; }
    bool equal(const element* str) const { return comparefunc<_cpcase>(this, str) == 0; }
    bool equal(const element* str, int len) const { return comparefunc<_cpcase>(this, str, len) == 0; }
    bool less(const element* str) const { return comparefunc<_cpcase>(this, str) < 0; }
    bool less(const element* str, int len) const { return comparefunc<_cpcase>(this, str, len) < 0; }
    bool operator < (const element* str) const { return less(str); }
    bool operator < (const _string* str) const { return less(str->c_str(), str->length()); }
    bool operator < (const _string& str) const { return less(str.c_str(), str.length()); }
    bool operator == (const element* str) const { return equal(str); }
    bool operator == (const _string* str) const { return equal(str->c_str(), str->length()); }
    bool operator == (const _string& str) const { return equal(str.c_str(), str.length()); }
    bool operator != (const element* str) const { return !equal(str); }
    bool operator != (const _string* str) const { return !equal(str->c_str(), str->length()); }
    bool operator != (const _string& str) const { return !equal(str.c_str(), str.length()); }
    bool operator > (const element* str) const { return greater(str); }
    bool operator > (const _string* str) const { return greater(str->c_str(), str->length()); }
    bool operator > (const _string& str) const { return greater(str.c_str(), str.length()); }
};

typedef _string_tool<gchar> strtool;

/*
 * string compare tool for short const string, notify that the s1 increase:
 * string symmetrical check
 */
template<class _element>
class strsymcheck
{
public:
    typedef _element mchar;
    typedef strsymcheck<mchar> mref;

public:
    template<int _len>
    static bool check(const mchar* s1, const mchar* s2) { return *s1 ++ == *s2 ++ && mref::check<_len-1>(s1, s2); }
    template<>
    static bool check<0>(const mchar* s1, const mchar* s2) { return true; }
    template<>
    static bool check<1>(const mchar* s1, const mchar* s2) { return *s1 ++ == *s2 ++; }
    template<int _len>
    static bool check_run(const mchar*& s1, const mchar* s2)
    {
        if(check<_len>(s1, s2)) {
            s1 += _len;
            return true;
        }
        return false;
    }
};

#ifdef _cstrcmp
#undef _cstrcmp
#endif
#define _cstrcmp(sr, cmp)    strsymcheck<gchar>::check<_cststrlen(cmp)>((sr), (cmp))

#define _cstrncmp(sr, len, cmp) \
    ((len >= _cststrlen(cmp)) && strsymcheck<gchar>::check<_cststrlen(cmp)>((sr), (cmp)))

#ifdef _cstrcmprun
#undef _cstrcmprun
#endif
#define _cstrcmprun(sr, cmp) strsymcheck<gchar>::check_run<_cststrlen(cmp)>((sr), (cmp))

#define mkstr(str1, str2) (((string(str1) += str2)).c_str())
#define mkstr3(str1, str2, str3) (((string(str1) += str2) += str3).c_str())
#define mkstr4(str1, str2, str3, str4) ((((string(str1) += str2) += str3) += str4).c_str())

template<int _cmpstr> inline
bool strequ(const char* str) { return *(const int*)str == _cmpstr; }
template<char _cmpch> inline
bool chequ(const char* str) { return *str == _cmpch; }
template<char _c1, char _c2>
struct chless { static const bool result = _c1 < _c2; };
template<char _c1, char _c2, bool _less = chless<_c1, _c2>::result>
struct chmin {};
template<char _c1, char _c2>
struct chmin<_c1, _c2, true> { static const char value = _c1; };
template<char _c1, char _c2>
struct chmin<_c1, _c2, false> { static const char value = _c2; };
template<char _c1, char _c2, bool _less = chless<_c1, _c2>::result>
struct chmax {};
template<char _c1, char _c2>
struct chmax<_c1, _c2, true> { static const char value = _c2; };
template<char _c1, char _c2>
struct chmax<_c1, _c2, false> { static const char value = _c1; };
template<int _len> inline
char strhit(const char* str, const char* cmp) { return *str == *cmp++ ? *str : strhit<_len + 1>(str, cmp); }
template<> inline
char strhit<1>(const char* str, const char* cmp) { return *str == *cmp ? *str : 0; }
template<> inline
char strhit<0>(const char* str, const char* cmp) { return *str; }
template<char _from, char _to>
char strhit(const char* str)
{
    return (*str >= chmin<_from, _to>::value &&
        *str <= chmax<_from, _to>::value) ?
        *str : 0;
}

/* used by the tool chain. */
inline int conv_quadstr(const char* str)
{
    int n = 0;
    if(str[0] == 0)
        return n;
    n |= str[0];
    if(str[1] == 0)
        return n;
    n |= ((uint)str[1]) << 8;
    if(str[2] == 0)
        return n;
    n |= ((uint)str[2]) << 16;
    if(str[3] == 0)
        return n;
    n |= ((uint)str[3]) << 24;
    return n;
}
inline char _1of_quadstr(int quadstr) { return (char)(quadstr & 0xff); }
inline char _2of_quadstr(int quadstr) { return (char)((quadstr & 0xff00) >> 8); }
inline char _3of_quadstr(int quadstr) { return (char)((quadstr & 0xff0000) >> 16); }
inline char _4of_quadstr(int quadstr) { return (char)((quadstr & 0xff000000) >> 24); }
inline int len_of_quadstr(int quadstr)
{
    if(!(quadstr & 0xff))
        return 0;
    if(!(quadstr & 0xff00))
        return 1;
    if(!(quadstr & 0xff0000))
        return 2;
    return (quadstr & 0xff000000) ? 4 : 3;
}
inline int utf8_unit_len(const char* str)
{
    if(str[0] == 0)
        return 0;
    if(!(str[0] & 0x80))
        return 1;
    if((str[0] & 0xe0) == 0xc0)
        return 2;
    if((str[0] & 0xf0) == 0xe0)
        return 3;
    if((str[0] & 0xf8) == 0xf0)
        return 4;
    if((str[0] & 0xfc) == 0xf8)
        return 5;
    if((str[0] & 0xfe) == 0xfc)
        return 6;
    return -1;
}
inline bool is_utf8_head(const char* str)
{
    assert(str);
    return (str[0] & 0xc0) != 0x80;
}
inline const char* to_utf8_head(const char* str)
{
    assert(str);
    for( ; str[0] && !is_utf8_head(str); str --);
    return str;
}
inline const char* next_utf8_unit(const char* str)
{
    assert(str);
    if(str[0] == 0)
        return str;
    for(str ++; !is_utf8_head(str); str ++);
    return str;
}
inline const char* prev_utf8_unit(const char* str)
{
    assert(str);
    return to_utf8_head(-- str);
}
inline bool utf8_unit_valid(const char* str)
{
    assert(str);
    return !is_utf8_head(str) ? false :
        next_utf8_unit(str) - str == utf8_unit_len(str);
}
inline int utf8_len(const char* str)
{
    assert(str);
    int len = 0;
    for( ; str[0]; str = next_utf8_unit(str), len ++);
    return len;
}

class string:
    public _string<gchar>
{
public:
    typedef std::basic_string<gchar> stdstr;
    typedef _string<gchar> superref;
    typedef superref::iterator iterator;
    typedef superref::const_iterator const_iterator;
    typedef superref::protoref protoref;

public:
    using superref::npos;
    using superref::begin;
    using superref::c_str;
    using superref::capacity;
    using superref::compare;
    using superref::copy;
    using superref::empty;
    using superref::find;
    using superref::find_first_not_of;
    using superref::find_first_of;
    using superref::find_last_not_of;
    using superref::find_last_of;
    using superref::max_size;
    using superref::operator+=;
    using superref::operator=;
    using superref::rbegin;
    using superref::reserve;
    using superref::rfind;
    using superref::substr;
    using superref::test;
    using superref::_test;
    using superref::to_lower;
    using superref::to_upper;
    using superref::to_int;
    using superref::to_int64;
    using superref::to_real;
    using superref::compare_cl;
    using superref::greater;
    using superref::less;
    using superref::equal;
    using superref::operator<;
    using superref::operator>;
    using superref::operator!=;
    using superref::operator==;
    using superref::front;
    using superref::back;
    using superref::assign;
    using superref::append;
    using superref::clear;
    using superref::erase;
    using superref::insert;
    using superref::push_back;
    using superref::replace;
    using superref::resize;
    using superref::swap;
    using superref::at;
    using superref::operator[];
    using superref::format;
    using superref::from_int;
    using superref::from_int64;
    using superref::from_real;
    using superref::length;
    using superref::pop_back;

public:
    string() {}
    string(const gchar* str): superref(str) {}
    string(const gchar* str, int len): superref(str, len) {}
    string(gchar ch, int cnt): superref(ch, cnt) {}
    string(protoref& rhs) { assign(rhs.c_str(), (int)rhs.size()); }
    int size() const { return (int)superref::size(); }
    bool operator<(const string& str) const { return less(str.c_str(), str.length()); }
    bool operator>(const string& str) const { return greater(str.c_str(), str.length()); }
    bool operator==(const string& str) const { return equal(str.c_str(), str.length()); }
    bool operator!=(const string& str) const { return !equal(str.c_str(), str.length()); }
};

inline bool is_mbcs_half(const char* str)
{
    if(!str || !(str[0] & 0x80))
        return false;
    int i = 1;
    for( ; str[i] && (str[i] & 0x80); i ++);
    return i % 2 != 0;
}

inline const gchar* get_next_char(const gchar* str, uint& c)
{
#ifdef _UNICODE
    if(!str || !str[0]) { c = 0; return 0; }
    c = (uint)str[0];
    return ++ str;
#else
    return get_mbcs_elem(str, c);
#endif
}

inline const gchar* get_next_char(const gchar* str, uint& c, const gchar* end)
{
#ifdef _UNICODE
    if(!str || str == end) { c = 0; return 0; }
    c = (uint)str[0];
    return ++ str;
#else
    return get_mbcs_elem(str, c, end);
#endif
}

__gslib_end__

#endif


#pragma once

#include "Library.h"
#include "Tile.h"
#include "TileContentLoadResult.h"
#include "TileContentLoader.h"
#include "TileRefine.h"

#include <gsl/span>
#include <spdlog/fwd.h>

#include <cstddef>
#include <memory>
#include <vector>

namespace Cesium3DTilesSelection {

class Tileset;

/**
 * @brief Creates a {@link TileContentLoadResult} from 3D Tiles external
 * `tileset.json` data.
 */
class CESIUM3DTILESSELECTION_API ExternalTilesetContent final
    : public TileContentLoader {
public:
  /**
   * @copydoc TileContentLoader::load
   *
   * The result will only contain the `childTiles` and the `pNewTileContext`.
   * Other fields will be empty or have default values.
   */
  CesiumAsync::Future<std::unique_ptr<TileContentLoadResult>>
  load(const TileContentLoadInput& input) override;

private:
  /**
   * @brief Create the {@link TileContentLoadResult} from the given input data.
   *
   * @param pLogger The logger that receives details of loading errors and
   * warnings.
   * @param tileRefine The {@link TileRefine}
   * @param url The source URL
   * @param data The raw input data
   * @return The {@link TileContentLoadResult}
   */
  static std::unique_ptr<TileContentLoadResult> load(
      const std::shared_ptr<spdlog::logger>& pLogger,
      const glm::dmat4& tileTransform,
      TileRefine tileRefine,
      const std::string& url,
      const gsl::span<const std::byte>& data);
};

} // namespace Cesium3DTilesSelection


#pragma once

#include <gsl\gsl>
#include <winrt\Windows.Foundation.h>
#include <d3d11.h>
#include <map>
#include "DrawableGameComponent.h"
#include "FullScreenRenderTarget.h"
#include "GaussianBlur.h"

namespace Rendering
{
	class DiffuseLightingDemo;

	class GaussianBlurDemo final : public Library::DrawableGameComponent
	{
	public:
		GaussianBlurDemo(Library::Game& game, const std::shared_ptr<Library::Camera>& camera);
		GaussianBlurDemo(const GaussianBlurDemo&) = delete;
		GaussianBlurDemo(GaussianBlurDemo&&) = default;
		GaussianBlurDemo& operator=(const GaussianBlurDemo&) = default;
		GaussianBlurDemo& operator=(GaussianBlurDemo&&) = default;
		~GaussianBlurDemo();

		std::shared_ptr<DiffuseLightingDemo> DiffuseLighting() const;

		float BlurAmount() const;
		void SetBlurAmount(float blurAmount);

		virtual void Initialize() override;
		virtual void Update(const Library::GameTime& gameTime) override;
		virtual void Draw(const Library::GameTime& gameTime) override;

	private:
		std::shared_ptr<DiffuseLightingDemo> mDiffuseLightingDemo;
		Library::FullScreenRenderTarget mRenderTarget;
		Library::GaussianBlur mGaussianBlur;
	};
}


/* vector/vector.c
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_vector.h>

/* turn off range checking at runtime if zero */
int gsl_check_range = 1;

#define BASE_GSL_COMPLEX_LONG
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_GSL_COMPLEX_LONG

#define BASE_GSL_COMPLEX
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_GSL_COMPLEX

#define BASE_GSL_COMPLEX_FLOAT
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_GSL_COMPLEX_FLOAT

#define BASE_LONG_DOUBLE
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_LONG_DOUBLE

#define BASE_DOUBLE
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_DOUBLE

#define BASE_FLOAT
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_FLOAT

#define BASE_ULONG
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_ULONG

#define BASE_LONG
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_LONG

#define BASE_UINT
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_UINT

#define BASE_INT
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_INT

#define BASE_USHORT
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_USHORT

#define BASE_SHORT
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_SHORT

#define BASE_UCHAR
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_UCHAR

#define BASE_CHAR
#include "templates_on.h"
#include "vector_source.c"
#include "templates_off.h"
#undef  BASE_CHAR


#ifndef __GSL_MATRIX_H__
#define __GSL_MATRIX_H__

#include <gsl/gsl_matrix_complex_long_double.h>
#include <gsl/gsl_matrix_complex_double.h>
#include <gsl/gsl_matrix_complex_float.h>

#include <gsl/gsl_matrix_long_double.h>
#include <gsl/gsl_matrix_double.h>
#include <gsl/gsl_matrix_float.h>

#include <gsl/gsl_matrix_ulong.h>
#include <gsl/gsl_matrix_long.h>

#include <gsl/gsl_matrix_uint.h>
#include <gsl/gsl_matrix_int.h>

#include <gsl/gsl_matrix_ushort.h>
#include <gsl/gsl_matrix_short.h>

#include <gsl/gsl_matrix_uchar.h>
#include <gsl/gsl_matrix_char.h>

#endif /* __GSL_MATRIX_H__ */


#pragma once
#include <gsl/span.h>
#include <msgpack.hpp>
#include <type_traits>
#include <typeindex>
#include <unordered_map>
#include <cstddef> // somehow needed for size_t?

namespace meta {
using packer = msgpack::packer<std::ostream>;
using unpacker = msgpack::unpacker;

struct Attribute {
  std::type_index typeindex;
  const void *data;
};

struct Field {
  const char *name;
  const char *qualName;
  std::type_index typeindex;
  std::size_t offset;
  void *(*getPtr)(void *object);
  gsl::span<const Attribute> attributes;

  template <typename T> T *getAs(void *object) const {
    return reinterpret_cast<T *>(getPtr(object));
  }

  template <typename T> const T *getAttribute() const {
	  for (auto&& a : attributes) {
		  if (a.typeindex == std::type_index{ typeid(T) }) {
			  return static_cast<const T*>(a.data);
		  }
	  }
	  return nullptr;
  }
};

struct Enumerator {
  const char *name;
  uint64_t value;
  gsl::span<const Attribute> attributes;

  template <typename T> const T *getAttribute() const {
	  for (auto&& a : attributes) {
		  if (a.typeindex == std::type_index{ typeid(T) }) {
			  return static_cast<const T*>(a.data);
		  }
	  }
	  return nullptr;
  }
};

enum class TypeKind { Enum, Struct, Union };

struct Enum;
struct Record;

struct Type {
  const char *name;
  TypeKind kind;
  gsl::span<const Attribute> attributes;
  void (*serialize)(packer &, const void *);
  void (*deserialize)(unpacker &, void *);

  template <typename T> const T *as() const {
    if (kind == T::t_kind)
      return static_cast<const T *>(this);
    return nullptr;
  }
};

struct Record : public Type {
  static constexpr TypeKind t_kind = TypeKind::Struct;
  Record(const char *name, gsl::span<const Field> public_fields_,
           gsl::span<const std::type_index> bases_,
           gsl::span<const Attribute> attributes_,
           void (*serialize_)(packer &, const void *),
           void (*deserialize_)(unpacker &, void *))
      : Type{name, t_kind, attributes_, serialize_, deserialize_},
        publicFields{public_fields_}, bases{bases_} {}

  gsl::span<const Field> publicFields;
  gsl::span<const std::type_index> bases;
};

struct Enum : public Type {
  static constexpr TypeKind t_kind = TypeKind::Enum;
  Enum(const char *name, gsl::span<const Enumerator> enumerators_,
         uint64_t (*getValue_)(const void *e),
         void (*setValue_)(void *, uint64_t),
         gsl::span<const Attribute> attributes_,
         void (*serialize_)(packer &, const void *),
         void (*deserialize_)(unpacker &, void *))
      : Type{name, t_kind, attributes_, serialize_, deserialize_},
        enumerators{enumerators_}, getValue{getValue_},
        setValue{setValue_} {}

  gsl::span<const Enumerator> enumerators;

  int findEnumeratorIndex(const void *obj) const {
    auto v = getValue(obj);
    for (int i = 0; i < enumerators.size(); ++i) {
      if (enumerators[i].value == v)
        return i;
    }
    return -1;
  }

  const Enumerator *findEnumerator(const void *obj) const {
    int i = findEnumeratorIndex(obj);
    if (i < 0)
      return nullptr;
    return &enumerators[i];
  }

  // manipulation functions
  uint64_t (*getValue)(const void *obj);
  void (*setValue)(void *obj, uint64_t value);
};

template <typename T> const Type *typeOf();
const Type *typeOf(std::type_index ti);

template <typename T> void serialize(packer& p, const T& data)
{
	serialize(p, typeOf<T>(), &data);
}

template <typename T> void serialize_dynamic(packer& p, const T& data)
{
	// always use the dynamic type
	serialize(p, typeOf(typeid(data)), &data);
}

inline void serialize(packer& p, const Type* ty, const void* data)
{
	if (!ty) return;
	ty->serialize(p, data);
}

// enum value-to-string and string-to-value
template <typename T, typename = std::enable_if_t<std::is_enum<T>::value>>
const char *getEnumeratorName(T constant);
template <typename T, typename = std::enable_if_t<std::is_enum<T>::value>>
T getEnumeratorValue(const char *name);
}