#ifndef STRUCTURES_H
#define STRUCTURES_H

namespace std {
template <class T> constexpr auto begin(T& t) -> decltype(t.begin());
template <class T> constexpr auto begin(const T& t) -> decltype(t.begin());
template <class T> constexpr auto end(T& t) -> decltype(t.end());
template <class T> constexpr auto end(const T& t) -> decltype(t.end());
template <class T> constexpr auto size(const T& t) -> decltype(t.size());
} // namespace std

extern "C" {
extern int printf(const char *restrict, ...);
}

struct Val {int X; void g(); };

struct MutableVal {
  void constFun(int) const;
  void nonConstFun(int, int);
  void constFun(MutableVal &) const;
  void constParamFun(const MutableVal &) const;
  void nonConstParamFun(const MutableVal &);
  int X;
};

struct NonTriviallyCopyable {
  NonTriviallyCopyable() = default;
  // Define this constructor to make this class non-trivially copyable.
  NonTriviallyCopyable(const NonTriviallyCopyable& Ntc);
  int X;
};

struct TriviallyCopyableButBig {
  int X;
  char Array[16];
};

namespace ADT {

struct S {
  typedef MutableVal *iterator;
  typedef const MutableVal *const_iterator;
  const_iterator begin() const;
  const_iterator end() const;
  const_iterator cbegin() const;
  const_iterator cend() const;
  iterator begin();
  iterator end();
};

S::const_iterator begin(const S&);
S::const_iterator end(const S&);
S::const_iterator cbegin(const S&);
S::const_iterator cend(const S&);
S::iterator begin(S&);
S::iterator end(S&);
unsigned size(const S&);

struct T {
  typedef int value_type;
  struct iterator {
    value_type &operator*();
    const value_type &operator*() const;
    iterator& operator ++();
    bool operator!=(const iterator &other);
    void insert(value_type);
    value_type X;
  };
  iterator begin();
  iterator end();
};
T::iterator begin(T&);
T::iterator end(T&);

} // namespace ADT

using ADT::S;
using ADT::T;

struct Q {
  typedef int value_type;
  struct const_iterator {
    value_type &operator*();
    const value_type &operator*() const;
    const_iterator &operator++();
    bool operator!=(const const_iterator &other);
    void insert(value_type);
    value_type X;
  };
  struct iterator {
    operator const_iterator() const;
  };
  iterator begin();
  iterator end();
};

struct U {
  struct iterator {
    Val& operator*();
    const Val& operator*()const;
    iterator& operator ++();
    bool operator!=(const iterator &other);
    Val *operator->();
  };
  iterator begin();
  iterator end();
  int X;
};

struct X {
  S Ss;
  T Tt;
  U Uu;
  S getS();
};

namespace ADT {

template<typename ElemType>
class dependent {
 public:
  dependent<ElemType>();
  struct iterator_base {
    const ElemType& operator*()const;
    iterator_base& operator ++();
    bool operator!=(const iterator_base &other) const;
    const ElemType *operator->() const;
  };

  struct iterator : iterator_base {
    ElemType& operator*();
    iterator& operator ++();
    ElemType *operator->();
  };

  typedef iterator_base const_iterator;
  const_iterator begin() const;
  const_iterator end() const;
  iterator begin();
  iterator end();
  unsigned size() const;
  ElemType & operator[](unsigned);
  const ElemType & operator[](unsigned) const;
  ElemType & at(unsigned);
  ElemType & at(unsigned, unsigned);
  const ElemType & at(unsigned) const;

  // Intentionally evil.
  dependent<ElemType> operator*();

  void foo();
  void constFoo() const;
};

template<typename ElemType>
unsigned size(const dependent<ElemType>&);
template<typename ElemType>
unsigned length(const dependent<ElemType>&);

template<typename ElemType>
class dependent_derived : public dependent<ElemType> {
};

} // namespace ADT

using ADT::dependent;
using ADT::dependent_derived;

template<typename First, typename Second>
class doublyDependent{
 public:
  struct Value {
    First first;
    Second second;
  };

  struct iterator_base {
    const Value& operator*()const;
    iterator_base& operator ++();
    bool operator!=(const iterator_base &other) const;
    const Value *operator->() const;
  };

  struct iterator : iterator_base {
    Value& operator*();
    Value& operator ++();
    Value *operator->();
  };

  typedef iterator_base const_iterator;
  const_iterator begin() const;
  const_iterator end() const;
  iterator begin();
  iterator end();
};

template<typename Contained>
class transparent {
 public:
  Contained *at();
  Contained *operator->();
  Contained operator*();
};

template<typename IteratorType>
struct Nested {
  typedef IteratorType* iterator;
  typedef const IteratorType* const_iterator;
  IteratorType *operator->();
  IteratorType operator*();
  iterator begin();
  iterator end();
  const_iterator begin() const;
  const_iterator end() const;
};

// Like llvm::SmallPtrSet, the iterator has a dereference operator that returns
// by value instead of by reference.
template <typename T>
struct PtrSet {
  struct iterator {
    bool operator!=(const iterator &other) const;
    const T operator*();
    iterator &operator++();
  };
  iterator begin() const;
  iterator end() const;
};

template <typename T>
struct TypedefDerefContainer {
  struct iterator {
    typedef T &deref_type;
    bool operator!=(const iterator &other) const;
    deref_type operator*();
    iterator &operator++();
  };
  iterator begin() const;
  iterator end() const;
};

template <typename T>
struct RValueDerefContainer {
  struct iterator {
    typedef T &&deref_type;
    bool operator!=(const iterator &other) const;
    deref_type operator*();
    iterator &operator++();
  };
  iterator begin() const;
  iterator end() const;
};

namespace Macros {

struct MacroStruct {
  int Arr[10];
};
static MacroStruct *MacroSt;
#define CONT MacroSt->

} // namespace Macros

#endif  // STRUCTURES_H
