/* Copyright (c) 1997-2006
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.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.
*/

#ifndef _POLYMAKE_ASSOC_H
#define _POLYMAKE_ASSOC_H "$Project: polymake $$Id: assoc.h 7402 2006-07-27 23:44:14Z gawrilow $"

#include <stdexcept>

#ifndef _POLYMAKE_TRANSFORMED_CONTAINER_H
#  include <TransformedContainer.h>
#endif
#ifndef _POLYMAKE_GENERIC_SET_H
#  include <GenericSet.h>
#endif

namespace pm {

/** Exception type
    If an access operation on a read-only associative container, which should return a reference,
    fails due to a non-existent search key, an exception of this type is raised.
*/
class no_match : public std::runtime_error {
public:
   no_match() : std::runtime_error("key not found") { }
   no_match(const std::string& reason) : std::runtime_error(reason) { }
};

template <typename Map>
struct map_masquerade_helper {
   typedef pair<const typename Map::key_type, typename Map::data_type> value_type;
   typedef operations::member<value_type, const typename Map::key_type, &value_type::first> key_accessor;
   typedef operations::member<value_type, typename Map::data_type, &value_type::second> value_accessor;
};

template <typename Map>
class Keys
   : public modified_container_impl< Keys<Map>,
				     list( Operation< typename map_masquerade_helper<Map>::key_accessor >,
					   Hidden< Map > ) >,
     public GenericSet< Keys<Map>, typename Map::key_type, typename Map::key_comparator_type> {
   typedef modified_container_impl<Keys> _super;
public:
   const typename Map::key_comparator_type& get_comparator_object() const
   {
      return this->hidden().get_comparator_object();
   }
   bool contains(typename function_argument<typename Map::key_type>::type x) const
   {
      return this->hidden().exists(x);
   }
};

template <typename Map>
class Values
   : public modified_container_impl< Values<Map>,
				     list( Operation< typename map_masquerade_helper<Map>::value_accessor >,
					   Hidden< Map > ) > { };

template <typename Map> inline
const Keys<Map>& keys(const Map& map)
{
   return reinterpret_cast<const Keys<Map>&>(map);
}

template <typename Map> inline
const Values<Map>& values(const Map& map)
{
   return reinterpret_cast<const Values<Map>&>(map);
}

namespace operations {

template <typename MapRef, typename KeyRef>
struct associative {
   typedef MapRef first_argument_type;
   typedef typename deref<KeyRef>::type second_argument_type;
   typedef typename inherit_ref<typename deref<MapRef>::type::data_type, MapRef>::type result_type;

   result_type operator() (typename function_argument<MapRef>::type map, typename function_argument<KeyRef>::const_type k) const
   {
      return map[k];
   }
};
} // end namespace associative

template <typename _Map, typename _Keys,
	  bool _keys_match=isomorphic_types<typename _Map::key_type, _Keys>::value>
struct assoc_helper {
   typedef typename _Map::data_type& type;
   typedef const typename _Map::data_type& const_type;

   static type _do(_Map& map, const _Keys& k)
   {
      return map.insert(k)->second;
   }

   static const_type _do(const _Map& map, const _Keys& k)
   {
      typename _Map::const_iterator e=map.find(k);
      if (e.at_end()) throw no_match();
      return e->second;
   }
};

template <typename _Map, typename _Keys>
struct assoc_helper<_Map, _Keys, false> {
   typedef TransformedContainerPair<masquerade_keep_ref<unlimited_constant_container, _Map&>, const _Keys&,
				    BuildBinary<operations::associative> >
      type;
   typedef TransformedContainerPair<masquerade_keep_ref<unlimited_constant_container, const _Map&>, const _Keys&,
				    BuildBinary<operations::associative> >
      const_type;

   static type _do(_Map& map, const _Keys& keys)
   {
      return type(map,keys);
   }

   static const_type _do(const _Map& map, const _Keys& keys)
   {
      return const_type(map,keys);
   }
};

template <typename _Map, typename _Keys> inline
typename assoc_helper<_Map,_Keys>::type
select_by_keys(_Map& map, const _Keys& keys)
{
   return assoc_helper<_Map,_Keys>::_do(map,keys);
}

template <typename _Map, typename _Keys> inline
typename assoc_helper<_Map,_Keys>::const_type
select_by_keys(const _Map& map, const _Keys& keys)
{
   return assoc_helper<_Map,_Keys>::_do(map,keys);
}

} // end namespace pm

namespace polymake {
   using pm::Keys;
   using pm::Values;
   using pm::select_by_keys;
}

#endif // _POLYMAKE_ASSOC_H

// Local Variables:
// mode:C++
// End:
