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

#ifndef POLYMAKE_TOPAZ_FINITE_FIELDS_H
#define POLYMAKE_TOPAZ_FINITE_FIELDS_H

#include "polymake/internal/converters_basic_defs.h"
#include <iostream>
#include <limits>

namespace polymake { namespace topaz {

#ifdef __INTEL_COMPILER
# define POLYMAKE_USE_ENUM 1
#elif defined(__GNUC__)
# define POLYMAKE_USE_ENUM __GNUC__>3 || (__GNUC_MINOR__ >=4 && __GNUC_PATCHLEVEL__ >= 4)
#else
# define POLYMAKE_USE_ENUM 0
#endif

#if POLYMAKE_USE_ENUM

enum GF2 { GF2_zero=0, GF2_one=1 };

inline GF2 operator+(GF2 x) { return x; }
inline GF2 operator-(GF2 x) { return x; }

inline GF2 operator+ (GF2 a, GF2 b) { return GF2(int(a)^int(b)); }
inline GF2 operator- (GF2 a, GF2 b) { return GF2(int(a)^int(b)); }
inline GF2 operator* (GF2 a, GF2 b) { return GF2(int(a)&int(b)); }
inline GF2 operator/ (GF2 a, GF2 b) { return GF2(int(a)/int(b)); }

inline GF2& operator+= (GF2& a, GF2 b) { reinterpret_cast<int&>(a)^=int(b); return a; }
inline GF2& operator-= (GF2& a, GF2 b) { reinterpret_cast<int&>(a)^=int(b); return a; }
inline GF2& operator*= (GF2& a, GF2 b) { reinterpret_cast<int&>(a)&=int(b); return a; }
inline GF2& operator/= (GF2& a, GF2 b) { reinterpret_cast<int&>(a)/=int(b); return a; }

template <typename Traits> inline
std::basic_istream<char, Traits>&
operator>> (std::basic_istream<char, Traits>& is, GF2& a)
{
   return is >> reinterpret_cast<int&>(a);
}

template <typename Traits> inline
std::basic_ostream<char, Traits>&
operator<< (std::basic_ostream<char, Traits>& os, GF2 a)
{
   return os << int(a);
}

template <typename T, bool _is_integer=std::numeric_limits<T>::is_integer>
struct conv_to_GF2;

template <typename T>
struct conv_to_GF2<T, true> {
   typedef T argument_type;
   typedef polymake::topaz::GF2 result_type;
   result_type operator() (typename pm::function_argument<T>::type x) const { return result_type(x%2); }
};

#else // !POLYMAKE_USE_ENUM

enum { GF2_zero=0, GF2_one=1 };

class GF2 {
   int value;

   GF2(int v, bool) : value(v) { }
public:
   GF2() : value(0) { }
   GF2(int v) : value(v%2) { }

   const GF2& operator+ () const { return *this; }
   const GF2& operator- () const { return *this; }

   friend GF2 operator+ (const GF2& a, const GF2& b) { return GF2(a.value^b.value, true); }
   friend GF2 operator- (const GF2& a, const GF2& b) { return GF2(a.value^b.value, true); }
   friend GF2 operator* (const GF2& a, const GF2& b) { return GF2(a.value&b.value, true); }
   friend GF2 operator/ (const GF2& a, const GF2& b) { return GF2(a.value/b.value, true); }

   GF2& operator+= (GF2 b) { value^=b.value; return *this; }
   GF2& operator-= (GF2 b) { value^=b.value; return *this; }
   GF2& operator*= (GF2 b) { value&=b.value; return *this; }
   GF2& operator/= (GF2 b) { value/=b.value; return *this; }

   template <typename Traits> friend
   std::basic_istream<char, Traits>& operator>> (std::basic_istream<char, Traits>& is, GF2& a)
   {
      is >> a.value;
      a.value %= 2;
      return is;
   }

   template <typename Traits> friend
   std::basic_istream<char, Traits>& operator<< (std::basic_istream<char, Traits>& os, const GF2& a)
   {
      return os << a.value;
   }

   operator bool() const { return value; }
};

#endif // !POLYMAKE_USE_ENUM

} } // end namespace polymake::topaz

namespace std {

template <>
struct numeric_limits<polymake::topaz::GF2> : numeric_limits<unsigned int> {
   static polymake::topaz::GF2 min() throw() { return polymake::topaz::GF2_zero; }
   static polymake::topaz::GF2 max() throw() { return polymake::topaz::GF2_one; }
   static const int digits=1;
   static const int digits10=1;
};

}

#if POLYMAKE_USE_ENUM
namespace pm {

template <typename T>
struct conv<T, polymake::topaz::GF2> : polymake::topaz::conv_to_GF2<T> { };

}
#endif // POLYMAKE_USE_ENUM
#undef POLYMAKE_USE_ENUM

#endif // POLYMAKE_TOPAZ_FINITE_FIELDS_H

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