/* Copyright (c) 1997-2004
   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,v 1.14 2004/09/20 15:07:06 gawrilow Exp $"

#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>
class Keys
   : public modified_container_impl< Keys<_Map>,
               cons< Accessor< operations::member<typename _Map::const_reference,
						  typename deref<typename _Map::const_reference>::type::first_type> >,
		     Hidden< _Map > >
            >,
     public GenericSet< Keys<_Map>, typename _Map::key_type, typename _Map::key_comparator_type> {
   typedef modified_container_impl<Keys> _super;
public:
   typename _super::accessor get_accessor() const
   {
      typedef typename deref<typename _Map::const_reference>::type pair_type;
      return &pair_type::first;
   }
   const typename _Map::key_comparator_type& get_comparator() const
   {
      return this->hidden().get_key_comparator();
   }
};

template <typename _Map>
class Values
   : public modified_container_impl< Values<_Map>,
               cons< Accessor< operations::member<typename _Map::const_reference,
						  typename deref<typename _Map::const_reference>::type::second_type> >,
		     Hidden< _Map > >
            > {
   typedef modified_container_impl<Values> _super;
public:
   typename _super::accessor get_accessor() const {
      typedef typename deref<typename _Map::const_reference>::type pair_type;
      return &pair_type::second;
   }
};

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 typename attrib<MapRef>::plus_ref first_argument_type;
   typedef typename deref<KeyRef>::type second_argument_type;
   typedef typename inherit_ref<typename deref<MapRef>::type::data_type, first_argument_type>::type result_type;

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

template <typename _Map, typename _Keys,
	  bool _keys_match=isomorphic<typename _Map::key_type, _Keys>::answer>
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<unlimited_constant_container, _Map&>, const _Keys&,
				    BuildBinary<operations::associative> >
      type;
   typedef TransformedContainerPair<masquerade<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:
