//! A map type
#[forbid(deprecated_mode)];

use io::WriterUtil;
use to_str::ToStr;
use mutable::Mut;
use send_map::linear::LinearMap;

use core::cmp::Eq;
use hash::Hash;
use to_bytes::IterBytes;

/// A convenience type to treat a hashmap as a set
pub type Set<K:Eq IterBytes Hash> = HashMap<K, ()>;

pub type HashMap<K:Eq IterBytes Hash, V> = chained::T<K, V>;

pub trait Map<K:Eq IterBytes Hash Copy, V: Copy> {
    /// Return the number of elements in the map
    pure fn size() -> uint;

    /**
     * Add a value to the map.
     *
     * If the map already contains a value for the specified key then the
     * original value is replaced.
     *
     * Returns true if the key did not already exist in the map
     */
    fn insert(v: K, v: V) -> bool;

    /// Returns true if the map contains a value for the specified key
    fn contains_key(key: K) -> bool;

    /// Returns true if the map contains a value for the specified
    /// key, taking the key by reference.
    fn contains_key_ref(key: &K) -> bool;

    /**
     * Get the value for the specified key. Fails if the key does not exist in
     * the map.
     */
    fn get(key: K) -> V;

    /**
     * Get the value for the specified key. If the key does not exist in
     * the map then returns none.
     */
    pure fn find(key: K) -> Option<V>;

    /**
     * Remove and return a value from the map. Returns true if the
     * key was present in the map, otherwise false.
     */
    fn remove(key: K) -> bool;

    /// Clear the map, removing all key/value pairs.
    fn clear();

    /// Iterate over all the key/value pairs in the map by value
    pure fn each(fn(key: K, value: V) -> bool);

    /// Iterate over all the keys in the map by value
    pure fn each_key(fn(key: K) -> bool);

    /// Iterate over all the values in the map by value
    pure fn each_value(fn(value: V) -> bool);

    /// Iterate over all the key/value pairs in the map by reference
    pure fn each_ref(fn(key: &K, value: &V) -> bool);

    /// Iterate over all the keys in the map by reference
    pure fn each_key_ref(fn(key: &K) -> bool);

    /// Iterate over all the values in the map by reference
    pure fn each_value_ref(fn(value: &V) -> bool);
}

mod util {
    pub type Rational = {num: int, den: int}; // : int::positive(*.den);

    pub pure fn rational_leq(x: Rational, y: Rational) -> bool {
        // NB: Uses the fact that rationals have positive denominators WLOG:

        x.num * y.den <= y.num * x.den
    }
}


// FIXME (#2344): package this up and export it as a datatype usable for
// external code that doesn't want to pay the cost of a box.
pub mod chained {

    const initial_capacity: uint = 32u; // 2^5

    struct Entry<K, V> {
        hash: uint,
        key: K,
        value: V,
        mut next: Option<@Entry<K, V>>
    }

    struct HashMap_<K:Eq IterBytes Hash, V> {
        mut count: uint,
        mut chains: ~[mut Option<@Entry<K,V>>]
    }

    pub type T<K:Eq IterBytes Hash, V> = @HashMap_<K, V>;

    enum SearchResult<K, V> {
        NotFound,
        FoundFirst(uint, @Entry<K,V>),
        FoundAfter(@Entry<K,V>, @Entry<K,V>)
    }

    priv impl<K:Eq IterBytes Hash, V: Copy> T<K, V> {
        pure fn search_rem(k: &K, h: uint, idx: uint,
                           e_root: @Entry<K,V>) -> SearchResult<K,V> {
            let mut e0 = e_root;
            let mut comp = 1u;   // for logging
            loop {
                match copy e0.next {
                  None => {
                    debug!("search_tbl: absent, comp %u, hash %u, idx %u",
                           comp, h, idx);
                    return NotFound;
                  }
                  Some(e1) => {
                    comp += 1u;
                    unsafe {
                        if e1.hash == h && e1.key == *k {
                            debug!("search_tbl: present, comp %u, \
                                    hash %u, idx %u",
                                   comp, h, idx);
                            return FoundAfter(e0, e1);
                        } else {
                            e0 = e1;
                        }
                    }
                  }
                }
            };
        }

        pure fn search_tbl(k: &K, h: uint) -> SearchResult<K,V> {
            let idx = h % vec::len(self.chains);
            match copy self.chains[idx] {
              None => {
                debug!("search_tbl: none, comp %u, hash %u, idx %u",
                       0u, h, idx);
                return NotFound;
              }
              Some(e) => {
                unsafe {
                    if e.hash == h && e.key == *k {
                        debug!("search_tbl: present, comp %u, hash %u, \
                                idx %u", 1u, h, idx);
                        return FoundFirst(idx, e);
                    } else {
                        return self.search_rem(k, h, idx, e);
                    }
                }
              }
            }
        }

        fn rehash() {
            let n_old_chains = self.chains.len();
            let n_new_chains: uint = uint::next_power_of_two(n_old_chains+1u);
            let new_chains = chains(n_new_chains);
            for self.each_entry |entry| {
                let idx = entry.hash % n_new_chains;
                entry.next = new_chains[idx];
                new_chains[idx] = Some(entry);
            }
            self.chains <- new_chains;
        }

        pure fn each_entry(blk: fn(@Entry<K,V>) -> bool) {
            // n.b. we can't use vec::iter() here because self.chains
            // is stored in a mutable location.
            let mut i = 0u, n = self.chains.len();
            while i < n {
                let mut chain = self.chains[i];
                loop {
                    chain = match chain {
                      None => break,
                      Some(entry) => {
                        let next = entry.next;
                        if !blk(entry) { return; }
                        next
                      }
                    }
                }
                i += 1u;
            }
        }
    }

    impl<K:Eq IterBytes Hash Copy, V: Copy> T<K, V>: Map<K, V> {
        pure fn size() -> uint { self.count }

        fn contains_key(k: K) -> bool {
            self.contains_key_ref(&k)
        }

        fn contains_key_ref(k: &K) -> bool {
            let hash = k.hash_keyed(0,0) as uint;
            match self.search_tbl(k, hash) {
              NotFound => false,
              FoundFirst(*) | FoundAfter(*) => true
            }
        }

        fn insert(k: K, v: V) -> bool {
            let hash = k.hash_keyed(0,0) as uint;
            match self.search_tbl(&k, hash) {
              NotFound => {
                self.count += 1u;
                let idx = hash % vec::len(self.chains);
                let old_chain = self.chains[idx];
                self.chains[idx] = Some(@Entry {
                    hash: hash,
                    key: k,
                    value: v,
                    next: old_chain});

                // consider rehashing if more 3/4 full
                let nchains = vec::len(self.chains);
                let load = {num: (self.count + 1u) as int,
                            den: nchains as int};
                if !util::rational_leq(load, {num:3, den:4}) {
                    self.rehash();
                }

                return true;
              }
              FoundFirst(idx, entry) => {
                self.chains[idx] = Some(@Entry {
                    hash: hash,
                    key: k,
                    value: v,
                    next: entry.next});
                return false;
              }
              FoundAfter(prev, entry) => {
                prev.next = Some(@Entry {
                    hash: hash,
                    key: k,
                    value: v,
                    next: entry.next});
                return false;
              }
            }
        }

        pure fn find(k: K) -> Option<V> {
            unsafe {
                match self.search_tbl(&k, k.hash_keyed(0,0) as uint) {
                  NotFound => None,
                  FoundFirst(_, entry) => Some(entry.value),
                  FoundAfter(_, entry) => Some(entry.value)
                }
            }
        }

        fn get(k: K) -> V {
            let opt_v = self.find(k);
            if opt_v.is_none() {
                fail fmt!("Key not found in table: %?", k);
            }
            option::unwrap(move opt_v)
        }

        fn remove(k: K) -> bool {
            match self.search_tbl(&k, k.hash_keyed(0,0) as uint) {
              NotFound => false,
              FoundFirst(idx, entry) => {
                self.count -= 1u;
                self.chains[idx] = entry.next;
                true
              }
              FoundAfter(eprev, entry) => {
                self.count -= 1u;
                eprev.next = entry.next;
                true
              }
            }
        }

        fn clear() {
            self.count = 0u;
            self.chains = chains(initial_capacity);
        }

        pure fn each(blk: fn(key: K, value: V) -> bool) {
            self.each_ref(|k, v| blk(*k, *v))
        }

        pure fn each_key(blk: fn(key: K) -> bool) {
            self.each_key_ref(|p| blk(*p))
        }

        pure fn each_value(blk: fn(value: V) -> bool) {
            self.each_value_ref(|p| blk(*p))
        }

        pure fn each_ref(blk: fn(key: &K, value: &V) -> bool) {
            for self.each_entry |entry| {
                if !blk(&entry.key, &entry.value) { break; }
            }
        }

        pure fn each_key_ref(blk: fn(key: &K) -> bool) {
            self.each_ref(|k, _v| blk(k))
        }

        pure fn each_value_ref(blk: fn(value: &V) -> bool) {
            self.each_ref(|_k, v| blk(v))
        }
    }

    impl<K:Eq IterBytes Hash Copy ToStr, V: ToStr Copy> T<K, V>: ToStr {
        fn to_writer(wr: io::Writer) {
            if self.count == 0u {
                wr.write_str(~"{}");
                return;
            }

            wr.write_str(~"{ ");
            let mut first = true;
            for self.each_entry |entry| {
                if !first {
                    wr.write_str(~", ");
                }
                first = false;
                wr.write_str(entry.key.to_str());
                wr.write_str(~": ");
                wr.write_str((copy entry.value).to_str());
            };
            wr.write_str(~" }");
        }

        pure fn to_str() -> ~str unsafe {
            // Meh -- this should be safe
            do io::with_str_writer |wr| { self.to_writer(wr) }
        }
    }

    impl<K:Eq IterBytes Hash Copy, V: Copy> T<K, V>: ops::Index<K, V> {
        pure fn index(k: K) -> V {
            unsafe {
                self.get(k)
            }
        }
    }

    fn chains<K,V>(nchains: uint) -> ~[mut Option<@Entry<K,V>>] {
        vec::to_mut(vec::from_elem(nchains, None))
    }

    pub fn mk<K:Eq IterBytes Hash, V: Copy>() -> T<K,V> {
        let slf: T<K, V> = @HashMap_ {count: 0u,
                                      chains: chains(initial_capacity)};
        slf
    }
}

/*
Function: hashmap

Construct a hashmap.
*/
pub fn HashMap<K:Eq IterBytes Hash Const, V: Copy>()
        -> HashMap<K, V> {
    chained::mk()
}

/// Convenience function for adding keys to a hashmap with nil type keys
pub fn set_add<K:Eq IterBytes Hash Const Copy>(set: Set<K>, key: K) -> bool {
    set.insert(key, ())
}

/// Convert a set into a vector.
pub pure fn vec_from_set<T:Eq IterBytes Hash Copy>(s: Set<T>) -> ~[T] {
    do vec::build_sized(s.size()) |push| {
        for s.each_key() |k| {
            push(k);
        }
    }
}

/// Construct a hashmap from a vector
pub fn hash_from_vec<K: Eq IterBytes Hash Const Copy, V: Copy>(
    items: &[(K, V)]) -> HashMap<K, V> {
    let map = HashMap();
    for vec::each(items) |item| {
        match *item {
            (copy key, copy value) => {
                map.insert(key, value);
            }
        }
    }
    map
}

// XXX Transitional
impl<K: Eq IterBytes Hash Copy, V: Copy> @Mut<LinearMap<K, V>>:
    Map<K, V> {
    pure fn size() -> uint {
        unsafe {
            do self.borrow_const |p| {
                p.len()
            }
        }
    }

    fn insert(key: K, value: V) -> bool {
        do self.borrow_mut |p| {
            p.insert(key, value)
        }
    }

    fn contains_key(key: K) -> bool {
        do self.borrow_const |p| {
            p.contains_key(&key)
        }
    }

    fn contains_key_ref(key: &K) -> bool {
        do self.borrow_const |p| {
            p.contains_key(key)
        }
    }

    fn get(key: K) -> V {
        do self.borrow_const |p| {
            p.get(&key)
        }
    }

    pure fn find(key: K) -> Option<V> {
        unsafe {
            do self.borrow_const |p| {
                p.find(&key)
            }
        }
    }

    fn remove(key: K) -> bool {
        do self.borrow_mut |p| {
            p.remove(&key)
        }
    }

    fn clear() {
        do self.borrow_mut |p| {
            p.clear()
        }
    }

    pure fn each(op: fn(key: K, value: V) -> bool) {
        unsafe {
            do self.borrow_imm |p| {
                p.each(|k, v| op(*k, *v))
            }
        }
    }

    pure fn each_key(op: fn(key: K) -> bool) {
        unsafe {
            do self.borrow_imm |p| {
                p.each_key(|k| op(*k))
            }
        }
    }

    pure fn each_value(op: fn(value: V) -> bool) {
        unsafe {
            do self.borrow_imm |p| {
                p.each_value(|v| op(*v))
            }
        }
    }

    pure fn each_ref(op: fn(key: &K, value: &V) -> bool) {
        unsafe {
            do self.borrow_imm |p| {
                p.each(op)
            }
        }
    }

    pure fn each_key_ref(op: fn(key: &K) -> bool) {
        unsafe {
            do self.borrow_imm |p| {
                p.each_key(op)
            }
        }
    }

    pure fn each_value_ref(op: fn(value: &V) -> bool) {
        unsafe {
            do self.borrow_imm |p| {
                p.each_value(op)
            }
        }
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_simple() {
        debug!("*** starting test_simple");
        pure fn eq_uint(x: &uint, y: &uint) -> bool { *x == *y }
        pure fn uint_id(x: &uint) -> uint { *x }
        debug!("uint -> uint");
        let hm_uu: map::HashMap<uint, uint> =
            map::HashMap::<uint, uint>();
        assert (hm_uu.insert(10u, 12u));
        assert (hm_uu.insert(11u, 13u));
        assert (hm_uu.insert(12u, 14u));
        assert (hm_uu.get(11u) == 13u);
        assert (hm_uu.get(12u) == 14u);
        assert (hm_uu.get(10u) == 12u);
        assert (!hm_uu.insert(12u, 14u));
        assert (hm_uu.get(12u) == 14u);
        assert (!hm_uu.insert(12u, 12u));
        assert (hm_uu.get(12u) == 12u);
        let ten: ~str = ~"ten";
        let eleven: ~str = ~"eleven";
        let twelve: ~str = ~"twelve";
        debug!("str -> uint");
        let hm_su: map::HashMap<~str, uint> =
            map::HashMap::<~str, uint>();
        assert (hm_su.insert(~"ten", 12u));
        assert (hm_su.insert(eleven, 13u));
        assert (hm_su.insert(~"twelve", 14u));
        assert (hm_su.get(eleven) == 13u);
        assert (hm_su.get(~"eleven") == 13u);
        assert (hm_su.get(~"twelve") == 14u);
        assert (hm_su.get(~"ten") == 12u);
        assert (!hm_su.insert(~"twelve", 14u));
        assert (hm_su.get(~"twelve") == 14u);
        assert (!hm_su.insert(~"twelve", 12u));
        assert (hm_su.get(~"twelve") == 12u);
        debug!("uint -> str");
        let hm_us: map::HashMap<uint, ~str> =
            map::HashMap::<uint, ~str>();
        assert (hm_us.insert(10u, ~"twelve"));
        assert (hm_us.insert(11u, ~"thirteen"));
        assert (hm_us.insert(12u, ~"fourteen"));
        assert hm_us.get(11u) == ~"thirteen";
        assert hm_us.get(12u) == ~"fourteen";
        assert hm_us.get(10u) == ~"twelve";
        assert (!hm_us.insert(12u, ~"fourteen"));
        assert hm_us.get(12u) == ~"fourteen";
        assert (!hm_us.insert(12u, ~"twelve"));
        assert hm_us.get(12u) == ~"twelve";
        debug!("str -> str");
        let hm_ss: map::HashMap<~str, ~str> =
            map::HashMap::<~str, ~str>();
        assert (hm_ss.insert(ten, ~"twelve"));
        assert (hm_ss.insert(eleven, ~"thirteen"));
        assert (hm_ss.insert(twelve, ~"fourteen"));
        assert hm_ss.get(~"eleven") == ~"thirteen";
        assert hm_ss.get(~"twelve") == ~"fourteen";
        assert hm_ss.get(~"ten") == ~"twelve";
        assert (!hm_ss.insert(~"twelve", ~"fourteen"));
        assert hm_ss.get(~"twelve") == ~"fourteen";
        assert (!hm_ss.insert(~"twelve", ~"twelve"));
        assert hm_ss.get(~"twelve") == ~"twelve";
        debug!("*** finished test_simple");
    }


    /**
    * Force map growth
    */
    #[test]
    fn test_growth() {
        debug!("*** starting test_growth");
        let num_to_insert: uint = 64u;
        pure fn eq_uint(x: &uint, y: &uint) -> bool { *x == *y }
        pure fn uint_id(x: &uint) -> uint { *x }
        debug!("uint -> uint");
        let hm_uu: map::HashMap<uint, uint> =
            map::HashMap::<uint, uint>();
        let mut i: uint = 0u;
        while i < num_to_insert {
            assert (hm_uu.insert(i, i * i));
            debug!("inserting %u -> %u", i, i*i);
            i += 1u;
        }
        debug!("-----");
        i = 0u;
        while i < num_to_insert {
            debug!("get(%u) = %u", i, hm_uu.get(i));
            assert (hm_uu.get(i) == i * i);
            i += 1u;
        }
        assert (hm_uu.insert(num_to_insert, 17u));
        assert (hm_uu.get(num_to_insert) == 17u);
        debug!("-----");
        i = 0u;
        while i < num_to_insert {
            debug!("get(%u) = %u", i, hm_uu.get(i));
            assert (hm_uu.get(i) == i * i);
            i += 1u;
        }
        debug!("str -> str");
        let hm_ss: map::HashMap<~str, ~str> =
            map::HashMap::<~str, ~str>();
        i = 0u;
        while i < num_to_insert {
            assert hm_ss.insert(uint::to_str(i, 2u), uint::to_str(i * i, 2u));
            debug!("inserting \"%s\" -> \"%s\"",
                   uint::to_str(i, 2u),
                   uint::to_str(i*i, 2u));
            i += 1u;
        }
        debug!("-----");
        i = 0u;
        while i < num_to_insert {
            debug!("get(\"%s\") = \"%s\"",
                   uint::to_str(i, 2u),
                   hm_ss.get(uint::to_str(i, 2u)));
            assert hm_ss.get(uint::to_str(i, 2u)) == uint::to_str(i * i, 2u);
            i += 1u;
        }
        assert (hm_ss.insert(uint::to_str(num_to_insert, 2u),
                             uint::to_str(17u, 2u)));
        assert hm_ss.get(uint::to_str(num_to_insert, 2u)) ==
            uint::to_str(17u, 2u);
        debug!("-----");
        i = 0u;
        while i < num_to_insert {
            debug!("get(\"%s\") = \"%s\"",
                   uint::to_str(i, 2u),
                   hm_ss.get(uint::to_str(i, 2u)));
            assert hm_ss.get(uint::to_str(i, 2u)) == uint::to_str(i * i, 2u);
            i += 1u;
        }
        debug!("*** finished test_growth");
    }

    #[test]
    fn test_removal() {
        debug!("*** starting test_removal");
        let num_to_insert: uint = 64u;
        let hm: map::HashMap<uint, uint> =
            map::HashMap::<uint, uint>();
        let mut i: uint = 0u;
        while i < num_to_insert {
            assert (hm.insert(i, i * i));
            debug!("inserting %u -> %u", i, i*i);
            i += 1u;
        }
        assert (hm.size() == num_to_insert);
        debug!("-----");
        debug!("removing evens");
        i = 0u;
        while i < num_to_insert {
            let v = hm.remove(i);
            assert v;
            i += 2u;
        }
        assert (hm.size() == num_to_insert / 2u);
        debug!("-----");
        i = 1u;
        while i < num_to_insert {
            debug!("get(%u) = %u", i, hm.get(i));
            assert (hm.get(i) == i * i);
            i += 2u;
        }
        debug!("-----");
        i = 1u;
        while i < num_to_insert {
            debug!("get(%u) = %u", i, hm.get(i));
            assert (hm.get(i) == i * i);
            i += 2u;
        }
        debug!("-----");
        i = 0u;
        while i < num_to_insert {
            assert (hm.insert(i, i * i));
            debug!("inserting %u -> %u", i, i*i);
            i += 2u;
        }
        assert (hm.size() == num_to_insert);
        debug!("-----");
        i = 0u;
        while i < num_to_insert {
            debug!("get(%u) = %u", i, hm.get(i));
            assert (hm.get(i) == i * i);
            i += 1u;
        }
        debug!("-----");
        assert (hm.size() == num_to_insert);
        i = 0u;
        while i < num_to_insert {
            debug!("get(%u) = %u", i, hm.get(i));
            assert (hm.get(i) == i * i);
            i += 1u;
        }
        debug!("*** finished test_removal");
    }

    #[test]
    fn test_contains_key() {
        let key = ~"k";
        let map = map::HashMap::<~str, ~str>();
        assert (!map.contains_key(key));
        map.insert(key, ~"val");
        assert (map.contains_key(key));
    }

    #[test]
    fn test_find() {
        let key = ~"k";
        let map = map::HashMap::<~str, ~str>();
        assert (option::is_none(&map.find(key)));
        map.insert(key, ~"val");
        assert (option::get(&map.find(key)) == ~"val");
    }

    #[test]
    fn test_clear() {
        let key = ~"k";
        let map = map::HashMap::<~str, ~str>();
        map.insert(key, ~"val");
        assert (map.size() == 1);
        assert (map.contains_key(key));
        map.clear();
        assert (map.size() == 0);
        assert (!map.contains_key(key));
    }

    #[test]
    fn test_hash_from_vec() {
        let map = map::hash_from_vec(~[
            (~"a", 1),
            (~"b", 2),
            (~"c", 3)
        ]);
        assert map.size() == 3u;
        assert map.get(~"a") == 1;
        assert map.get(~"b") == 2;
        assert map.get(~"c") == 3;
    }
}
