/*
 * Copyright (c) 1999 Johnny C. Lam.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  The author makes no representations
 * about the suitability of this software for any purpose.  It is
 * provided "as is" without express or implied warranty.
 */

#ifndef STL_ALGORITHM_INCLUDED
#include <algorithm>
#define STL_ALGORITHM_INCLUDED
#endif

#ifndef STL_UTILITY_INCLUDED
#include <utility>
#define STL_UTILITY_INCLUDED
#endif

inline
Haplotype::Haplotype(size_type sz)
    : _value(new HaplotypeValue(sz))
{}

template <class InputIterator>
inline
Haplotype::Haplotype(InputIterator first, InputIterator last)
    : _value(new HaplotypeValue(distance(first, last)))
{
    copy(first, last, _value->loci.begin());
}

inline
Haplotype::const_iterator
Haplotype::begin() const
{
    return _value->loci.begin();
}

inline
Haplotype::const_iterator
Haplotype::end() const
{
    return _value->loci.end();
}

inline
Haplotype::size_type
Haplotype::size() const
{
    return _value->loci.size();
}

inline
const Marker
Haplotype::operator[](size_type k) const
{
    return _value->loci[k];
}

inline
Haplotype::const_iterator
Haplotype::const_iterator_at(size_type k) const
{
    return begin() + k;
}

inline
Haplotype::property_t
Haplotype::Properties() const
{
    return HaplotypeProperties::Instance()->Properties(*this);
}

inline
void
Haplotype::SetProperties(property_t properties) const
{
    HaplotypeProperties::Instance()->SetProperties(*this, properties);
}

inline
bool
Haplotype::IsLatent() const
{
    property_t properties = Properties();
    return (properties == NONE) || (properties & LATENT);
}

inline
Haplotype
Haplotype::Substr(const_iterator start, const_iterator end) const
{
    return Haplotype(start, end);
}

inline
void
Haplotype::Mutate(size_type k, Marker m)
{
    if (_value->is_shared())
	_value = new HaplotypeValue(*_value);
    _value->loci[k] = m;
}

inline
Haplotype::HaplotypeValue::HaplotypeValue(size_type sz)
    : loci(sz)
{}

inline
Haplotype::HaplotypeValue::HaplotypeValue(const container_type &l)
    : loci(l)
{}

inline
bool
operator<(const Haplotype& lhs, const Haplotype& rhs)
{
    return lexicographical_compare(lhs.begin(), lhs.end(),
				   rhs.begin(), rhs.end());
}

inline
istream&
operator>>(istream& istr, Haplotype& h)
{
    h.Read(istr);
    return istr;
}

inline
void
HaplotypeProperties::Observed(const Haplotype& h, property_t properties)
{
    SetProperties(h, properties);
}

inline
void
HaplotypeProperties::SetProperties(const Haplotype& h, property_t properties)
{
    typename PropertyMap::iterator i = _properties.find(h);
    if (i == _properties.end()) {
	if (properties != Haplotype::NONE)
	    _properties.insert(i, std::make_pair(h, properties));
    } else {
	if (properties == Haplotype::NONE) {
	    _properties.erase(i);
	} else {
	    i->second = properties;
	}
    }
}

inline
HaplotypeProperties::property_t
HaplotypeProperties::Properties(const Haplotype& h) const
{
    typename PropertyMap::const_iterator i = _properties.find(h);
    if (i == _properties.end()) {
	return Haplotype::NONE;
    } else {
	return i->second;
    }
}
