/* Copyright (c) 1997-2010
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.de

   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, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   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.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: Ring.h 9991 2010-12-12 23:36:28Z gawrilow $
*/

#ifndef POLYMAKE_RING_H
#define POLYMAKE_RING_H

#include "polymake/Array.h"
#include "polymake/Rational.h"
#include "polymake/hash_map"

namespace pm {

class Ring_base {
protected:
   typedef hash_map< Array<std::string>, int > repo_by_names_type;
   typedef hash_map< std::pair<std::string, int>, int* > repo_by_arity_type;

   static int* find_by_names(repo_by_names_type& repo_by_names, const Array<std::string>& names);
   static int* find_by_arity(repo_by_names_type& repo_by_names, repo_by_arity_type& repo_by_arity, int n, const std::string& name);

   static const Array<std::string>& get_names(int* id_ptr)
   {
      return reverse_cast(id_ptr, &repo_by_names_type::value_type::second)->first;
   }

   static int global_id;
};

// we need the Coefficient type for the Ring consistency

template <typename Coefficient=Rational, typename Exponent=int>
class Monomial;
template <typename Coefficient=Rational, typename Exponent=int>
class UniMonomial;

template <typename Coefficient=Rational, typename Exponent=int>
class Ring : public Ring_base {
protected:
   static repo_by_names_type& repo_by_names()
   {
      static repo_by_names_type repo;
      return repo;
   }

   static repo_by_arity_type& repo_by_arity()
   {
      static repo_by_arity_type repo;
      return repo;
   }

   int* id_ptr;

   class Monomial_constructor {
   public:
      typedef int argument_type;
      typedef Monomial<Coefficient, Exponent> result_type;
      result_type operator() (argument_type i) const;

      Monomial_constructor(const Ring& r) : ring(r) {}
   private:
      Ring ring;
   };

public:
   typedef Coefficient coefficient_type;
   typedef Exponent exponent_type;

   class Variables;

   // undefined object - to be read later from perl::Value
   Ring() : id_ptr(0) {}

   template <typename Container>
   Ring(const Container& names, typename enable_if<void*, isomorphic_to_container_of<Container, std::string>::value>::type =0)
      : id_ptr(find_by_names(repo_by_names(), names)) {}

   explicit Ring(int n, const std::string& name="x")
      : id_ptr(n>1 ? find_by_arity(repo_by_names(), repo_by_arity(), n, name)
               : find_by_names(repo_by_names(), Array<std::string>(1, name))) {}

   explicit Ring(const std::string& name)
      : id_ptr(find_by_names(repo_by_names(), Array<std::string>(1, name))) {}

   int n_vars() const { return get_names(id_ptr).size(); }

   const Array<std::string>& names() const { return get_names(id_ptr); }

   int id() const { return *id_ptr; }

   bool operator== (const Ring& other) const
   {
      // uninitialized rings are never equal to each other
      return id_ptr && id_ptr == other.id_ptr;
   }

   bool operator!= (const Ring& other) const
   {
      return !operator==(other);
   }

   Variables variables() const;

   UniMonomial<Coefficient, Exponent> variable() const;

   template <typename Input> friend
   Input& operator>> (GenericInput<Input>& in, Ring& me)
   {
      Array<std::string> names;
      in.top() >> names;
      me.id_ptr=find_by_names(repo_by_names(), names);
      return in.top();
   }
};

template <typename Coefficient, typename Exponent>
struct spec_object_traits< Ring<Coefficient,Exponent> >
   : spec_object_traits<is_opaque> {

   typedef const Array<std::string> serialized;

   static serialized& serialize(const Ring<Coefficient,Exponent>& r)
   {
      return r.names();
   }
};

} // end namespace pm

namespace polymake {
   using pm::Ring;
}

#endif // POLYMAKE_RING_H

// Local Variables:
// mode:C++
// c-basis-offset:3
// indent-tabs-mode:nil
// End:
