new object $dictionary: $libraries;

var $root inited = 1;

public method .add_elem() {
    arg dict, key, elem;
    var value;
    
    // same as old dict_add_elem
    value = (| dict[key] |);
    if (type(value) != 'list && type(value) != 'error)
        throw(~type, "Value for key " + toliteral(key) + " (" + toliteral(value) + ") is not a list.");
    if (value)
        value = [@value, elem];
    else
        value = [elem];
    return dict.add(key, value);
};

public method .add_elem_union() {
    arg dict, key, elem;
    var value;
    
    value = (| dict[key] |);
    if (value)
        value = setadd(value, elem);
    else
        value = [elem];
    return dict_add(dict, key, value);
};

public method .apply() {
    arg tdict, list;
    var x;
    
    // Apply a translation-dict to a list
    for x in [1 .. list.length()] {
        catch ~keynf
            list = list.replace(x, tdict[list[x]]);
    }
    return list;
};

public method .apply_to_keys() {
    arg tdict, dict;
    var x, newdict;
    
    // Apply a t-dict to the keys of a dict
    newdict = #[];
    for x in (dict) {
        catch ~keynf
            x = x.replace(1, tdict[x[1]]);
        newdict = newdict.add(@x);
    }
    return newdict;
};

public method .apply_to_values() {
    arg tdict, dict;
    var x, newdict;
    
    // Apply a t-dict to the values of a dict
    newdict = #[];
    for x in (dict) {
        catch ~keynf
            x = x.replace(2, tdict[x[2]]);
        newdict = newdict.add(@x);
    }
    return newdict;
};

public method .del_elem() {
    arg dict, key, elem;
    var value;
    
    value = (| dict[key] |);
    if (type(value) != 'list && type(value) != 'error)
        throw(~type, "Value for key " + toliteral(key) + " (" + toliteral(value) + ") is not a list.");
    value = value.setremove(elem);
    if (!value)
        return dict.del(key);
    return dict.add(key, value);
};

public method .invert() {
    arg dict;
    var inverted, x;
    
    // Invert a dict (keys<->values)
    inverted = #[];
    for x in (dict.keys())
        inverted = inverted.add(dict[x], x);
    return inverted;
};

public method .map_method() {
    arg ls, what;
    var x, dict;
    
    // args[1] == list of objects
    // args[2] == symbol for method on objects
    // it will create a dictionary out of the two.
    dict = #[];
    
    // Get list's method(whatever) and add it to the dictionary
    for x in [1 .. ls.length()]
        dict = dict.add(ls[x], ls[x].(what)());
    return dict;
};

public method .merge() {
    arg [args];
    var x, dict, z, tule, dz, axz, keys;
    
    // merges all dictionaries into a single one, if they have the same key's --
    // basing off of args[1] (this should be the longest list (i know, bad Lynx).
    dict = args[1];
    keys = args[1].keys();
    for x in [2 .. args.length()] {
        for z in (keys) {
            dz = dict[z];
            axz = args[x][z];
            if (type(dict[z]) == 'list)
                tule = dz;
            else
                tule = [dz];
            tule = [@tule, axz];
            dict = dict.add(z, tule);
        }
    }
    return dict;
};

public method .merge_to_list() {
    arg [args];
    var x, dict, z, tule, dz, axz, keys, list;
    
    // merges all dictionaries into a single list, where each related key
    // is merged with all it's other values as a subrange
    // basing off of args[1] (this should be the longest list (i know, bad Lynx).
    dict = .merge(@args);
    list = [];
    for z in (dict.keys())
        list = [@list, dict[z]];
    return list;
};

public method .nunion() {
    arg dict1, dict2;
    var key;
    
    // like union() but for dictionaries.  adds any keys from dict2 that don't
    // already exist in dict1 to dict1 and returns the result.  Order of keys in
    // result is not guaranteed.
    if (dict1.keys().length() < dict2.keys().length()) {
        for key in (dict1)
            dict2 = dict2.add(key[1], key[2]);
        return dict2;
    } else {
        for key in (dict2)
            dict1 = dict1.add(key[1], key[2]);
        return dict1;
    }
};

public method .ordered_union() {
    arg dict1, dict2;
    var key;
    
    // like union() but for dictionaries.  adds any keys from dict2 that don't
    // already exist in dict1 to dict1 and returns the result.  
    // This one is guaranteed to pass the keys from argument1 to the argument2. It is more suitable for argument parsing.
    for key in (dict1)
        dict2 = dict2.add(key[1], key[2]);
    return dict2;
    
    // $#Copied 05 Jul 96 00:58 from $dictionary.union() by $jenner
};

public method .replace() {
    arg dict, key, value;
    
    dict = (> dict.del(key) <);
    dict = (> dict.add(key, value) <);
    return dict;
};

public method .setadd_elem() {
    arg dict, key, elem;
    var value;
    
    value = (| dict[key] |);
    if (value)
        value = setadd(value, elem);
    else
        value = [elem];
    return dict_add(dict, key, value);
};

public method .to_list() {
    arg dict;
    var list, x, k;
    
    // merges into an associated list.
    k = dict.keys();
    list = [];
    for x in (k)
        list = [@list, [x, dict[x]]];
    return list;
};

public method .to_trie() {
    arg dict;
    var i, trie;
    
    trie = (<$trie, [0, ""]>);
    for i in (dict) {
        trie = trie.add(i[1], i[2]);
        refresh();
    }
    return trie;
};