new object $list: $libraries; var $root inited = 1; private method ._merge_sort() { arg list, keys; var i, j, l1, k1, l2, k2, n1, n2, n; n = listlen(list); if (n == 1) return [list, keys]; n1 = n / 2; n2 = n - n1; l1 = ._merge_sort(list.subrange(1, n1), keys.subrange(1, n1)); k1 = l1[2]; l1 = l1[1]; l2 = ._merge_sort(list.subrange(n1 + 1, n2), keys.subrange(n1 + 1, n2)); k2 = l2[2]; l2 = l2[1]; list = []; keys = []; i = 1; j = 1; if (n > 30) pause(); while (i <= n1 && j <= n2) { if (k1[i] <= k2[j]) { list = [@list, l1[i]]; keys = [@keys, k1[i]]; i = i + 1; } else { list = [@list, l2[j]]; keys = [@keys, k2[j]]; j = j + 1; } } return [[@list, @l1.subrange(i), @l2.subrange(j)], [@keys, @k1.subrange(i), @k2.subrange(j)]]; }; public method ._swap_compare() { arg elem1, elem2, compare, [elem]; elem = [@elem, 1][1]; switch (compare) { case 'lt: return elem1[elem] < elem2[elem]; case 'gt: return elem1[elem] > elem2[elem]; default: return 0; } }; public method ._vcolumnize() { arg list, lines, cols, width, [other]; var outlist, line, i, j, sep; sep = [@other, " "][1]; width -= sep.length(); lines = lines > list.length() ? list.length() : lines; outlist = []; for i in [1 .. lines] { line = list[i].pad(width); for j in [1 .. cols] (| (line = line + sep + list[i + j * lines].pad(width)) |); outlist = [@outlist, line]; } return outlist; }; public method .add() { arg list, element; return (> list.setadd(element) <); }; public method .addkey() { arg l, key, val; var i; find i in [1 .. l.length()] where (l[i][1] == key); return i ? l.replace(i, [key, val]) : [@l, [key, val]]; }; public method .affix() { arg l1, l2; var last, first; // Combines l1 and l2 by appending the first element of l2 to the last // of l1. if (type(l2) != 'list) l2 = [l2]; last = (| l1.last() |) || ""; first = (| l2[1] |) || ""; l1 = [@l1.chop(), last + first]; if (l2.length() > 1) l1 = l1 + l2.subrange(2); return l1; }; public method .alphabet() { //returns the alphabet in a list return ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]; }; public method .center_lines() { arg lines, width, [args]; var line; return map line in (lines) to ($string.center(line, width, @args)); }; public method .chop() { arg list, [count]; // chops the last <count> elements off the list. // return [] if count is longer then the list. count = count || 1; return (| list.subrange(1, list.length() - count) |) || []; }; public method .columnize() { arg list, cols, [rest]; var width, lines, line, separator, linelength, curcol; // turn [...] into ". . ." // rest[1]==separator; rest[2]==linelength separator = [@rest, " "][1]; linelength = [@rest, 78, 78][2]; width = linelength / cols - separator.length(); lines = []; while (list) { line = list[1].pad(width); list = list.subrange(2); for curcol in [2 .. cols] { if (list) { line = line + separator + list[1].pad(width); list = list.subrange(2); } } lines = [@lines, line]; } return lines; }; public method .compress() { arg list; var x; // [a,a,b,b,c,c,d,d] => [a,b,c,d] // removes duplicate entries in a list return hash x in (list) to ([x, 1]).keys(); }; public method .count() { arg list, elem; var count; // count of elem in list while (elem in list) { count = count + 1; list = list.subrange((elem in list) + 1); } return count; }; public method .del() { arg list, element; return (> list.setremove(element) <); }; public method .delkey() { arg l, key; var i; find i in [1 .. l.length()] where (l[i][1] == key); return i ? l.delete(i) : l; }; public method .element_maxlength() { arg list; var elm; return map elm in (list) to (tostr(elm).length()).max(); }; public method .flatten() { arg list; var toret, elem; // [[[x], x], x] => [x, x, x] toret = []; for elem in (list) { if (type(elem) == 'list) toret += .flatten(elem); else toret = toret + [elem]; } return toret; }; public method .fold() { arg list, object, method, [args]; var i, out; // apply object.method to a current result and the next element, return the // result switch (list.length()) { case 0: return 0; case 1: return list[1]; } out = list[1]; for i in (list.subrange(2, list.length() - 1)) out = object.(method)(out, i, @args); return out; }; public method .getkey() { arg l, key; var i; find i in [1 .. l.length()] where (l[i][1] == key) || throw(~keynf, "Key not found."); return l[i][2]; }; public method .getkey_index() { arg l, key; var i; find i in [1 .. l.length()] where (l[i][1] == key) || throw(~keynf, "Key not found."); return i; }; public method .grep() { arg lines, regexp; var line, result, out, reg; out = []; for line in [1 .. lines.length()] { reg = lines[line].match_regexp(regexp); if (reg) out = out + [[line, reg, lines[line]]]; } return out; }; public method .heapsort() { arg list, [sort_by]; var heap, sort_type, sort_elem; sort_elem = [@sort_by, 1][1]; sort_type = [@sort_by, 'gt, 'gt][2]; switch (sort_type) { case 'gt: heap = $small_first_heap_class.new(list, sort_elem); default: return list; } list = []; while (heap.length()) { list = heap.element(1); heap = heap.del(1); } return list; }; public method .intersection() { arg l1, l2; var i, out; // set intersection if the arguments out = []; for i in (l1) { if (i in l2) out = out.setadd(i); } return out; }; public method .last() { arg list; return list[list.length()]; }; public method .lcolumnize() { arg list, [args]; var line, part, lines, max, cols, col, width, len, sep; len = [@args, (| sender().linelen() |) || 79][1]; sep = [@args, "", ""][2]; lines = []; line = ""; max = .element_maxlength(list) + sep.length(); cols = len > max ? len / max : 1; width = len / cols - sep.length(); col = cols; for part in (list) { col = col - 1; if (!col) { lines = lines + [line + part]; line = ""; col = cols; continue; } line = line + part.pad(width); } if (line) return lines + [line]; return lines; }; public method .lmap() { arg list, method, [args]; var x, s; //call methods for each thing in list on sender() s = sender(); return map x in (list) to (s.(method)(x, @args)); }; public method .make() { arg n, [elt]; var i; elt = [@elt, 0][1]; return map i in [1 .. n] to (elt); }; public method .map_to_english() { arg list, method, [args]; // because I (Lynx) am lazy return .to_english(.mmap(list, method, @args)); }; public method .map_to_string() { arg list, method, [args]; // because I (Lynx) am lazy return .join(.mmap(list, method, @args)); }; public method .max() { arg list; return (| max(@list) |) || 0; }; public method .mfilter() { arg list, method, [args]; var x; // similar to .mmap, but returns a list of objects which returned a // true value from 'method. return filter x in (list) where (x.method(@args)); }; public method .min() { arg list; return (| min(@list) |) || 0; }; public method .mmap() { arg list, method, [args]; var x; // call 'method on each object, return results. return map x in (list) to (x.(method)(@args)); }; public method .msort() { arg list, [keys]; keys = keys ? keys[1] : list; if (listlen(list) != listlen(keys)) throw(~invarg, "Invalid key list - the list lengths must be the same."); if (!list) return []; return (._merge_sort(list, keys))[1]; }; public method .non_alphanumeric() { // returns nun-alphanumeric in a list of characters return ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "-", "=", "~", "`", "'", "{", "}", "[", "]", "|", "/", "?", "\"", "\\", ",", ".", "<", ">", ";", ":", " "]; }; public method .nth_element_maxlength() { arg lists, element; var list; // Returns longest string whose index is element in one of the lists in // lists. if (type(element) != 'integer) throw(~type, "Second argument is not an integer"); if (type(lists) != 'list) throw(~type, "First argument is not a list"); return map list in (lists) to (tostr(list[element]).length()).max(); }; public method .numbered_text() { arg text; var line; // receives a list of strings, returns that list with line numbers // prepended return map line in [1 .. text.length()] to ("%3r: %l".format(line, text[line])); }; public method .numbers() { // returns a list of numbers return ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; }; public method .omap() { arg list, object, method, [args]; var obj; // calls object.method(obj, @args) for each obj in list return map obj in (list) to (object.(method)(obj, @args)); }; public method .prefix() { arg list, prefix; var elem; return map elem in (list) to (prefix + elem); }; public method .random() { arg list; return list[random(listlen(list))]; }; public method .reverse() { arg list; var i, len; // .reverse(list) // -> list with its elements reversed len = list.length() + 1; return map i in [1 .. len - 1] to (list[len - i]); }; public method .set_contains() { arg [args]; var super, list, element; // True if the first list given is a superset of all subsequent lists. // False otherwise. [] is a superset of [] and nothing else; anything is // a superset of []. If only one list is given, return true. super = args ? args[1] : []; for list in (args.delete(1)) { for element in (list) { if (!(element in super)) return 0; } } return 1; }; public method .set_difference() { arg [args]; var set, list, element; // Usage: diff(set 1, set 2, ..., set n) // Returns all elements of set 1 that are not in sets 2..n if (!args) return []; set = args[1]; for list in (args.delete(1)) { for element in (list) set = set.setremove(element); } return set; }; public method .set_equal() { arg set1, set2; var element; // True if the two lists given contain the same elements. // False otherwise. while (set1) { element = set1[1]; if (!element in set2) return 0; while (element in set2) set2 = set2.setremove(element); while (element in set1) set1 = set1.setremove(element); } return set2 == []; }; public method .setremove_all() { arg list, remove; while (remove in list) list = .setremove(list, remove); return list; }; public method .slice() { arg big_list, element; var list; // Return elementh' element of all lists in big_list // No type or length checking done for speed purposes. return map list in (big_list) to (list[element]); }; public method .sum() { arg data; var ret, i; // returns a sum of each element in the list. ret = data[1]; for i in (data.subrange(2)) ret += i; return ret; }; public method .swap() { arg list, a, b; var holder; // swap elements at indexes a and b holder = (> list[a] <); list = (> list.replace(a, list[b]) <); list = list.replace(b, holder); return list; }; public method .swapsort() { arg list, [sort_by]; var bot_elem, cur_elem, elem, compare; // note: iterative implementation allows sorts of extra-long lists elem = [@sort_by, 1]; compare = [@sort_by, 'gt, 'lt][2]; for bot_elem in [1 .. list.length()] { for cur_elem in [bot_elem + 1 .. list.length()] { if (._swap_compare(list[bot_elem], list[cur_elem], compare, elem)) list = $list.swap(list, bot_elem, cur_elem); } } return list; }; public method .to_buffer() { arg [args]; return (> $buffer.from_strings(@args) <); }; public method .to_english() { arg list, [options]; var empty, and, sep; empty = [@options, "nothing"][1]; switch (list.length()) { case 0: return empty; case 1: return tostr(list[1]); } and = [@options, ", and ", ", and "][2]; sep = [@options, ", ", ", ", ", "][3]; return join(list.delete(list.length()), sep) + and + tostr(list[list.length()]); }; public method .valid_objects() { arg list; var obj; return filter obj in (list) where (valid(obj)); }; public method .vcolumnize() { arg list, cols, [rest]; var linelength, sep, width, lines, i, j, line, outlist; linelength = [@rest, (| sender().linelen() |) || 79][1]; sep = [@rest, " ", " "][2]; lines = list.length() / cols + (list.length() % cols ? 1 : 0); width = linelength / cols; return ._vcolumnize(list, lines, cols, width, sep); }; public method .vcolumnize2() { arg list, lines, [rest]; var linelength, sep, cols, width, i, j, line, outlist; linelength = [@rest, (| sender().linelen() |) || 79][1]; sep = [@rest, " ", " "][2]; cols = list.length() / lines + (list.length() % lines ? 1 : 0); width = linelength / cols; return ._vcolumnize(list, lines, cols, width, sep); }; public method .vcolumnize3() { arg list, lines, [rest]; var linelength, cols, width, i, j, line, outlist; linelength = [@rest, (| sender().linelen() |) || 79][1]; cols = list.length() / lines + (list.length() % lines ? 1 : 0); width = linelength / cols; outlist = []; for i in [1 .. lines] { line = ""; for j in [0 .. cols] (| (line = line + list[i + j * lines].pad(width)) |); outlist = [@outlist, line]; } return outlist; }; public method .vcolumnize4() { arg list, [args]; var linelength, sep, lines, cols, width, max; linelength = [@args, (| sender().linelen() |) || 79][1]; sep = [@args, "", ""][2]; max = .element_maxlength(list) + sep.length(); cols = linelength > max ? linelength / max : 1; width = linelength / cols; lines = list.length() / cols + (list.length() % cols ? 1 : 0); return ._vcolumnize(list, lines, cols, width, sep); };