guugelhupf/ 40755 1751 0 0 7515105216 12267 5ustar mneumannwheelguugelhupf/src.old/ 40755 1751 0 0 7515105216 13633 5ustar mneumannwheelguugelhupf/src.old/ruby/ 40755 1751 0 0 7515105216 14614 5ustar mneumannwheelguugelhupf/src.old/ruby/util.rb100644 1751 0 103 7515105044 16164 0ustar mneumannwheelAbstractMethodException = Exception.new("Abstract method called!") guugelhupf/src.old/ruby/gh.rb100644 1751 0 25754 7515105044 15670 0ustar mneumannwheel# # Copyright (c) 2002 Michael Neumann # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL # THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require "util" module GH # # Representation of a token. # class Token attr_accessor :str, :pos def initialize(str, pos) @str, @pos = str, pos end end # # Abstract superclass of all kinds of streams. # class Stream ## # returns:: nil on Eos # def next raise AbstractMethodException end def each(&b) while token = self.next b.call token end end end # # Abstract superclass of all streams returning Token objects. # class TokenStream < Stream end # # Abstract superclass of all token filters. # class TokenFilter < TokenStream def initialize(inStream) @inStream = inStream end end class Tokenizer def initialize(stringOrReadable) @stringOrReadable = stringOrReadable end end # --------------------------------------------------------------------------------------- # Concrete Implementations # --------------------------------------------------------------------------------------- class SimpleTokenizer < Tokenizer def initialize(stringOrReadable) super @next_callcc = nil @pos = 0 end def next if @next_callcc.nil? @stringOrReadable.scan(/[A-Za-z]+/) do |token| callcc { | @next_callcc | return Token.new(token, @pos += 1) } end return nil else @next_callcc.call end end end class StopwordTokenFilter < TokenFilter def initialize(inStream, hashTable) super(inStream) @hashTable = hashTable end def next loop { if token = @inStream.next return token unless @hashTable.member?(token.str) else return nil end } end end class LowercaseTokenFilter < TokenFilter def next if token = @inStream.next token.str.downcase! token else nil end end end # # Filters tokens through an external program. # class ExternalTokenFilter < TokenFilter def initialize(inStream, command, write_all_before_read=false) super(inStream) @pipe = IO.popen(command, "w+") @write_all_before_read = write_all_before_read @pipe.sync = true @write_thread = Thread.new { loop { @inStream.each do |token| put_token(token) end } @pipe.close_write } if not @write_all_before_read @write_thread.priority=-100_000 # TODO: stop? end end def next if @write_all_before_read get_token else if @write_thread.alive? @write_thread.priority=0 # TODO: wakeup? token = get_token @write_thread.priority=-100_000 # TODO: stop? token else get_token end end end private def put_token(token) # TODO: External format @pipe.puts "#{token.str} #{token.pos}" end def get_token # TODO: External format if token = @pipe.gets Token.new(*token.split) else nil end end end class ExternalTokenStream < TokenStream def initialize(io) @io = io @pos = 0 @token = Token.new(nil, nil) end def next if line=@io.gets line.chomp! #a = line.split(/\s+/) # @token.str = line @token.pos = (@pos += 1) #Token.new(line, @pos += 1) @token # TODO: file-representation: pos1 word1 \n pos2 word2 else nil end end end class HashTable attr_reader :table, :size def initialize(size=5_000) @size = size @table = Array.new(@size) end def [](key) value = @table[key.hash % @size] if value.nil? nil else # value: [ [k1, v1], [k2, v2] ] value = value.assoc(key) if value.nil? nil else value[1] end end end def []=(key, val) value = @table[key.hash % @size] if value.nil? # no entry yet @table[key.hash % @size] = [ [key, val] ] else # value: [ [k1, v1], [k2, v2] ] x = value.assoc(key) if x.nil? # no entry yet value << [key, val] else # replace value x[1] = val end end end def key?(key) self[key] != nil end def each_pair(&b) @table.each do |value| next if value.nil? # value: [ [k1, v1], [k2, v2] ] value.each(&b) end end end # hash[token] = [ [token1, [pos, pos, ...]], [token2, [pos, pos, pos] ] class Index attr_reader :index, :docId def initialize(docId) @index = HashTable.new @docId = docId end def add(token) if @index.key? token.str # posting already exists posting = @index[token.str] # assert posting.docId == @docId posting.positions << token.pos else # create new posting posting = Posting.new(@docId) posting.positions << token.pos @index[token.str] = posting end end end class CharStream def initialize(stringOrReadable) @stringOrReadable = stringOrReadable case @stringOrReadable when IO @next = proc {|obj| obj.getc} when String @next = proc {|obj| if obj.empty? nil else res = obj[0] if res != nil obj[0,1] = "" end res end } end end def next @next.call(@stringOrReadable) end end # TODO: find better names for class Posting class Posting attr_accessor :docId, :positions def initialize(docId, positions=nil) @docId = docId @positions = positions || [] end =begin def dump_to_string "#{docId}(" + @positions.join(" ") + ")" end =end def dump_to_string [docId, @positions.size, *@positions].pack("N*") end =begin # file must implement getc def self.read_from_stream(stream) str = "" while (c=stream.next) != ?) str << c.chr end str << c if str =~ /^(\d+)[(](.*?)[)]$/ Posting.new($1.to_i, $2.split(" ").collect{|i| i.to_i}) else raise "Wrong format" end end =end end # # Stores multiple Index objects in one Hash. # (the JoinedIndex is readonly). # class JoinedIndex attr_reader :index def initialize @index = HashTable.new end def addIndex(index) # index has for each key one posting index.index.each_pair {|word, posting| if @index.key? word postings_list = @index[word] postings_list << posting # TODO: OrderedList after docId else @index[word] = [posting] end } end def dump_to_file(ix, pst) ix << [@index.size].pack("N") pst_pos = 0 @index.table.each_with_index do |cell, i| if cell.nil? # offset -1 means empty cell ix << [-1].pack("N") else ix << [pst_pos].pack("N") cell_str = dump_cell_to_string(cell) pst_pos += cell_str.size pst << cell_str end end end private def dump_entry_to_string(word, postings) # word(size)....size bytes.... pst_str = postings.collect {|posting| posting.dump_to_string }.join("") "#{word}(#{pst_str.size})#{pst_str}" end def read_entry_header_from_file(file) str = "" while (c=file.getc) != ?) str << c.chr end str << c if str =~ /^([^(]*)[(](\d+)[)]$/ [$1, $2.to_i] # (word, size) else raise "ERROR" end end # a cell is a cell of the HashTable def dump_cell_to_string(cell) # cell: [ (word, postings), (word, postings) ] # representation as string: # nr_of_words: INT # entry1 # entry2 # ... [cell.size].pack("N") + cell.collect do |word, postings| dump_entry_to_string(word, postings) end.join("") end end class SimpleAnalyser def initialize(index) @index = index end def process(stringOrReadable) stream = LowercaseTokenFilter.new( SimpleTokenizer.new(stringOrReadable)) stream.each do |token| @index.add(token) end end end class StopwordAnalyser def initialize(index, hash) @index = index @hash = hash end def process(stringOrReadable) stream = LowercaseTokenFilter.new( SimpleTokenizer.new(stringOrReadable)) stream = StopwordTokenFilter.new(stream, @hash) stream.each do |token| @index.add(token) end end end class StopwordAnalyserX def initialize(index, hash) @index = index @hash = hash end def process(stringOrReadable) stream = LowercaseTokenFilter.new( ExternalTokenStream.new(stringOrReadable)) stream = StopwordTokenFilter.new(stream, @hash) stream.each do |token| @index.add(token) end end end def stopWordList_to_HashTable(fileName) hash = {} IO.foreach(fileName) do |line| hash[line.chomp] = true end hash end module_function :stopWordList_to_HashTable end # module GH if __FILE__ == $0 include GH jindex = JoinedIndex.new stopWords = stopWordList_to_HashTable("stopWords.en") docId = 1 fileSize = 0 while filename=STDIN.gets filename.chomp! puts "#{docId.to_s.ljust(6)} #{fileSize.to_s.ljust(8)} #{filename}" index = Index.new(docId) #file = File.read(filename) fileSize += File.size(filename) cmd = "/home/mneumann/mmap.gcc #{filename}" IO.popen(cmd) do |file| # StopwordAnalyserX.new(index, stopWords).process(file) jindex.addIndex(index) end # docId += 1 end p fileSize jindex.dump_to_file(File.new("/tmp/words.idx", "w+"), File.new("/tmp/words.cnt", "w+")) =begin #jindex.index.each_pair {|key, postings_list| # puts "#{ key }: #{ postings_list.first.positions.size }" #} jindex.index["michael"].each do |posting| p posting f = File.open("/tmp/aaa", "w+") f << posting.dump_to_string f.close f = File.open("/tmp/aaa") read_posting = Posting.read_from_stream(CharStream.new(f)) p read_posting end =end end guugelhupf/src.old/token_stream.ml100644 1751 0 3355 7515105044 16762 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) (* $Id$ *) exception Eos_exception class token (str:string) (pos:int) = object val mutable str = str val mutable pos = pos method get_str = str method get_pos = pos end class type ['a] stream = object method next : 'a end class type token_stream = [token] stream guugelhupf/src.old/token.cc100644 1751 0 1323 7515105044 15355 0ustar mneumannwheel// --------------------------------------------------------------------- class Token { protected: // This may be either byte-position, word-position or something else. int position; // Pointer to data word. char *start_ptr; char *end_ptr; // Type of token. May be unused. char *type; }; // --------------------------------------------------------------------- template class Stream { public: virtual TokenType& next() = 0; }; // --------------------------------------------------------------------- template class Filter : public Stream { public: Filter(Stream &inStream) : inStream(inStream) { } protected: Stream &inStream; }; guugelhupf/src.old/cc/ 40755 1751 0 0 7515105216 14220 5ustar mneumannwheelguugelhupf/src.old/cc/Stream.h100644 1751 0 225 7515105044 15677 0ustar mneumannwheel#ifndef __HEADER__STREAM__ #define __HEADER__STREAM__ template class Stream { public: virtual TokenType* next() = 0; }; #endif guugelhupf/src.old/cc/MyMmapTokenStream.cc100644 1751 0 3510 7515105044 20177 0ustar mneumannwheel#include "MyMmapTokenStream.h" MyMmapTokenStream::MyMmapTokenStream(int fd, int length) : MmapStream::MmapStream(fd, length) { this->last_ptr = (char*) this->ptr + this->length; currentToken.position = -1; // position is word-wise (start with 0) currentToken.end_ptr = currentToken.start_ptr = (char*) this->ptr; currentToken.type = (char*)NULL; } char MyMmapTokenStream::buchstabe[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; Token* MyMmapTokenStream::next() { currentToken.position += 1; currentToken.start_ptr = currentToken.end_ptr; // go on where we left off last time // skip non characters while (currentToken.start_ptr < this->last_ptr && !MyMmapTokenStream::buchstabe[*currentToken.start_ptr]) { ++currentToken.start_ptr; } currentToken.end_ptr = currentToken.start_ptr; // consume characters while (currentToken.end_ptr < this->last_ptr && MyMmapTokenStream::buchstabe[*currentToken.end_ptr]) { ++currentToken.end_ptr; } if (currentToken.end_ptr >= this->last_ptr) { return (Token*) NULL; } return ¤tToken; } guugelhupf/src.old/cc/test.cc100644 1751 0 2772 7515105044 15612 0ustar mneumannwheel#include #include #include #include #include #include #include "MyMmapTokenStream.h" int getFileLength(int handle) { int length; length = lseek(handle, 0, SEEK_END); lseek(handle, 0, SEEK_SET); return length; } void printout(char *start, char *end) { while (start != end) { putchar(*start); start++; } } struct eqstr { bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) == 0; } }; int main(int argc, char** argv) { int fd; int length; Token *tok; if ((fd = open(argv[1], O_RDONLY)) == -1) { perror("Failed to open file!"); exit(-1); } length = getFileLength(fd); MyMmapTokenStream obj(fd, length); hash_map, eqstr> h; int str_len; char *str; char *cur; char buffer[256]; while( (tok = obj.next()) != NULL ) { printout(tok->start_ptr, tok->end_ptr); putchar('\n'); /* // str_len = tok->end_ptr - tok->start_ptr + 1; //if (str_len > 255) { // perror("Token too large!"); // exit(-1); //} // str = buffer; str = (char*)malloc(str_len+1); if (str == NULL) { perror("Failed to alloc memeory!"); exit(-1); } for(int i=0; istart_ptr[i]); } str[str_len] = '\0'; //free(str); h[str] += 1; //printout(tok->start_ptr, tok->end_ptr); //putchar('\n'); */ } // printf("%d\n", h["sdlkf"]); return 0; } guugelhupf/src.old/cc/MyMmapTokenStream.h100644 1751 0 546 7515105044 20027 0ustar mneumannwheel#ifndef __HEADER__MY_MMAP_TOKEN_STREAM__ #define __HEADER__MY_MMAP_TOKEN_STREAM__ #include "basic.h" #include "Token.h" #include "MmapStream.h" class MyMmapTokenStream : public MmapStream { public: MyMmapTokenStream(int fd, int length); Token* next(); private: Token currentToken; char *last_ptr; static char buchstabe[256]; }; #endif guugelhupf/src.old/cc/Token.h100644 1751 0 644 7515105045 15532 0ustar mneumannwheel#ifndef __HEADER__TOKEN__ #define __HEADER__TOKEN__ #include "basic.h" struct Token { Token() { position = -1; start_ptr = (char*) NULL; end_ptr = (char*) NULL; type = (char*) NULL; } // This may be either byte-position, word-position or something else. int position; // Pointer to data word. char *start_ptr; char *end_ptr; // Type of token. May be unused. char *type; }; #endif guugelhupf/src.old/cc/basic.h100644 1751 0 152 7515105045 15525 0ustar mneumannwheel#ifndef __HEADER__BASIC__ #define __HEADER__BASIC__ #ifndef NULL #define NULL ((void*)0L) #endif #endif guugelhupf/src.old/cc/Filter.h100644 1751 0 411 7515105045 15667 0ustar mneumannwheel#ifndef __HEADER__FILTER__ #define __HEADER__FILTER__ #include "Stream.h" template class Filter : public Stream { public: Filter(Stream &inStream) : inStream(inStream) { } protected: Stream &inStream; }; #endif guugelhupf/src.old/cc/MmapStream.h100644 1751 0 1757 7515105045 16546 0ustar mneumannwheel#ifndef __HEADER__MMAP_STREAM__ #define __HEADER__MMAP_STREAM__ #include // mmap #include // mmap #include // perror #include "basic.h" #include "Stream.h" struct MmapException { char *message; MmapException(char *msg) : message(msg) {} }; template class MmapStream : public Stream { public: MmapStream(int fd, int length) { this->length = length; // mmap file into main memory if ((this->ptr = mmap(NULL, this->length, PROT_READ, 0, fd, 0)) == MAP_FAILED) { //throw MmapException("Failed to memory map file!"); this->ptr = NULL; throw MmapException("Failed to memory map file!"); } //if (madvise(this->ptr, this->length, MADV_SEQUENTIAL) == -1) { perror("Failed to advise VM manager!"); } } ~MmapStream() { munmap(this->ptr, this->length); } protected: void *ptr; // pointer to memeory mapped content int length; // length of memory mapped content }; #endif guugelhupf/src.old/cc/Makefile100644 1751 0 305 7515105045 15733 0ustar mneumannwheeltest: #cc -c MmapStream.cc cc -c MyMmapTokenStream.cc cc -c test.cc # cc MmapStream.o MyMmapTokenStream.o test.o -o test cc MyMmapTokenStream.o test.o -lstdc++ -o test clean: rm *.o rm test guugelhupf/src.old/test.ml100644 1751 0 4500 7515105045 15240 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) (* $Id$ *) open Token_stream class my_tokenizer : token_stream (in_stream : char Stream.t) = object val in_stream = in_stream method next = let buf = Buffer.create 16 in try match Stream.next in_stream with 'A'..'Z' as c -> Buffer.add_char buf c | 'a'..'z' as c -> Buffer.add_char buf c | _ -> if Buffer.length buf = 0 then class my_token_stream : token_stream = object val mutable buffer = [ new token "hallo" 1; new token "bello" 2 ] method next = match buffer with [] -> raise Eos_exception | x::xs -> buffer <- xs; x end ;; (* main program *) let ts = new my_token_stream in try while true do let token = ts#next in print_string token#get_str ; print_string " "; print_int token#get_pos; print_newline () done with Eos_exception -> print_string "at the end"; print_newline () guugelhupf/src.old/README100644 1751 0 513 7515105045 14567 0ustar mneumannwheel// don't use templates. they make it only unneccessaryly hard // index for only _one_ document abstract class DocumentIndex { } // stores the frequency of each word only. class TokenFrequencyDocumentIndex < DocumentIndex { } // stores the word positions of each occured word class TokenPositionDocumentIndex < DocumentIndex { } guugelhupf/src.old/ruby.old/ 40755 1751 0 0 7515105216 15371 5ustar mneumannwheelguugelhupf/src.old/ruby.old/test.rb100644 1751 0 4623 7515105045 16777 0ustar mneumannwheelrequire "util" module GH # a TokenFilter is not just a decider whether or not a Token will # be token, but it may also modify the token! module TokenStream include Enumerable def nextToken raise AbstractMethodException end def nextToken? end def each while nextToken? yield nextToken end end def |(filter) filter.each(self) do |token| yield token end end end class Analyser include TokenStream # ??? end class Tokenizer include TokenStream # returns a TokenStream # ... end # abstract class TokenFilter include TokenStream #include Enumerable # returns a TokenStream, input is also TokenStream # apply self on stream and return a new TokenStream def each(stream) stream.each do |token| yield token if isToken? token end end alias applyOnStream each def isToken?(token) raise AbstractMethodException end end class StopwordFilter < TokenFilter # returns a TokenStream, input is also TokenStream end class SimpleStopwordFilter < StopwordFilter end class StemmingFilter < TokenFilter # returns a TokenStream, input is also TokenStream end class SimpleAnalyser < Analyser def initialize(fileName) @tokenizer = Tokenizer.new(fileName) @stopWordFilter = SimpleStopwordFilter.new end def nextPosting #@tokenizer is a Tokenstream @tokenizer.filter(@stopWordFilter).filter(@stemmingFilter) @tokenizer | @stopWordFilter | @stemmingFilter @stopWordFilter.filter(@tokenizer) end end class MyTokenFilter < TokenFilter def isToken?(token) token > 2 end end class MyTokenizer < Tokenizer def initialize @tokens = [1,2,3,4] end def nextToken? not @tokens.empty? end def nextToken @tokens.shift end end class MyAnalyser < Analyser def initialize @tokenizer = MyTokenizer.new @filter = MyTokenFilter.new end def each @filter.applyOnStream(@tokenizer) {|t| yield t} #@filter.each @tokenizer.each {|t| yield t} #@tokenizer | end end class ExternalTokenFilter end class FilteredTokenStream def initialize(stream, filter) end end #FilteredTokenStream.new(Tokenizer.new(file), StopwordFilter.new) end # module GH class TokenFilter ## token gets rejected if returns nil, # otherwise the returned token is passed back def filter(token) raise AbstractMethodException end end a = GH::MyAnalyser.new p a.entries guugelhupf/src.old/ruby.old/a.rb100644 1751 0 13625 7515105045 16262 0ustar mneumannwheel# # Copyright (c) 2002 Michael Neumann # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL # THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require "util" module GH # a TokenFilter is not just a decider whether or not a Token will # be taken, but it may also modify the token! module TokenStream include Enumerable def nextToken raise AbstractMethodException end def nextToken? end def each while nextToken? yield nextToken end end def |(filter) filter.each(self) do |token| yield token end end end class Analyser include TokenStream # ??? end class Tokenizer include TokenStream # returns a TokenStream # ... end # abstract class TokenFilter include TokenStream #include Enumerable # returns a TokenStream, input is also TokenStream # apply self on stream and return a new TokenStream def each(stream) stream.each do |token| yield token if isToken? token end end alias applyOnStream each def isToken?(token) raise AbstractMethodException end end class StopwordFilter < TokenFilter # returns a TokenStream, input is also TokenStream end class SimpleStopwordFilter < StopwordFilter end class StemmingFilter < TokenFilter # returns a TokenStream, input is also TokenStream end class SimpleAnalyser < Analyser def initialize(fileName) @tokenizer = Tokenizer.new(fileName) @stopWordFilter = SimpleStopwordFilter.new end def nextPosting #@tokenizer is a Tokenstream @tokenizer.filter(@stopWordFilter).filter(@stemmingFilter) @tokenizer | @stopWordFilter | @stemmingFilter @stopWordFilter.filter(@tokenizer) end end class MyTokenFilter < TokenFilter def isToken?(token) token > 2 end end class MyTokenizer < Tokenizer def initialize @tokens = [1,2,3,4] end def nextToken? not @tokens.empty? end def nextToken @tokens.shift end end class MyAnalyser < Analyser def initialize @tokenizer = MyTokenizer.new @filter = MyTokenFilter.new end def each @filter.applyOnStream(@tokenizer) {|t| yield t} #@filter.each @tokenizer.each {|t| yield t} #@tokenizer | end end class ExternalTokenFilter end class FilteredTokenStream def initialize(stream, filter) end end #FilteredTokenStream.new(Tokenizer.new(file), StopwordFilter.new) end # module GH ####################################################################################### # Todo:: concurrent access? class TokenStream include Enumerable def nextToken raise AbstractMethodException end def hasToken? raise AbstractMethodException end def each #:yiels: token while hasToken? yield nextToken end end end # should a filter be possible to add tokens to the stream? # I think yes! # # inputStream :filter: outputStream # class TokenFilter # _token_ gets rejected if this method returns nil, # otherwise the returned token (possibly modified) stays # in the stream. # If an Array is returned, than all elements are inserted back # into the stream. def filter(token) raise AbstractMethodException end def filterStream(inputStream, outputStream) end end class FilteredTokenStream < TokenStream def initialize(stream, filter) @stream, @filter = stream, filter @buffer = [] end def hasToken? @stream.hasToken? || !@buffer.empty? end # Todo: may not be access concurrently! def nextToken loop { if @buffer.empty? tok = @stream.nextToken res = @filter.filter(tok) if res.nil? next elsif res.is_a? Array @buffer.push(*res) next else return res end else return @buffer.shift end } end end ################################ class MyTokenFilter < TokenFilter def filter(token) puts "------ in filter" puts "token: #{token}" return nil if token < 2 # reject if token < 4 token + 10 else [token] * 5 end end end class MyTokenStream < TokenStream def initialize @tokens = [1,2,3,4] end def hasToken? not @tokens.empty? end def nextToken @tokens.shift end end a = FilteredTokenStream.new(MyTokenStream.new, MyTokenFilter.new) p a.entries # TokenStream: method `next' # TokenFilter: is_a TokenStream. # Tokenizer: a TokenStream # (filter in) => out # |(filter in) # (in | filter1) | filter2 =begin class ['a] filter = object end;; let f = new filter;; let filtered_stream = f#create_filtered_stream stream_x;; method create_filtered_stream stream = filtered_stream stream filter ;; class ['a] stream = object end;; =end guugelhupf/src.old/ruby.old/b.rb100644 1751 0 4657 7515105045 16250 0ustar mneumannwheel# # Copyright (c) 2002 Michael Neumann # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL # THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require "util" module GH # # Abstract superclass of all kinds of streams. # class Stream #EosException = Exception.new("Reached end of stream!") ## # returns:: nil on Eos # def next raise AbstractMethodException end end # # Abstract superclass of all streams returning Token objects. # class TokenStream < Stream end # # Abstract superclass of all token filters. # class TokenFilter < TokenStream def initialize(in_stream) @in_stream = in_stream end end class Tokenizer end # # Concrete Implementations # # # Filters tokens through an external program. # class ExternalTokenFilter < TokenFilter def initialize(in_stream, command) super(in_stream) @command = command in, out = popen(@command) end # Todo: have to specify output/input format def next # check if some output is available out << @in_stream.next.external_format # ... end end # # Reads tokens from an external program # class ExternalTokenStream < TokenStream end end # module GH guugelhupf/src.old/ruby.old/wordStaten.rb100644 1751 0 120 7515105045 20116 0ustar mneumannwheelwhile line = gets a = line.split(/\s+/) next if a.size != 4 puts a[2] end guugelhupf/src.old/ruby.old/gh.rb.old100644 1751 0 12616 7515105045 17214 0ustar mneumannwheel# # Copyright (c) 2002 Michael Neumann # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL # THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require "util" module GH # # Representation of a token. # class Token attr_accessor :str, :pos def initialize(str, pos) @str, @pos = str, pos end end # # Abstract superclass of all kinds of streams. # class Stream ## # returns:: nil on Eos # def next raise AbstractMethodException end def each(&b) while token = self.next b.call token end end end # # Abstract superclass of all streams returning Token objects. # class TokenStream < Stream end # # Abstract superclass of all token filters. # class TokenFilter < TokenStream def initialize(inStream) @inStream = inStream end end class Tokenizer def initialize(stringOrReadable) @stringOrReadable = stringOrReadable end end # --------------------------------------------------------------------------------------- # Concrete Implementations # --------------------------------------------------------------------------------------- class SimpleTokenizer < Tokenizer def initialize(stringOrReadable) super @next_callcc = nil @pos = 0 end def next if @next_callcc.nil? @stringOrReadable.scan(/[A-Za-z]+/) do |token| callcc { | @next_callcc | return Token.new(token, @pos += 1) } end return nil else @next_callcc.call end end end class LowercaseTokenFilter < TokenFilter def next if token = @inStream.next token.str.downcase! token else nil end end end # # Filters tokens through an external program. # class ExternalTokenFilter < TokenFilter def initialize(inStream, command, write_all_before_read=false) super(inStream) @pipe = IO.popen(command, "w+") @write_all_before_read = write_all_before_read @pipe.sync = true @write_thread = Thread.new { loop { @inStream.each do |token| put_token(token) end } @pipe.close_write } if not @write_all_before_read @write_thread.priority=-100_000 # TODO: stop? end end def next if @write_all_before_read get_token else if @write_thread.alive? @write_thread.priority=0 # TODO: wakeup? token = get_token @write_thread.priority=-100_000 # TODO: stop? token else get_token end end end private def put_token(token) # TODO: External format @pipe.puts "#{token.str} #{token.pos}" end def get_token # TODO: External format if token = @pipe.gets Token.new(*token.split) else nil end end end # multiset # hash[token] = [ [token1, [pos, pos, ...]], [token2, [pos, pos, pos] ] class Index attr_reader :index, :docId def initialize(docId) @index = {} @docId = docId end def add(token) if @index.key? token.str list = @index[token.str] posting = list.assoc(token.str) if posting.nil? # create new posting posting = [token.str, [token.pos]] list << posting else # add position to existing posting posting[1] << token.pos end else posting = [token.str, [token.pos]] @index[token.str] = [posting] end end end # TODO: find better names for class Posting and Word class Posting attr_accessor :docId, :positions end class Word attr_accessor :word, :postings end # # Stores multiple Index objects in one Hash. # (the JoinedIndex is readonly). # class JoinedIndex def initialize @index = {} end def addIndex(index) index.index.each_pair {|key, value| if @index.key? key @index[] else [index.docId, end } end end if __FILE__ == $0 index = Index.new tokenizer = SimpleTokenizer.new(File.read("/tmp/a.doc")) tokenizer = LowercaseTokenFilter.new(tokenizer) #tokenizer = ExternalTokenFilter.new(tokenizer, "sort", true) #tokenizer = ExternalTokenFilter.new(tokenizer, "grep l", true) tokenizer.each do |token| index.add(token) #p index.index #readline end end end # module GH guugelhupf/src.old/ruby.old/automaton-generator.rb100644 1751 0 476 7515105045 21775 0ustar mneumannwheel# # Generate state tables for a regular expression # # Example 1 (match either "abc" or "def" regexp1 = "abc|def" # Example 2: # Two possible solution: # - longest match ("abcd") # - first match ("abc") ***** prefered for ORing ***** regexp2 = "abc|abcd" # Example 3: # longest match ("abcd") regexp3 = "abc(d)?" guugelhupf/doc/ 40755 1751 0 0 7515105216 13034 5ustar mneumannwheelguugelhupf/doc/limits.sgml100644 1751 0 473 7515105045 15302 0ustar mneumannwheelLimitations Maximale Wortlänge von 255 Zeichen. Maximale Index-Größe von 4 GB. Maximal 2^32 Dokumente pro Index/DocDB. guugelhupf/doc/lang.sgml100644 1751 0 4604 7515105045 14742 0ustar mneumannwheelWahl der geeigneten Programmiersprache Kriterien für die Auswahl Performance! Primäres Ziel ist eine rasche Indexierung, die wenn möglich im Bereich von MB/sec liegen sollte (bezogen auf einen herkömmlichen Computer z.B. Athlon 1000 MHz). Ausdruckskraft der Sprache (d.h. wie gut können Probleme beschrieben/gelöst werden) Darunter sollte jedoch die Leserlichkeit des Programmes nicht leiden. Bevorzugt werden Sprachen mit automatischer Speicherfreigabe (Reference Tracing/Counting oder Regions), da dies eine enorme Steigerung der Produktivität für den Programmierer darstellt und sich nicht erheblich auf die Performance auswirkt. Vorhandensein von geeigneten Algorithmen und Datenstrukturen wie z.B. Splaytrees oder RB-Trees. Leichtes Interfacing mit C. Natürlich sollte auch die eigene Qualifikation in der jeweiligen Programmiersprache in Betracht gezogen werden. Dies haben wir hier jedoch nicht getan. Vorstellung der evaluierten Sprachen Folgende Sprachen wurden unter die Lupe genommen: C++ Java Objective Caml Standard ML Ada 95 Eiffel Modula 3 Ruby TO BE WRITTEN guugelhupf/doc/hashing.sgml100644 1751 0 34056 7515105045 15466 0ustar mneumannwheelWahl einer geeigneten Hash-Funktion Kriterien für die Auswahl Gleichmäßige Verteilung (geringe Anzahl von Kollisionen). Akzeptabler Worstcase, d.h. Maximum der Kollisionen pro Zelle sollte möglichst gering sein. Verteilung sollte auch dann noch erhalten bleiben wenn anstelle von Modulo zur Beschränkung auf einen Wertebereich, eine Bitweise UND-Verknüpfung verwendet wird (wichtig für Performance). Performance. Keine Verwendung von Modulo-Operationen, da diese im Vergleich zu Bitweisen Operationen oder Additionen sehr langsam sind (gerade auf RISC Computern können diese die 100-fache Zeit benötigen). Vorstellung einiger Hash-Algorithmen Knuth's Algorithmus Die in Knuth's "The Art of Computer Programming" (Volume 3 "Sorting and Searching", Kapitel 6.4) vorgestelle Hash-Funktion ist vielleicht eine der bekanntesten überhaupt. Dennoch ist ihre Verteilung nur mittelmäßig. Darüber hinaus verwendet Knuth eine Modulo Operation (mit einer Primzahl) zur Beschränkung auf ein Intervall, welches ihre Performance mindert und wegen der verwendeten Primzahl ein automatisches Vergrößern der Hashtabelle komplizierter macht. >27))^*str++; } return hash; // % prime } ]]> Kernigham und Ritchies Algorithmus Kernigham und Ritchies Algorithmus ist eine einfache additive Hash-Funktion, die jedoch ungeeignet ist für seriöse Anwendungen. D.J. Bernsteins Algorithmus SDBM Algorithmus Dieser Algorithmus wird in Sleepycat's Datenbank BDB (Berkeley DataBase) verwendet. P.J. Weinberg Algorithmus >24; hash ^= g; } } return hash; } ]]> OCaml's Algorithmus Dieser Algorithmus wird in der aktuellen Version (3.04) von Objective Caml verwendet. SML's Algorithmus Dieser Algorithmus wird in der aktuellen Version (110.40) von SML/NJ (Standard ML/New Jersey) verwendet. STL's Algorithmus Dieser Algorithmus wird/wurde (?) in der STL (Standard Template Library) von C++ verwendet. Java's Algorithmus Dieser Algorithmus wird (?) in Java's SDK verwendet. Fowler/Noll/Vo Algorithmus FNV ist ein relativ neuer und guter Hash-Algorithmus. Er wurde von Phong Vo, Glenn Fowler entwickelt und von Landon Curt Noll verbessert. FNV hashes are designed to be fast while maintaining a low collision rate. The FNV speed allows one to quickly hash lots of data while maintaining a reasonable collision rate. Mehr Information zum Algorithmus sowie eine Implementation in C ist im WWW unter folgender Adresse verfügbar: http://www.isthe.com/chongo/tech/comp/fnv/index.html. Bob Jenkins Algorithmus Dies ist ein sehr schneller und guter Hash-Algorithmus. Er kommt ohne Multiplikation oder Modulo/Divison aus. Der berechneter Hash-Wert kann mittels Bitweisem UND auf einen kleineren Wertebereich eingeschränkt werden. Mehr Information zum Algorithmus sowie eine Implementation in C ist im WWW unter folgenden Adressen verfügbar: http://burtleburtle.net/bob/hash/evahash.html und http://burtleburtle.net/bob/hash/index.html#lookup (Dateien lookupa.h und lookupa.c) Im folgenden wird dieser Algorihmus NEW genannt. Vergleich der Algorithmen Qualität der Hash-Funktionen Für den Vergleich wurde jede Hash-Funktion mit einem Testset von ungefähr 4,3 Millionen unterschiedlichen Wörtern "gefüttert" (keine binären Daten). Dabei wurden die Kollisionen bei einer n-Zellen großen Hash-Tabelle aufgezeichnet (wobei n eine 2er Potenz ist) und daraus die Standard-Abweichung, Anzahl der leeren Zellen sowie die maximale Kollisionsrate (einer Zelle) errechnet. Gerade Letzteres ist wichtig zur besseren Abschätzung eines realen Worstcases. Es wurde nicht beachtet, daß durch die Anwendung von Modulo mit einer 2er Potenz zur Einschränkung des Wertebereiches eine Verschlechterung der Hash-Funktion entstehen kann (bei den Hash-Funktionen wo Modulo angewandt wurde). Oft wird eine Primzahl bei einer Modulo Operation verwendet. Standard-Abweichung Deutlich lässt sich anhand der Diagramme erkennen, daß die besten Hash-Funktionen, der Fowler/Noll/Vo (FNV) sowie der Bob Jenkiens (NEW) Algorithmus sind. Für größere n jedoch kommt der SDBM Algorithmus sehr nahe an beide heran. Anzahl leerer Zellen Auch bei diesem Test erkennt man deutlich, daß die Algorithmen FNV, NEW sowie SDBM am besten abschneiden. Maximale Kollisionsrate Auch in diesem Test schneiden SDBM, FNV und NEW sehr gut ab. Performance der Hash-Funktionen Getestet wurde die Berechnung von Wörter der Länge 1 bis 24. Der SDBM Algorithmus ist sehr schnell, NEW liegt im Mittelfeld, FNV bildet das Schlusslicht! Fazit Besonders positiv aufgefallen sind folgende drei Algorithmen: FNV NEW SDBM Wobei FNV von der Qualität der Hash-Werte am besten abschneidet (gerade für kleinere n), jedoch wegen der Performance (FNV ist der langsamste Algorithmus) ausscheidet. Der NEW Algorithmus unterscheidet sich in der Qualität der Hash-Werte kaum vom FNV Algorithmus, jedoch ist er fast doppelt so schnell. Der SDBM Algorithmus ist mehr als 2.5 mal so schnell wie NEW und ist in der Qualität nicht wesentlich schlechter als dieser. Besonders für große n sind alle drei Algorithmen in der Qualität nahezu gleich. guugelhupf/doc/images/ 40755 1751 0 0 7515105216 14301 5ustar mneumannwheelguugelhupf/doc/images/hashing/ 40755 1751 0 0 7515105216 15722 5ustar mneumannwheelguugelhupf/doc/images/hashing/Time0.png100644 1751 0 17640 7515105045 17533 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿÿÿÿÿÿÿÿ·±OÅPtRNS@æØf0IDATxœí]As¤8––‚L3¾AÄîÿÈ zÃW6 {jny0öô-®ìé[F¸{˜c&z¼·:TÏFþÛÕ{ È»ÞWiJ Jñññ$= !1¶$xì‚E)~ŸàK $2·h›4uŒ8™‰ ‹œÔ®gMG¦»s‹–ŒL¶©ú?Ðwíx»Î# Ù&mÿ>h]Ó&- O¤(>I aGrAX„¸-N"Ú)4’ÃÓ4Îr*ØiËØ“–œ-Ø%ák²“qÃíÙvÿÈÒ?>oä®HbAXœ©Ønó K³q<µ\±:éÍá°ei—M  ù¯y~ÜwÄÀË`ùñ[¾ÝË«ž‘ùMV=>‘üÌt‘äDWb‡¸ÜÊ4 rÜ5¥®üŠÛÿ‚Ÿoþ'ªR”÷hzUÀQ¥Ÿ.ž“kF¾ÙõA¾apö/#:ºpË8SzýŸ´ $CžˆCbÜÒD)]Ói"—l`ÐômÏà¥âi›—Úö褯Ÿ ÛŸDzf­ä7uÖúd'tÍ e ¦6N7)kô#cói*6ÍÔŸSt¥6'ÒæŽ¡ø¸Ô’YSóâÖ€`֚ˉ²´ŒÌú’B 'WÙ¯õÔb§ü$ÉNS“íEèرƧBȚώ:¹©>ÜœÔ'luY„îlíGÔ9u·S­Ùöå„E–)ßò=Û>ÞïõL$ŸfÅæSZh`wpia!{/Âb»‡K»ù’c޾Í>ÿ~<n– žÀÇÜUâ—{üù_¾Áƒ¬ò·Ï™TŒªÜQ™üé0ù• ™3ö´gOùñøs={˜šÚŸêåhÉœŸÉˆÌK`d;¬G«öèb.êß«]cè;QUóoŗНãœàéf-âÏé\ñÕÎPeTÙ"òá>€-{ѸPt!ƒ‰šß"ÏÛ*.$×E˜PÁÜ3M-ñ8ЃÄážÆ‰5ŸXWþ&"m|Ÿüœ_bîaûisÇLcäKÌôúâ{Áœým/TŽW@Ïœ?rüNln«è8`Áøm¤ ù‰ZŸXK6¶EP#!.xÅ<Žäè‘Ù”éM5¼8‚]=€&M£†*t½g{#¬b›-Cn ‡'9¬©~õïýõ3ß~­‡…š(«ÞµÜnWùX¢:ÂÑÍ¢Ô~Õ V£À¶[«!P oD5Ö ~Êç4ò•¦á\¨ƒLþ¯J\œ6¿ã—1+ŒpÏõ²*¾ç‰7ƒ+Ö¯€VÇXõJ¨Œ-òfèûËo—ÂÚKñņs@ „kaíÍ@ 0Óg½è1@¸$Ö^¼±Î[œ.M ó¾67_â¡ëfç9|ö¢ØlV­Ýÿ­+Ön“e¢´s×îê!6®KÐ\B;xõ![m¶]³vB7æ¦Ç¼ÂÓ!عi¯¥^™àƵ¢Àåu®Ì¥8Y†sòí¼ædö o·Cn ì7»¼ 8ýto fŠå¨1v e(êR—&Årâ &jïk0i´ÃmOœ@–tËäY‡JÌs®ÉpÑŽ-˜/ܺœk °;iqcí.1®È£¦Ûô5)À4íL‚S_~Líw7è¯àŠÈÚƒåü–ú"Á›9é†ò†â< †çHèÌ ”¶øÙµ“šJ õV¾JŸ(:A¹íŸÒ öº¶$WÒÙ) »‚æ¶ù2[ÆÐìNÒ q’s{×EsATó  |íE1†Ñ1£’>G¼bXNZ oûU9!œë ” Õ¼cèY´ œ'g Û Ãz[è¤rœ^â08ÃA®¥]€÷²15ÐnÐxd„–d %d¶ÍÕœ"°—!׎‘ C;¹‹ô›ª,Û>³i“ · äJù8™’ºÍóoYöÙNð6·¿ ‰K"‰š6%Qù“±»»š`˜´øáœÛ\N mú!$µ}¸{îî³,»Ê?¿•vv‚w¯å6IþüKòIÄû%5Z¦î0è4H±Òèüñpþ– R‚Ë Ì©Á6Yµu€V^n>aøUS°«¿d›?3\šW„³{Aü›[Aå/ç¥0:£rAࢫƵ-Ï ™2ìíËp¦EçÀJV9c>t0Ô©ß®¤Y î¹È÷‡ÁjйjvU«rW“ÊógQj=ÿ´Öþøòta>V8‘28¬§žz.„}æõ7ñüíx|©K^®ÌORüfÙ‰¡I³LÒy|[™­¤–]ú†6‘@/@©Ì$’”A§š¹„ñĹö¸,I–5·KÃY€• ­7×n¥1y„¶G®ydÞ±HÛi‰êaQµar’‹è݉Gx)«™UÖ7Þ„ÇQ,W¶Šaé³uü\7» è à–΄šÊÙn @Óf»iNýkÄâæÂf°N$DzfÆ©Oóööö[©Ç¶s›HìS÷ýÉ5áÓÝB˜·çó™ ãa «‚&pÔ­$µ ^¬ÐÈŒT … L)Rá`‚¢,÷C§‡ú}É#–x{¶ïïè3íð¦·°î£ ‡®ó6AäJÐq#8ï>veˆÌ¡´är¸'‚ÃCåwÆÊiÜ8®NyÝ Ý8NFVfo5]™$ˆ{õiÌÌÐU Ö4t‚æfĹ;˜‡«QËeF«[sUŠã\Þ\Øóáçì#|X¬Þt17®’`¼Èô.à–Ъ@}A}A}A}A}A}A}A}A}A}A}A}A}A}A}A}A}ñ^F¼9Ìe-P´àùî:<*‚ñÚo1#‚SA}A}A}A}A}A}A}A}A}A}A}A}A}A}A}A}A}Á«ÿÖNpå!8½ 1DÐDÐDÐDÐDÐDмZ8…NÃ{¸Å«b ±^‚jž õÞbµ~Ój –‹Õ¬ñ L­QAc5}¦µÀXÅi…Íå~Öu‹-Kt­Œ`{a˜•l¯!¶&‚@oÍSë"g«!Ø5¡÷Jv¯K´‚=ó¡¯`ï²N+ Ø?üâ‡ÖNZ’ Ó¢]U'ú"oC8, ¼þ¢.‘|“:ÅZÆ—î,•IR׈˱fì×—ë*,@0u´>¹äãÕ :ßÝDJxe‚Ž+Ûâz^×VÐq5JÀ¹¾ :Ü] $úÚ…WTÐ%R{±Ö«L]")íô•‚®A0ËÒl°hI`íËj²× è’9Ô:íU–®¢ààý…õP;E»©]çÍ•¸(ÁÍÀÍuYú’m+êpZ|ôRÓ1Ni.F°×úŽk_ˆ eÉÖ\×2žŸ eE\«œŸ “×ç¾îtp\œ ‰¹ Ú½Õ¨…!!(ݪþì”vîKÏIÐÅúF¯ =«‚ÃÖ7~qèùY`ÂâÕót±>6‰ßLg.ût̤à¼eŸŽ™ì/ûÙGP-f= Á´Óúd¡'K½‘˧ÏH0í>IçšÞØSA´ÕzƒI$“ÁÉ!ì}.Ö¶§ ó1QÁ~–å„G¡º2ß[Üæ'nh2‡vjYuO‚i{—²¸ÉÚU¿–KÏO'˜e¶†›óºóVÈœ«òoÑ 5ZÁVõ$@oZž-W‚OpaDLÂWÁ&=6â¶æòô ð´kCO´eÞ.~I‹N É”`º‚r[-?… ïk/T¿Óo®¾}ƒ `ÔVP'¨€œ¤ µì+õ«I•òØ4· Ô TN h÷ýª³ «]‚]¤ìOÐÖ)¤Ÿð59±î[âÑ5·XzTB~Dîn{€•ž?îîv¬háÔ `;Z©©½ãvxöFÉ—¼öÞ¾¢ckPÃ]Ú»ü½pXZcU :nqÑ¢6ú[åÛ5ê¶]x»Fl‹Ú6ÏYµü§;ÁŽÌáãµÊ*ü¯Q;|Á¥}GlË'Ëe“`àª^’IR%5XÊwûå »jÛì~Á=|8Ô€(Nzí¯IMÞÜ›â (jB»ì^ÐÝ>8´ºUà1’”¢&·vOÔp5ÙÃU[©ÚOy–eÿ|ÈÜêwפÃXY㿊Öõ™evÔ¨É[YQß‘< ½=ß jŸßް†4¬ÖëFð’z½+·‚Î’º«hbá,ÂwAEMd‚S.í ·ŒÉµ­aŸ|@›!îóǷ߸‚0Û v»‚Ͳ~ƒ:*€¢ÒôkYD·¨=Šš %II½/I õ:4©Ý0 ÁÚ! ’²ì‡OeÅv÷ZnwZ¸DÚU{.P¬?j½¾e9É-Eª¢\‘ÒÑ&X9¶›©%æ-"ÎI˱'pÏÃ<.@&Amûüp_€&lQ¯ìÁÔkÂà2½Õ²ùôCM°ô$íõl}+saô%µóùñåÇóÑBʉÚAë¸õ½AÖ¤@¯…Z/O‚ÕIe/5©Ô¬%6ðFQ{ÜzGÅž¶¹ ¤3©¬6_S“Û‰°Ü´µ³Tl’]ÁòãF*ÿ²Ÿ‡ZÁL¿¹à®$NAQl÷‡ ©ƒ/³n‚fXŸÌVí Ï>ßü~˜›TÁQ±í°‡¯E-Ï¡¤ôDÎÚXõÔ­OµÁmˆ…ðOjÿøV ƒZóå²ü, *jUÅJ§ *6Y‰>A>=;³ÌÚ€ºAQ<{fÙIÕï@ìg¬RVR»6A©ti}¸?1Y®AÕYë…~ùB¥›?Zÿ>¿Á3.È•Hõj¢NwQùÜNÔ"‡­;ö–•ë¯K7VR“6è¦×UÂzÐÿùp/¶ÿñÁ6Ë–]œZ¢q m‘0Ò\ÚFÐ6&‹©aÔ(Ò¶Úž2µ¨±xTÅTs>Èm4hºúoÚô-?7ͺñcΣ&M5ö©r+AãZgÕϭ¶[{¸$hߪ3Jš1n£zK >Xe93Uʉ:^'E„¬ß–fцQ­­‘ ðÎY±ýewá¨ÙòœpàZn<¥\+~x’Ž*/? o˜zc§A’츅`&Zž–Bu =ÓŽ˜€ÎLËP0èvÅ4r‰¹tM‘ßÛ›ôZõ@™}\l¿ºØâ™±¼kí†îÄX:Í\;ËŸØoªxì:µIÐ7™aǓз9NJ \·_U>käÀ[þ­l)JŽ­à¨å˜pló¶3¾ÖÇPF}ðXu$+¿óù+LúÁ% Àâíßm°+8Â&¶Þ Ï©ñz°´®ŠÕxÎ#bŠ"Rlj’ÐùAU”úðKÞU`ŸÏÒÕmpÁ1äQ»¼ƒžC;¶?ó@ —™qŠOŠwAD‡ª©³8„[lCGñ8OWpØ)BŽìûF§GÐucã²’·¢¶zˆWÚ›c¯¯.Û6ÓŠ‡Üµi$Pr¨´P˜XU‘þ]Æ«D‰±,7Çèæ×é”hìûhЄ äeÉìii ‡2nGKKý1C@Œÿ-R]œfquñµve\ÞPÛqk:Ð>® @˜1k\ ŒBôký+"B/àƒWü3â 8$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž0ÔŠ™þî=Á¦Z8 8†Zø% 8 Tz‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è ÐÜü«9€cÐÞ‰†€´šÃXPè Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž@µâö.‚#@-Ε¦IÀ1·v¡$ œ ²ÀÑØdÕ7,ÉÇ`£þ$È 0?C@NèŒM–’Nж‘%_« l F¶±aYº±áX‡PîZ\·€í] ¢Øk”{:¨ ìŠêõ‚ìDw¹§ƒìÀ&ëζ:H@ࡤ*ã’€ª×$ 0½tÔ/HÀB½”‘€£!Ë=é¯Èð€XMnÆ—{:¾s¡ÊMÙlòïf5‡þTÆ_îéhø½,Fn2«Óñ] ([é,i}e ö+§Œ”n§9à{p¶rOÇw#`–¦l®l«ãƒ XvOA8e3 $Iýåƒ ˜^¢Øúíª/[@ÏfšA²õ¾ ý›i Û‚xe¸Â0es5Ó4$Éng=ðœË]‘––àÖȶ:>œ€36Ó„t•hF¶Õñ±Ìü›iZ1×iu:>†€nƒìT–Å/]V§ã£è2ˆ`¨X0lu:>‚€®ƒº€E´6°Àq?~¿ªqŽ#t Hô¬:ÎþÞµ€^ÅZ] ·ø™Šw,`æcuL³:Í[w* PÏ+Û&^V§ã} (Ë=Ù;eŽõtV<¬NÇ;P=ÒðÃÈz¶ïKÀ̧ÜS˜)ë–x?¶ßq™„™rn…õ ØýŽËhŒö’𜥙UG0¿~ë°ç¨Ú6I’ í\¬[Àþw\ ;ÌãÛ¹.X§€³uO™óüö·^g)÷Ïfš V) g¹§püjÁúô.÷$.P_X±g+÷ ¬»–|ëp³f¯ +0õɶe]\ÊßÓN•è]Ø«Ðï¹F‚—¥I^Äß«±J§(§ž:âõìŒJ÷²`¯€Ë¬æ¹w<ôÉ«¤WÀ%ÞÑ¿Ò9\eæ^¾»^ ¼òjÙØî)§á*—Dë”Ë–©È½é`,i]îÃU.K9±¨€nHõü°ˆÕiL'ü(,' ãÒ QÃ.ƒ²ª—(jý0e.H1ÛÊ1µ×®a˓镅O§et@ZHf©au¡laL¡ú.£Ëfa·¤Ð˜=ÛöŠ&µ¶ÀðÄL\U@×÷ü—lەú„ª,ª'Π€Mù®%`ÊF¼ð"hÖC¶kÒs›eº¢uE ¡eß•t8ßʹšh•VbÉÉG‹²Z2 ·Í]G@×÷ükÁ§{¡Þ±þ°!È«—×}buì ‡º™…F¸`Ú——pô{~¢ä³B Dx-ÃwÉ]b 7®UûrÒŠ­p¡áT¦¤§Z MÌË ˜Ž|Ï/±×¼Â®© ë"È=z¾s‡ºPÚ-”<Â:qvQ LÝ_×PÍ KÍ€ß__`ؙÌ ¯Ã#,P¦sª~ ¥B›h×°@×i¨ ´ªý†öqqèt2Š(³\·„,ìRîtÁÒDÛæÃy; •ÈŸ}Í a‘öªÏ¡fD šÊõuYÁLÑ!ûR†oØÍ¶úñ¼¦.ÓP9ìž×dÔ¢å¤!šÔDC£ìá »aЩ{Êx-£ÏmÝÙÿ±hŠVí®­«ðÄN7*¼Íµ[öóa[îYë¾:Á|:uOÁkËšÕ ´œ&ê7R´ýátØ«}¥h•P¥Õe(Úíùüí>šµ LÙp÷”j¯×a}¿ ·šf£²¯ž Dÿ¤\ìQ6Â*ŠŠáƒ‡‡,ûüööûÑX£z—S¥§7ÓôÊMo´Âª‘z'•€šÃl´,:Q‰vŠö7Ñrª¬L³í,á¥bŸÿ ¢IÅ7°Uél`¿E-Û®WKkl²?‹kMÕä5y­:?D¸2ªz§èÀIž °wg5DSß¶"ø«E´*WJ¡jëBÑP§ì-Ï¿ä}{{;j8ˆ8Ý¢ÝØÕjï „NIO8Ó/\Õ«Öùñ*³'¾Öòªe[-|w§eá××Z4=ÿŠVU’¹¡³X—­ │2'Í®–eWiiX,AXYþ¡<‘.‚ÅÒ°ua³@%“Öl µÌ…¡Ù6­DÓÓËøZ4!(áǦb ¡ºÂÝ¢ÙÕ²ìêîüÈ>UE”^޵:?T㬫` £¹‰¢5cFÅxþÂg]¨ë2…:°éȶ.·M¶Á“îí›lÃf]O¥h·R(¬ºŠ+Gëš ÎŽ~¿9HvîOƒ„ë¶ÍžZÖ'½‡pÖPŒ™E”Q\Íg].pöÝÞoý3df(áDUØj 2ìðºp°û=¿:£ê=ë.äe‘¶Í÷ÇÃæaÝBuÁMÀ¾÷›åL!Iù(hØêTK ê‰ç¢ ÞoŽ=B¹T†‹¡WÀî÷›»FÔvYÖ<­*RQ[<ýãoúßã‹<´j¡ºÐ/`×ûÍÝ#jkEó8„¥ýT<«ýÂ5ƒ&z.2ì›_R¯ XCjÙ¶´º¤{Dm)šÌžR4éŠ|É•hÊÁaÎ˹>ÜL´ž¼$¬s-ˆ³W­ylTíõ—L)v†ÍϲUµ®—qÍ®Š2ð“*÷’俵æö§ßÿõ…5D³Y׋*7uÑPpþèb£@Yú¶¢‰õ«(ú+¡ Å9V¨ÙæD\úÑdÓJŠö³ì^”é;MGSÀ¨!`UÈëFç‡)š.Td¦;æ3…GŸx.Bð•·,° kOs?,Åw­Öè@¸ 4ƒæa݈¹¾În<¼ßGZJf8îkq´3pÇ—×lûëp¬]¦æÚÉa÷—]%‚.¦! }¿ñ[#Ž–PÄ¢¨ë2û~n¨+Ò„bÂFÚÐÒ.­ñcõ$ ý:ÄáÆé qìû™“€u¤ˆw‰©_kMϸnc} ÒîudhfêY~K @X#"Ýi4}Nn6VDÐäˆ"®T_QÀ˜$‚0*M1*5í>n¶pVˆ&G;ãØHŒã¿bÃ9&§Cïó  úß}oaôsËýp”6r ¤uq7~Ý JÀú£áþÃTþEPß9DWFØ{Ì+¨z¿Ð}<]J×bF¬º ¹ßPÁ<`üî˜î(§&ˆb뫂 ]Ã^RìÜu°,Î#î¢H9ZU 8teå¸þHÞ|®ÒÇóĬu²7ÄoŠ ë‰9 ÊÑNAð2ò›Çý–Šã¾XàÃØŒã²úæø!O#¯Ë‡Þ]jk-¶®ˆ; ñš@©;sýdM’\&ø¸Ê? β‰%%Ìò@*aÊšZhS;ºà‰Îi£ ²+Ed šal6ZSÊØ›-jŽÕz_=:ãæu¾zÉ+£c…ÁŒ•ʱàl»øWÿ ó ÝóU:’Zê«EÈuY¶Är£/ö@ÆŠ=&#¿Z·ÄýXñ×ÝÈu¶þŒ•u#-¼]âÇüjøÖ¿Ž-ÑD>Ý!/š·%þf²ve~¶ÛôdLYZª õщÍÄáJÆb2:Ku"aí±™Ž%ì L&–ÁäH%É1²)“À$þɦwô¨ßcž5_6ãt3ó‚…_ÿ«ƒOd“•Ëð*KÅ‚à’fƒ·±‹c)+¢©Àx Ì8œá2º}K12t»Ó(°Z¸À”–-Ðëi\â¢:‘-bØ•ûv†n'UŒvX,] ‹ùntôgb”Yzç3,ôâ2‚gpúˆçX‰õ’ ìœØ} ³9.[`âzØùz=ÉKüäý²þºh›ÒÜÄ-T`ˆJky,‹æÐ¼& Lü®îévŒ«M.P•÷ƒ? 7÷¢·Ö T¼’ÇK±D–›þ“nB©jÛl{Ï^] ÅN”·™§ajK²M[¶Ð°Ó/âå3è VO•8 ”êÈ©R qnóÓoÇŸaq™¦êŽ$ Üjg#ò¼}Èþ•Å]^öœAçÏ™ñcœûG°&gVg&x=÷È«á™s= *þéa1 ˜ëžVsÔÄ<†¡ìôºÍ@…pù0ä L gà•C¿œ`8½¯japÿÝÜË fÁriPšøÅ[ÈÉͤ¸ÉBÅr&{Ysä˜A—f,*NÜ=ަi|0ãg ^…"ʤ²¹s¦^¯2GrQ¼…ùÑ cq»®–.0z¼»> gº X^1¡wd £Zº@ËxlÓÙ²ÌÝóâvýÜk±lq¯6,„jñÞÆ`™;s°kÙ5>ù18`×û% lÌp±{^¬$Ö2®7ÕÒ U‹›á†mëá\£f¸k ̲¹ƒþÕ¼ÏxçU°¯ –&krÏ Àêx …]Ÿ—+ ùŽ)©á«;¾<EÂ,°R….V bÔsãO£Ûá|×`S,i5¼<.mˆO‹]“ôsxl–»N±â%»Ñ›u#ªc¿Í!å ¬àu»Ø5Œ' ¨|ëã¢>¶V³ôóËK»×ä÷…KÜ>‹”SêKñ¸ó’6þc™—¾}À§? _âÍA˜ =h"RëþÒ2 êjÓ ÿŠ’S¶Ÿû«Sëösw±Ÿµ™„#pqÅñÃOÇã¿w2è.P& ÀÍ/H ͱŸ ñóðoãêQžfvLs$ó0“'h˜š3ûãypª%‘µHèLíÈ`<~ÆW.DaP^þfg?%{æi±ÙVUòÙ‡¹;IÌ.sõÌšH³.爈9¸3cH—åy3xâ™üi¹±£¡>rчr-ÛNh©ÄO¨•7i‰É96â½àœ%orXd3˜Æcs0>+ò¢Õå²bU«+Üpçžbi*¾Ÿ&4ªV(YÆS½CàÌ7.¤ÉA&ÿª† - ßÃXäAÙrLÝŽ ôêi^5,ÛsêŸÉ‘½þ´ÇVšDŽËÛ­Í¢ \NTÌÞ@‹Mç‚Μ "8Dp.ˆà\Á¹ ‚ãÐ=_,‚-ÿ*ä„`÷HV$«Î3q왲Ážñ§8ö ¦ÆA°QT#d­ˆ‚`¢ Ø2Õ ‚}¢ Xõœ‹`ïÌòöNˆ`)EhG «È ö?ÜA0Á¢ólûÜtYÛ£™ùóÕöE ,‹îSë˜Íµ>Á²ÿi˜õ Vý§W'ºiw¡ñÕ †nÚİ:Á–`Õð]`Ô÷¡§µ ¶D <ê34Á5m9.U¤àL«ŽË¥ n­'Úò˜ÜŒŽž;Ÿá[› Cì™8¿2A)t?³¸.ÁãQšà®êŒiVV°’¶µÿ dG':(X€ ¾;»' =ŽaÞŒ1Áîg#Ö%X)ìɲ*AS3z6j[•`Ť >]â Óîú ]“`ÅTCÜ·ߊµ€I¬#M¼¬×ɬIPF‚…0ÁÞÍW#ØtÉlaÏ€ø:0åÝÜ cûÎg˜×"èU‹ø”¯ïÒV% wéí=Z‰ êO‚LÝ9×!¨ÄHæ±÷ ¯DP X0¹ láVªv.Û\1äO$Á^Äî]çe ‚ŸO'“†Í‡‹¾Ì+,~ó®+¬q{‚¥M/ùš<Ç{s‚¥‹>°^ÀÍ :ü’—r`)¤uo;wI:´$ÄZ xI¶¿.MuS‚MŒ…×5aÏÿWöW‘› H¥Ó…üô…¸Á4}ø¢Óªb<ž‡­»ÁR.*À¥¤û^ âFK{Ð J~˦܄`êŒinàöë»W2x}‚\UÖû_‘_ÓRWvã!+E"ÂÔ’%ÅšS¹ {púÇ |Mà^=)F•³4Á –øÀÄÉ>%+/,3¶ß_{E<~)½DzNlê{ÒO?¡GòÈétpx‰?"±ûZ¿°MýÏ‘ì`ÞŒÂéôg/ñ¯×%N^âŸ*qÿþþúM*f*¸ùtð?ñðñÓXáÚ ¯µ­å:åîa$™—§A3–šG:¿tíñ$Â÷ žëå§3%‘Hq¹l„Üï È­>6Hˆ ûý~—ãJ?¹>†—,s¦ýˆæ³ÀFzòÀê_°j ,‹ƒNq¹a—Æétª ïÉ ŒmƬÄÿæ÷Šççç§ ­K˜#ìvkØÀ¢M }yy9g`ƒ`‚èÉBªëú"óàæuz3»œ[3)ÁÞqéeËÒŒ‡|âR+ÊÖ}ƒ• ³×$Lø8¹=^·}I'Ûz4¯;'G àÃÆ¥ ÎYhÓ=ÂhŽqroao ÂHtX÷×\lÆå’_øŽ©Ê£Þ3 P原²*Ý–¡ÜsS½“K•©µÍ*ŸÖN37_ºŒë•Õ˜^C­!èžaNž›±χ /¤9ºHs‰s®\&.‚ˆŽÜö !SûÚ<ºaNÙ÷U¡ø1³¾_dˆ£Åìœ "8Dp.ˆà\Á¹ ‚sAç‚Μ "8Dp.ˆà\Á¹ˆ”`3¢+Á”΃~Ê)Z‚͈i¬Íuo…`ô]Àyl½ü•Nh##Ø< © 63#"%Ø R‚ÍÄ‘H 6ëtFJ°Y¡ŽNCeRDp¬¥X#%Ø$ã$h!N‚Ö¿8 Zë)ÇIÐZI4N‚â$X5I"8öD^"8Q´'bGIÐ^÷>J‚öŠÏQ´§íGI°Šœ`=Áè/± :ëÊÁ p¾'‚ÓÐÊÄ“Ùá ˜‰¾jôú ö¢Ù1³Æ¢žËG'zú ÝàYŠËaQwÜ“0Ö‰Þº_ÆÚ41.‚:–aïÌý{T‘U[ê±8 êEÙÓïGåoâ"Øt žãT°Ùù”i£"ˆª©O˜1lÝš5&‚Zµ$ÖgšÌÞ±öîÀl6Õ³÷Žˆ`³¹-;GùDŽ!µ‹ô‘!Á|°w߈ Áö]”¼ÈŽ·ô2Íüñ”@‚—(g`6õâé\nöïÕ›xÚÓÏËÍûèªzQÈ-´7;=  AÕ{^€—Ic´ÁÆqß0ó.‚h‚›pèXªýAáeJöÞœˆ… 4A$(¼L„˜ÒñCK\˜±”^z{hGBÐÚä¶qpö[eR0„Aå x¹Øu$‚MC »G7wKoˆ#ÙOîÐQtf]~ g±ÕçQÛ&èVâ8–sI­X†Ú툢 xNü)ðÍÅÝ"8‚MRxw¿´(Vø*÷ùö*qm|M:Á_ß¹u$&‚ˆn;Aiy.¥»§àêý)¡þNî«4.º€ØÆÎÝÉnm‚ž€‰¨Ä…sdm‚¥vÒòÂî~õêÈÚ›Ý xI`·o7ÇÊË÷ýÖ¯Äk4*ZÏ¯Ž¬MÐßðëÙ«#k¬t¢À×mý¿^Y› ƒ„}­koÚ5 Z>ií¶ïêoô¹„=±¿ŸýQ×#èð+øø?ï /ÛjS« oüyúÆR¿Ž¬y‰# nÏ, öo^‹`i»@IPA‡; ÁGq8 v ^‡ ûìÜ„yi²ÿèg]…`éôÉÛM™ ÷×]ƒ ËO¶r;<Þ ÑÜž Ì)ª¬÷¼$âvS8ž÷W'˜‚ùUÖ]mÏâßî°_™ Òká·–lW¬mƒHïä*à%ÙýpNY âvSœÐöP9 xIöÏìâw#‚zº¢KO]Ð{Ü_Ê~¯FPwÜ;½§•›[¸½£ëúÒů› ™£Ù$¾”ƒ‰Ÿ)Í—ñ“*RÝÓ ‹îÊ~(’õ·öXÿÓ§&|˜údæË;©`türôx‰Ä ç—[G²ÌÍá§oýðé¥OPïˆz:…‰?OükøÃá?»ÿ²ÿ·¡­Y5šnþëtÆm)½DR«Ø:®Æ4<üÒI§'1 YOBÛE– ³D YW¢Ù9Ý4üPFߘNOk6L×,xæåm9Å{äO"çA"ï<ůI¹¸V0¾ç9 Â:»L'º¿|õÑä01iZÔbêB‡ 3¦žäÄD‡dVZ“@øžw\•¨m}Ó¸D¨6œ'·k¬Šò¬Üâ9ãÜ:æ °ßï!•_ž‰ØS¼šÙ¾n³ˆ*ÜïiŽ´2ˆß™3š¥o!erë+ˆù¯0ùÀN”ÒÍ0c~ ÂHÀn¤¬’‰Bþ|³‘€]ÐPȵ×`° Æ-x ¶¤";a P (k0 8f±1K@Æü=½HÀ.4keø 5ø 8Íjw¾Š æ¡Í’€í0Mˆ[ƒË0' Ø Ó„±“€#Ñ å[k0 ØŠÆØ^ƒIÀ6XظÀöL¶Á2@)`w &[``ãÅmH›’€-° PO¬ìt$`Û› jp²ß3,IÀ¶ˆ5˜Û½ f#ô{ïe'}ØظÀoÐoöïIÀ!8h\ lƒ7û$àl\ ¶Á›÷á#K$  ǽÌZCuŽj!ˆ 0)€œP­mj0óý€t`ïza45¸hù èÀÞ·F (\à¥ÓI@ζ+F@Ä´5!$ ‹¶&]`g &m¸1L3©¨§“€6Ú fFË\´~†lÐD£ ÄÜn€$ …–»8+ˆ!ûQv` &5Ja€VŸ½ Yj°?Kÿ~çH—%«, õW1Xƒ» pp)ø<¿ Kv²FŒê ¦Ë= Ìîö9¡Qe½-tB¸ÀÞL>PBhTUÍÛæ¦m7PƒI@èŽ%±5˜¤nýµ 0AL¯’€ õsŸ€+LJÕàn${õAÌ@ &Cý¬n¿áL óž`-š¤ bz  ô³ \`Ùk€w/` Ÿe€û$ùá<`€÷. wœf·Ûî‡jð êg{À=ûï?Õàû°E?Û²äë` ¾k[ôsbÀíóW˜–Zøyܱ€þ Àò€B¿çoÏå€Þ«€G2ÔÏêEÿ¾Öõ` ¾WK\V'8luc1\§è÷¡|·ÍÊb\ý.ô4`€w* T_{I¡Ð Ôïñïu}2À»0x:6#qh/Õù·ªsû0áN•¦PQ¿Ë³Õà»åƒ5*ïÌe+0•hý†ƒ@vwâ¢Ø}PygŒ€F¿bÁïJ@)_ÕzN ˆú};3\€qØïI@|Ü·C>V(…~øl5Þ襃MÈx”7Œ©íØà LñÑÂ÷ÁîîB@qßQbÛÛq¾0)­TöÍ~ÿq°¾ S°¾°ëYê§Z?´ÖÍþ<â»PŠW±ÏUG†¢I‚~gå,“Ý¡åáÖßµ€©ŠBÄ}Ç—Öçõ]óK¤~ð!!žnE¾Ce‹‘6·j­÷ˆÂ¤’]²ßÖg¬¾ÉûÃèoû,ACóN*Wµæ´ík¿{Jv¸@`ò¾¸âÛÞ¼€&B¤ú<ò`šÝªí£…N$"ZùËõ¾‚!¤«äû©ŽÎM4¢Íí¨´Í/Ãe$¶Ï_ëê¿–Éøº«³€-7£uIÕŽö¦O|9uE|ZÀýŽ éêo//¯ëúoWË×/`Ûߟ¿Ê#éáÓáÇ…5¾xšZ_B´tU{s÷?€tøM¢àºþgáýØPœ1G´ÍÒÿùpŠ¿B|ÜÛ€#?VÏcþ²ÿÛ¾ò~ìúXË;¿ ä*¼#ëúÞ‘ýŸöî‘×ÁM<~ö}à4¹Â#ŸöûÿÊóJ7Ãda·ñ?×vÄÿgþnÜœñlJÿH¸+|=)^ îS&~íoÐ'Ü’‡·Há¥h0 ®VË—…Gf‡%¼¥„‰úË‚²Û,ÐßLºíȘOù™p+< èG °(&éB¦_2s ó2`¬jò\ v¹” ßpy㣷“çêÏC]¢÷c`™cƒæN¢‘-#Û ´Ê2a‚ÿ‰<Ïä[ìpUÜ¿É#ÂLÀ°Ënr–ë&ï˜Ùr%*Ö5Gaa(sˆ’šVJuTÊ÷vôÙ‰òŸlg”£̘\“ªé”¶Ca»5íjYåâ0@E®m5µœÈ‘¿E9“ã øû¹ìŽæMtÊyú]ËkÉñÄ;{¥€ÓÊ9ª¦<ãæ‡s®>Ï¥"êª@ì’éØÅ‡­´€2œžPÎ8w8JË1T¦úÜeH%cyñõ"ÝJL[ È“M/GY Fq¹êùÇ7ª¯>Ïä²_r0!ËÃÁi??¹œ!$÷§…Ê9/TÎe¡r–ðìcðü Z¡¶L²‘´šÊÁðéŒ ŠV8—#íŠ<ÖuÍ0ôPqÐÄ 2Ù±=3q W„õ¹¹4ψ'9Ù!ã\éˆzæl|ö‚8«øM)Q#.Z^TêøNea|‹dþš*!X Î¥ßÉñç  ×…aÒñ@>±€¶@.ãIõ™ëƒJm0Ã',gù` žgá|Ÿ+A>@ Ü/–º‡u‚™…j‘Rn%`fæSNŒßÔ3YÆT 3 Û¤† «üÂu+YG–ç&C÷ãɈéLŠßt? ˜«Ÿ1„ ùøî8݇+ý«¨Tö f-á’áÜT( Ì3=a{bü¦-C,õ[ ÆâÐ# «à…ëÑòL]þêáÜD¼ÿ pp-Y#þIEND®B`‚guugelhupf/doc/images/hashing/Time1.png100644 1751 0 12157 7515105045 17532 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿÿÿüJ7ŒtRNS@æØfIDATxœíÍÒ£ †¥b²ÆÅÜG¦ìÊš©ö²ÉÞªtåþ/aü9*xP19O§ùˆ1äõ!ËŽ„>*ñ7aG ˆmÈ…ßzU_zžÉe™_rÇy’²äb-ñÎ)Ž´î—ò>‘\óay¿ËUäk&¹îÃú¦^kÕÝêÏíý¹Ü$žÏ±Þ¬”+Öµàe~ðüÕëë:•–æšâ"Ü|§OfL¾¸~Eú&ITåä¿RHþ?-G…,ʉ—÷[ÈÖoªWÕŒêfA,áy¦¾©ù­(<«pžÉå±·"jrß̸*…J«©é”Ó^Ê®fæúQÑEt,» Ȱkç•‹Ëð‹=W³BÔ|˜Ÿ¿®³iúó¼^—Òzʵ²ùú[„‹œ:5MQ *?ŽÖ€¬›ñ-[ÖWî˜D†:U•“ÿ]©!‚ N ]C ¼1óm%“ÚêÊ^\mâgR‹Êé²["©piÛÛ³Jdu¾w%®û=>©5ÛÕ½.eyoo޵Ä(5ù•º}5Üjøú4Á§H ‡‘ZÙmÑ,×—u³¨l_ŸÏ'{܇W“àØšÅ;Q]‚S÷j}¾nÓ¤fß!”©­IL%Rh y=eäŠH°‡Y¯Õpó6Ü w} Wë—Ǻ”$Œ¬©ú²—;ïw­yHƒ=./wÉëqGWÏùøGšÆåf5ÿ¹J¨‰ª¸Œ]Þ`jG¬I”Iy2ñ&]V0~ö«s~ôèV1AAA{éèáN0rz±‚¯¾6F_s·É"ZJŠÈ7H¢^INUy?^âGÜÍmäUQÜGÞê³J\y"m÷¢?%/íb/éÛóDb¤ž÷üÓ[º¯L—à®5Äýá³Áb¥Ž |䕪c@Ùp·¨#Ü)ô‰Ü›TÚ¸G×…±<ÕiA¥0$ؾZæ{vXòštÞÝ«®ÍÏný?ˆRIè_jçÒ) ƒÆ ×! MׇÄM×¹âúšíþQ‚}úQ{a”`›ÎõÕwؘL$—½YJ ìcïÎLÇýõ¿Ó(;…‹Ìt‰ý ÷ªÞ6ª{Œ³¿ÍÚôœ¼ o¡yWL:€kü§è½‰ØkÈŸ™^B…zéF…öT§ã»tJâÖ«É®ÚÞ$nñ÷Á ®{àDKP§wxŸ¥8”wà xxðÉŠá £G,'Èa‚}ü Soìc+;B9c¬hBÙ%OÿmK 5ȇz¯ž¸,T§=®–ì—ÍÔsžœµÛ÷>ËúGBõü'Cé¾IGê%ÐÏâAAñ[ÄZi#¨õHAAÄѤ~/‡ b3èÂÌדæ.v÷ãL…´Õ‰£Ì÷éÂØ6|¥ƒ &C•§kß—xwHYâÝËr‘r¾ó9èòŒ¼[ y·ž*eï>Uä• úÓJ¤w*cj94…}AÿöT¨¹Õ+õ5Õ÷9iжx¸©C=v†íšÃÁ5ß–Oûµøx—É™v굿†Á¸¾ä@™=íŒè]W ỬÁê]ü_»P‰‘ƒ~øÊé„ôaþ·ÍŸÇCþþãqWq>š¼_¾»C¢ú˜hËéefþ3ÑÈB–:^eS¦4ÞT®®áa0VÔËy®•ã؇"¬@osÎN„]ˆ5´/õÏåù¨{‚ðÀ‘VLZQr  —ÎôPæ!èƒðås ¨,³GnÒ ølèóg‰8ÁNL¼ HÛov¥Ñ8Kô(æã`9‰Â8„÷A¦§{â§38zŒž~Ъôzì°¢ÉújvµBNnÇÒSH[Ýo![#…½Còªª/¬“±¬^𬗳²åÂô~¬)Àys‹µÇ×M:¦š¢þ5ØÄÉýy&rcÓŸíŒ:BÔ„–rà65÷dÖN<ɺ÷™ªÌ0=p9➟B9Yw´¶Ã•ij)ÆÌŒí*pšŸdÆ:»©ã\î¾BÇõ4£ý..ô{yúUõÖv}w1AÄ#ù¬«ŽÆ4Ñ‹C†ô9bIA±@,$ ÄB±@,$ ÄB±@,$ ÄB±@,$ ÄB±@,¬ÿ“¶Àî.ezœF`ò»˜®…b!XH ˆ…b!XH ˆ…b!XH ˆ…b!XH ˆ…b9‹Àäoä´Ÿ%H'xY ÄB±@,$ ÄB±œA`ÑÇRä ­XR°,Ý!¦é lç3Iw·3¤+°êLrç)ïâ G«Ic>¢æUf´cÇ‘Wr¼õ?R ×Ô]ýmˆCž'Ë«»ßÈñšPä¸[až“'–…çÄq ˜ØÛœ©1Ž4ÏèþgÝ]`è4­; Ÿ¨u_+f¹ÝSàªynw¸n’àÝ®&x/«çXÞC ÷Dh.v(Äú/ï °ò›©m‚mVªRŸ°ÀS|oì ~ÚÑ-æµs5[ Œ6/êV£M0¿™ƒ±&½ÝF`”ܧ‰/0ò¬¼ñFË}š Œ;årlsŸ&¦ÀM愎)0rîÓDup‹ ¿# Œû4ÑæÍéG ªÕ1O$˜VÇ<±h%cÚ À¢ôœÀ:Œˆgëõd$qŸd}r:ùg蔲Z ˆ!ÇâÒþíe­tp“²‚΃"ŽŽ1ý|æX-ÅÉEÎl®¦9Ç ÜèøÐÔµ × T½˜2» ]´´Ìg$+ØÁHö]ÔDö:,;äXE$Q2¼´¢€ƒ7d ±¯É¢Ô_¸¼x³eù ôÉ}e»ËºðRŒE á@8oß ªËNn]Òns½¾5šFÚ¤±ƒó~õÔõxI¸À¥rå]Ö™½‹=ø|>Ò@9Ç,B Ü»ó¹ïñ¸ËßQwæÁ:èÑð½”o/¿œÜ†£·#H ×YãcýŠ^× î·‚ðèeß%¢:= ²¯@ÏË.ë³ä BO›µËG(Q/9Éuv}½ä/3öéŠG_ÞÕ(uRY/3ÀÁÁ¾NŽgÙÇ:QʯçXšS .ˆ`ØÚçe:¨Ï!ž4²ZQ†^k½±ƒª(¿¼ß}X½ZÂEa¡Ìæ¤T˸>Wèsˆ“^ÇÈ/F/ÒóîÇÝ;· [jKTºýZ#ðýnöœö.ÿÍŸÇ{Vàv3¥)»^òï*iN7•ÏÛðÚ¹Ý.žMF)¨Ÿu(¶¢ Q‚FA^•¼“yv¥W6Þ¹›t<3Ϥ4<ÎrÐñ`Ì,×È¢ nöƒ1s\·‘¦q t=3ƒ]¸Æd$°É}Í™Ãñ`Ì4Ûê³×[6Ög ôܹC!¼‰¬PM9üÜ\Ä΃@ ˆîä—»>ØWl_ýùá(Æ;i}üh,VÛ îú˱ÃáFŽ»¶†ÀBòŸ"5ü]"¼ÐÙÅÎvfrd5µ*ç KºÔø(äŒ÷k¶ãŽè/f]ø[¾ãëf¶}™1>–Éøh¹‘*s 46ÁúUøÛpIÁ@XXK˜èÛ_Ô2 ò!$‚Àd9Rdm)×ÐEšúüv´ ã´–¢@‚øNä |æ.³šÅζ_Q0ky}k–«šs쑾ä›Â˜W•pÏ šýœ+Äò® “é|9¶G­Þ å w×À'ê½ÍÊV{ñVËóH¡ú*6H‘‘ ÂÉ>aÔ3·û‰oB·ÜLeÈ¢k{3kUëÊH¢U¨„nßТ· §ULíœoBg]Lêˆþ+?àð½^¯7¥Pg ósùY÷ž·itéô˾ h Ü@Æõu$°Ö „9P®Û­Òjÿg†jýoÌ‘íÆXô?˜á6P¯ËF‚eœ9ÓùZ¾0ì H)2ºc‡ƒãYG¸ª|ù‰?2·ÈÀÈ@$d 2 ˆ„ DB"!‘HÈ@$d 2 ˆ„ DB"!‘HÈ@$d 2 ˆ„ DB"!‘HÈ@$d 2 ˆ„ DB"!‘HÈ@$d 2 ˆ„ Db,Æ=æ3ß(éq¥Œ¨žd¦•HÈ@$d 2 ˆ„ DB"!‘HÈ@$d 2 ˆ„ DB"!‘HÈ@$d 2 ˆ„ DB"!‘HÈ@$d 2 ˆ„ DB"!‘HÈ@$d 2 ˆdô˜CQaŒ ¤ÇH²„Be 2 ˆ„ DB"!‘HÈ@$d 2 ˆ„ DB"!‘(· {á‰t‹±Œ¾èXX‹OZ)®…r`0yÕпSe åÀòö¿†ª1ÁäÕÈ@F9ПÆ=Ê+Ðå^^ ñÇ}`2Ð&ÏZ÷,˜:‡Ð!¼@e”{ʋ䕕{*'Q¦µ®YîAÈÀIò¬î\!§P9Ž \‡,öfÊ=h ²6ëyCˆæ¤!‚¾AÔ‘K®DUXDF#²ÐrB®(÷ ?oàšròÛ®,÷ £Ç~e: ‘aÊ=ÈÈÀ_™ÑWîA~ÒÀJˆ sØB~¨ ”W¤D{aYdd`0"V±gðCÊúŠˆžê¯ظ'220]rÍ—(²lÓ _nà6åä› Ü¬Üƒ|¡Û—{/4Pl^îA¾ÐÀíË=È·¸K¹ùaï³Ê=È—8Ùûls¾ÃÀéÞg›s~ç{ŸmÎy ôë}¶9g6Ч÷ÙæœÖ@ÏÞg›sNý{Ÿmι T¦íXÉóàl&QîANe`*åä<&TîAÒ70Árr“+÷ ɘb¹IÛÀDË=Hš&^îAR50ér’¤yâå$=»! ÈÀ@ô5½s|€„ <„ ÌÅ9ÛKÙпKÇÀÓœ8Ò4°:ò¾F)ؽçaÖ@n/Ú±û/"¸Ïç@=ŽÌ®ŠNqÜv\î£#Ù¾z‰“½ÙÅZrl(rqªãwœý²c Ä>iº7Ýá[«WËaFxÒtcàÉÂaàçØCx‡roÖœQ¼Œ×qx«G¿¶«Q˽É"*Ô¨©¸ëjKÆ>Š,N¹çÚ¦Ës|â67Dz D”{À´Þ«{˜çâ“ÔCŽ“ÖYG¯bqOØ_ÀÆÞšè#›æ<šå]ü²Güöi©õ·¼í Œõ„ýå]–ï.þ(¥#Þlßpx>«¨»”`uÅuøJ¶3PDªî•%Ü,¸ŽxoB¿¬¯ÁIã6åà†9PÄ8Û6ͧÈfNsÇ¡ ‡­‡Ü²©<¸‘±ž¸ºèã¨nßÂ혊Oš° q Œý¤éÝUóJ‹¸ЏOšÞ÷ÌJ \'â‘s ˆØL»ìz(º°M»¾`yD‘垣bV0“»fÖ±ãS¦AràB£G潿ïò-–W}QS>à~(‡ozw™-áÎ ž¹k*ʬ—òý.„4ñ–?Õ¿UªÏªê_CC|âF×hMÀfèÔ‘dœCsW,æ ”íø;ˆ·Ëówùç_·Rùî× ¾ø1ZõœBÆF-•ñ››YÈîKnUÕäÂn¥w´ò~”»k>eüæxرáö¡5ˆÃLƒxWc6Âþ•¶QS¸é“2¿Æ4ˆ¯Û>qõZ^%U¼ Üø‰«Û·`àFO\™µŒSÛ·h`«Ü›l]ÅIþ8–s`Xz˜¶û)YiàBÅÿØÖÕ®\Κ¬e<;Çvh»'Ç‚­+?Ó¾–e›¤¿gdl ÑK߯Ij˜Æq)Ž$ß²ÙCØýµ£ô¦j ä4MRâKšMÄa&fp®ñby¹ç %3^LÄÁ:àDzá¹5sù/ÀfÂ8?6Šû߸œ* ™†îåÆwu@B<ã|ˆC3÷rf~‹£² c9Ø0!°i£/Õ« 4ü›0‡?o˜ã^žy8¬ÄÙ”™p[yÆvˇ8Ø×ÜðÌô;¾KAA¤‡•F³ÎÉÌFá„7È&ç¬5°}« ,ÈÂ%´¼Ëмó°i÷1³…C8išhLå3¦‰EÁÕ[Õp.ÈÀõ°}G"âëñz„~ øAÄ »¥u–þ3ŠcZ yi’/_nú?—§GÕ[¹ºØÌŒûNjåß †YWwÕo&tY´ ‚²‚ºxŽ«*åµ`»¾ÓŒkªݳ;ƒY›i*ÐLV£“ɨ«Ó Øå@•=‚r`Æ™OmLå@U+ø’]„bä=U.”™M*IÙ÷ÓeàÿÐ1’ŸìŽIEND®B`‚guugelhupf/doc/images/hashing/Empty1.png100644 1751 0 11061 7515105045 17723 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿkw+ÄtRNS@æØfÍIDATxœíÛ²³*…¥ﱺû=Òåê{«Vö½»öŸ÷•ðŒÊa™‰ãÛûÏ2ÉȉxŠ"Ùýgò0áa•ÑA,ñ»ô»is (Št{æ–,§Gò âMù¦ mRçè»_ÊÔ_œ“{n ù'u†þÕNã™îž8T}ˬ¯5¶W?ÕËcø>µ°Sn¤õN,R IÌëÑgPQ¬Z:§i¦{›€„I ïrò?æ«&Û3íX='Ö(\ g GŽSÍ›JãtLú€}*a“:Çù¡8IŽ‹væaŽóFŹ>.š<÷õ²‰t°NþÕiî´ÍT#ul–é…Æ÷;í³4Psº‰bѸ‰i›Ydü•2…z5¹êŒÔ8)zæGò“qdˆ ‘!2|G†ö52n Sg¸qU™ÂÝ‘•zåf]Í¢fh­!e¸uýލ°I¡ -ÃÅ.‘ 2Ü€a¹yå¤pë’jÚŸ,…ˆ¿3·s»€¢°IœáöUdNÅ&q†íÎåëx…;×õÙüäÝ[Ñ ›­ÌT¤Dæ·{+ƒ‡©3Ü¿÷“áámÉ8…öña{|a"ÿNqÝš ÌÐyguŠ”¿š~a4|¶ðúó|>›£}a3]5‘ÖÂøõãÂu:©ôOÍ÷XÃíÑít}·BŒÞD÷"Œ0)ôÅ%A¸`•±~'ˆÆ©Ÿ®n¹ê¿jQ¨eµ{ÌRp¤T§2*ÏJk”U·‚ºæwñp ©Wûˆá«ž—'qÁRà—CÝ™ñ˜²ÚÉÔ3LEÐu’ ¶_õȽ'MÒÜÊ7t¡aN»u_F3[N~ žáºjH 0yà%®j> þOx'T†êbaóäëš´Ù}@©ILÉ~Ÿ$§ýÄÀ#ñ†Ìîû„e˜þ¬q3ðÈ'> I_ÿ7ÉsLK™º2|%¼7\»`_ÖIv…ú٤̰ã7m†© á­I™[™8¿n¤Í/}5“:¿Ä¼g’é˜Rï/üõWÿDÐøÔÏ¡è(Ã!Ÿ¸ˆ³Ÿ0:xäààÁ–jÁþŠiá³ôÈP×Ôs!2Õ³!]f*¿*eGœèÇ´æOÈ-º˜÷—¢„ôÿ¹&CÕfìŸÇ©–OI•Y%/Xš•%BtfTB=´eþcÐþ½òI¥ÑÇUzéÒ! R@*H©@ ¤T 0Q0oò›ŽS²ø€NSH©@ •¼=nÎÂA*YúÜ~‡ƒGøÕf-Г¬{Øç¡Û¬ú<Ç•U Ïƒ¹4–“du°Ù\+Oƒä¨*™ŸÏdxÔ\vcXŠŠÁ.>ÜÃLܯ9”Á#*ÕL@lú>®œE ªdöBdM!@É’=öpöû$®ÉûpYP?¤ Ã:åäÙÅ}62lƒz(¢šÙ"¤×Ðù[ÿÜ"‹ƒM@ÚÓ†öü;]`Ø•0Ã.ë¸v¶À2´›6êA‹À®“' ,Ã*Á"‡ƒMXò“†wI?W`ÞŸúô]Üø%yšüþ£˜‰\'MÁýÑOÞüT1ãÔ)°Œéð}¢ÀòõŠ!àDmÔ;çI¾ÉY}ïZœê`Ä>I n#ܟψMOhÚ0?¿MAÍ û͆«ƒZ_ü$§ìâaØŸw liòÎp0&2f¼U`ø9œÍ{Œ©™W¼O`Ù&=Èøü³Îön :³±æ¯¡ŽëU¯¶¸Õ?ÿ³j½ˆÔ¨%+^¯¿WkþãµæµZóïúç=“õP|?ÖN·÷Œˆ,ºÖV쯭FTã=C)fVä¤0ÃçfⳂsGCÀ[]?D¥K·šGOO¦§§æSeèÙ\˜¹ôôÈ•z‘ÂŒ}Zª#±*sÝg²Òc$™±œR½ãñ„3øBvtˑٲ"T…+…òKÿÕ3 ë†÷…®˜Õc”*Y:S¡™s³g†*3 s̘Ö/—N8ɘ \~R,Òœ¦NUvRA'õ.³]\™÷¦™`v®Zs¡ùtÁ7¡ÊmÅy:h¡GˆÔcerì¦Á>˜ÁãˆyR@*H©@ ¤T R@*H©@ ¤T R@*büÃü*¿Loä|Œ@¶@ ¤T †aLa@ {\¤ÂKàÆøGH…—ÀÁ] †°5Ê+[@`[CH±¸°9T"Rá$ps8¤ÂHàöx§Ÿ"Pýa}bÖá%§š›ÝwÈ ÃK{$ ìÞkk¼>U߬áøTÜíÑà Ðu$Þsˆ—À øTlŒyÅFàÞ€‰¼nÀF bk\.V·Pƒ°hd© ÞxMr²–]¶®™[€N¸8¨‚x+Fr7R°¸‰ú"·ˆâ`¾:.î/ÖÓÁqƒÉ.Þ bN·as¨Ûõô!~¶ÙŸt’‘ÀmÄjÚÎ|lÇÿŽà!ðhnàóT8ØŘÀxTAÜlÄC`±?H:{°x4o'{c}³x íÁDãmˆœýIަ»²fjÿïÎ%À¥OÓîpýÊàá´ŽÞS¤çc:$ç¬ã °8šÒ‚‰ÀýI-TAÜì~ÊCà~sx Ç3Tóx0ñ Çäx#L|œ_`q,åmÜɉvpµÆ«µ ¾ÛÔ5Ö*a¥’Ö6i—¸˜UÒÞÈÎØhÉÙX³õmʆM¬mlÅ•õ]ÂŽ¾­,ÉLº³>]©Èa¡Ð5cÅd<žžJõpÕ2gÒrß±ž£êj%Pše~íà ‹Ñx¸ja)fmº¾™¨_›¢ós­æµTï…n=Kó à[¨ëúQ©/–•yÔP½H©˜÷î¼R¨EuhPITº.±þ9ݹg+‹J·Ë»ôz£nóÊ>' ¡ÔÏï)IÂ|W1ئT¶iIR¦jbã·¾ª_içôPoE%Þ!p—æè®‚«ÓøRvÏßöJf5ÔÂJºy}äÛñ¡¿o°mËÀu^ÕN®M!Qu¡ÐU¤2¦ê4—ó÷&ÝhŠ*mëÏÕgÃûñaõ>ŸqÝ·17Pý@!ûãŠ4á-g?\>ngÒifyõÿŠ…:ý7–ÈþÇÍJ`5þøÉŒmMZ±2p¶NŠÍ|¾–/,çHB¥†ÌžF§ûòš÷¨³U©N¶û—Üz> ©e á¡W˜Â@0 $‰À@"0 $‰À@"0 $‰À@"0 $‰À@"0 $‰À@"0 $‰À@"0 $‰À@"0 $‰À@"0 $‰À@"0ˆX,Ïz8 ›ƒbùÆ2ð¿),JàЇyÖÕ ö9@HD` HD` Hà3´ <’PSS¼œ©`ànû`à%B˜†×ð´0pŸÖ' ÜÒð`îâUaà>0†çñ0p¿w4|'y€;À@"ž wðž¦n‰øF0 ÜÒ🩠nâ]aà60FÀ\s0p ÿ74Bf‹\us°&dÖ®'d¹¤&‚ou}4ãiÏÊ@LÈRŒ0ÂÀ ` hÓƒÕÄÎOgbh1Bî]üq¦†£{qw¦†C+úþh`` £¨£[Ñ]ô® à­h|’ÃÀ5£îúOWF0 \XaàH#4‚{å®W5зöŠ]£®fàÁ¾!¼$8‚µâÀ§‹èÁÊ—5ðU$‰àë˜(‚¯j`Ù¶i àE ,u ¤Ü͹¤„ΙW40n˜¶.hà0Vo’x=§É—›$ù]ÍÀÉ¿4|5_Óbš¾˜í´˜¨^ÊÀrò¯®êG’L/d`9[þí l’äzçþuþÂÀ0fÕ_QϺ†AÌüëìÓmÀ&IÆ×0p¾·‡{Öê.aà¬õœØ¾k8¶žo$W°\ÀÀ±ú«ïMúÜ¿ßÀÁ¿·Ø÷ýÕß›ìûzÇñÉ›w}ÃwØ*–üÐ;ã« 4¹ÞißWh.¾¼×¾o6PWõ›íûbUëùýö}¯í[. ¾ÒÀ¶ûïöÓœò]_h`]¿êú$û¾ÉÀò¥i ózGú=¥ÿ[ÿ㼸{¯ÿW»n#ú¤Y~™q¬+n=Ï· ¿’á*]®4~m~ë×Ùh—q2ÐM¾¸›&ÕýŽs`gà§!T÷&R¸»ŒÉ¢Jòe•G|ú¤>‚ |B]zÔ'~Jw"ÕÏÏýenÑâœFH÷›Òè•£TxÔì²pèsx<É?€pÐJÙC/r\!W ¤jÿéÀí]elÒo„i9‹ámÿO7¹}®¹\±€ÉEÏ-&Û$ Ü¢óJÊ® ý‡Y¨*iÞv¦™q¤Ïy(€s¸©»c1ÞïRº“|½êX̮Λ¬ó£Þ´|7ÿº•Ú°7P˜ Ѳª¦$bJ9o,sn8ßÌ Ú®}ª T !LãU·¤~–jcÈ¡Qô°+‚ʇJè¹a;]Ýåè‡íÓ‰ú ƶ³n=wª„ºÑÁÞAsëÂøPè¦WÙ¿ÒêûöWgÅì"þT»•ÚÏ ‡X™ trý¦¿E`Z€æÔ£³pH• Öÿ•Øy^ X£IEND®B`‚guugelhupf/doc/images/hashing/Max0.png100644 1751 0 14545 7515105045 17363 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿÿÿÿÿÿÿÿ·±OÅPtRNS@æØfõIDATxœí]Mo㺒%aÙ‰wð~ˆåͼìôÐê ggàN¦wn&¸³k`H/tî/4fÿÛaI}Q’)‘¶é¸N£mY¡JGGÅR‘¢(Æf‚ÏݰiCðÔöD N„¡Ák§RbŠÉwÇrKWƒÉz3eÿNp%™8[tSRb¹u,¸rµ˜äµ@c;JrVºu†«”Ìyç¯Ì™f²qT)¹•§ò¹tQuÁØ—¤È`œKHôWéÊ—l#¿dAøZtJp¨åÂT"¡~3–†­ü“ê¨0­HèaVOzò0ÓÚîÊâˆÄj✃`Ð)5£Ü,Î}Õ$ áRÎŒKF0ÈÌ»SÚ#c£sêáŒÜõ¢ž;›üêZ°t6y„$î°ä=|õ_Í4ìq¸`ÂþÏÕ¦ó•Ô9ã’™‹VO®ûf·®6%ÍBæfM®ƒIšL²^‹"/X•™¯!÷PÙ!-3_5¸úÀ¯*;«Vçlæu‘ àS-0c+ó“ }„x|ëTÔ;›„·ò'eŒ3“¯œ ©:çšÇøÍ/æêD D ß–!#9öGG#3™ÙuB8÷¬~ȶRØÓ²*ƒšcS“Îp½!0–ÁkË×ÐG½v¿£4‚Æ%àAVÀå£Es+×,í]mOqŒÛ/²áyð°ó:Ô&Epg·Ð÷ã|ÇÍ @“??K2ì®A²¥õ÷Ácàí¥V J÷,ÞŠÝn÷öe—eE–ewŸäGÕ¯>«œu;Lª]´»ô/Ý*ot|PÔGªÃÌ8ƒÎ!ž*‰‘ï®Á>< æ´aïHkÍÑð¹X«n‹)ÒØ…7J÷’ŽP÷MyÅPuz@Hðñ6†œÑg gà•B¿œd(bqâæ¹ sûÍðh¥ª WÝj3m¬T~Ê/©OQOiZ®ˆEÄ™Âé.ð”[ÅÁ!ƒd ’A2à¡ Šð‰$ƒd ’Á£ ±yøkJü’Á¶Žc0P7ih†˜ÃÇ&ŽxÓ;n¢5Nò°ÜFÁ²'a-%Cˆ5†àã‰ÐïX6Áâ)gÏýe,í T2Ûz•v²=D°Èåçzâ9øèm“ä ^¡ø ¯Y>;úM¯øYžŽÆ0Zu0ÓÙHžä³¥àÓ[©ÿf)¸úò0n®žpÁ:³Ü.U7¶¬ãåöÛ}ì¦YU¨Ç6­i~Þ«Ó½#37WËšÕBy ݤ8ÓOªnÇiÓI`®°†*”y||ܬ-P`ZŽê…]Ï2“2|¤Œ…Ev¸»7q™1¦OB///ßzLt£ßv&ãUÊÕ<6à0;™ â`fpAœÈv ™¾«2øò:pjõÒ¸Æ0;ðwÁj_Vn|($Þ:ҽØ–,‚~”ëÄ8¼BŽø—Šqê*ŠîÕã§ãàZ«6®Qù OnÐßPic¨s»…³÷"B¡¶º¾%ªOQ•8ynŠVFX˜¿LÍ\– ÓÕoÈéİô ªÙç˜Êkô<~•vÂüºd]æÄÕ7ë4Ë]‚'ǧWŸDQM–ª©hª&õ’ÜR¡®»§äȨÑC÷˜¡úv`šcqu“Tˆ’TDÐDÐDÐDÐDÐDÐDÐDÐDÐDÐDÐDÐDÐDÐDÐDЗB0ú.à4Ú^~£`ìãôôôôôôôôôôôôôôôôô…yÐ<ú>j=T>¾‡C.å>I¼ ‚¾ ‚¾ ‚¾ ‚¾ ‚¾ ‚¾àýÏßž:NÞ/â&š©8ú\,¢‘÷§ÑQ)Ø×YÄD0î§Ô9ãiÔÕx ÌÄD°_Áh0x%‰¥–\Â¥.n Œ¥–\®‚±Ô’KUÐev´Ó`˜`$µä‚Œ¤–ŒT’¨ZèýïL9Ú#Ñ[ٵČå·:Ñc©%c: '$¸Ž`,&ɵ¤ŸàòññÄ<1@Pþ‹$|ø _1Ô’!‚±DÂ~‚úå1Ô’~‚ÉÃÓ6'"xxÎØa(Âl´ï1Ô’Á@]Æð‚?6B0‡HA-$ˆ3ÇLC$&˜ÃÇùkÉ0ÁÛÆ ‡ &07öùpÄKé„Q<ðžŸa„ <Çëó×’±0SÆðºØ1‚9[ž?jÕý¦ÝÁ¤dë͹ëñ¨‚[¶Œ›  4¿û%ߣñUógvÂq‚Œí¿žŠÉÆkñÛïÏ …™ýž·dœ ¼óè}qÖ×Pðª·úŒ§ùÀ8ꜩÄÿ| Lç‚bõÙNó!‚ظƒP¸||T2:½¢½Â´º÷¼ÙýP-ÎËΊ#½ø¾‹Šè!‚EÑ}‰kd ›‡ÊÖå.B‚lõûRG„AÆú_Ö{ &øM-”Çf2€Ã7rô+þÞx|t8Üi‚“¼>[Vè@0Éåǯs½Ìå^JÈÎä†N7s¦R†ò˜LàDðVþÏÎt’ÝnǾ2v¦æÁIðTäò¨dúàxCû»þ>ýIv$x“³3u·º @ ay4*ýp%¨Þø¸9ýIvT¡ÒšÓW” s½T…È&ü‘3tÃò¤gÙ™à¿þ7Ó K¶*ÆÇ¤=Èß}ºäpÚÈ£Û\æ5÷°t2'º}cë½ Øex2}˜:vëæ ª3æ×§©+Ó—a¼»+x ðÑ1‡ FD}½¶ÌRp÷(9.îTè^=žìÒZÓSº´*Ì<Å·%ÏÏσ§Û^}l‚·;™?Y»ºTçÆ‚Þçª^.d—ö™€‚‰ä8:N©´ÖœHAMp·{%ïÕªê-ÜõÂSÞ( Ã…ìÒ>W»ÒX+vø©·™Z{ŽÂüÍ.=DpŒ·õ”›ÚeR|ÙI¦™Äý¾(ö{|`!"‚Õ.5S…BþØë»âS†¸“UK¡±ð©³p7@Ð÷Õ¬üS/Eµðwµð—z¡,d—N›]ûþcª+)ÿø§^ø”i)VY©õÎîôß²¬[¨ZXd'ïN«¼jõÅŒw©çé­ì,Ô>8€JÌÊá„ÑZT³™?òº´°6#¥kK¬ó·ƒo·ÏvEYf{ ëëÒÜ*mï÷ ·Uüm)Óʈyú„×(ͬÒV¡ÆBÔì„(ÁSxâ–DŽ£‚“ãÐÎUÀãËJ’–¤Æð²‡s'ˆ¸DÂ5…ã…™k B(@;@F¬_C³¼RáP&mF8ø5J¯X q±±Ê.cA¶ö¤a½s!sFù!dzÙ ²]‰ðh™GxîRà_RÈN9ì\.4§y~~Öוá2PAx–œa8Å¢©hï\ÉkÆË¨¡`°;ŽiË5C,#FËô<VgæB øàEç’¢“醟;UïpJ¶zËvÛ«ZŒ•]ˆþ9†3Ü—‰§µýQ~//PÑ1a“µ>…–9ì*D<øÃn·Ss±Áï@·ßï1VbÇ|rü€èT4Ä\è¹ ¥°—C o^¾©#‡ýCDBJB™¬à×-{UÓYðt0Э¡çª8¬„lµ&¸€›UuÌMe±Öue@ÂC3<űw(sî‡,„9€n×T×˱dNŃNLhÆÊê»î´:çr=´<ã ×ËQAð¾X‰¹‡Á¶1Âv‡!(¨ÄŽq5ôÞzb%þ5ÅclÔ›ØèC@ƒ´ïÙ6œÐðÔüÙºýÀÓNC«'Ä|8L>>s?¦OÀ®í¡“ó‘ D`–ŠÉªúÖW°ú·.gDo³þy5«íã­u -áSÝ«ŽWÙ´Q¯-„^]¦i‹³†Æ‰ — spµòêàbô ¨Ëж€u¼ãÉÑ›øèñþè  „@Pͯ* LÜ©Ç68g^­ê«7J@ÒÐ#u•t è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð-ÛƒÌI@4äiky÷9\BxçGã9’Ï=Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O€ž =Az¢3Èœ†øNEgŒ4ÒŸŠÖ(}š ~:(z‚ô è Ð$ 'H@O€ž =Az‚ô è Ð$ 'H@O €¢WFKÀE–›ÎåaŠ€›Û›ÏÅaJÞ° ؾ¡œSž %`ï£gÝâØt.¯ÂT…»¹Ù·š\°‹‰’ v15‘&;˜* Õá&7åÈÛ˜, ¹`Ó;È[ =1]@ªÃ-LpŸä‚MLp¿ß¿3rÁ6&¸–î ØÆ ¤:ÜÄĈ’ 60q9]Fºh{àá1Òä‚´‡Gé£ ’€5&'Òtic²€ä‚mLoÊ‘ ¶0]@tAÐ`ÆØjÎ51AÀ¥þO.ØÄ³¿f…rÁ¦X<|þ ä‚ L°xÄ%pAPa‚€IñðTla \ê°Â4‹§%.’ V˜˜Æ<àu˜\°ÆÔ<°dX‰ßI@©®*.Hu0¹%ò $Ô˜,`RVÍrA6§-ü tä‚ Ó”.•xM"fôÆ%*øNu0çQ/U‰És„J¼$T˜õ°¡tA©àšd3LþÁ ¾Sžû¸ëçR~lÉÙ\U‰¥ ^½‚3¸†T†-×‹ÍæÚŸ?œ)`’—òs›±ÅµûàÜGþ“|þ¶a䃟3]ðQb”ÏÅa¶€è‚ÛÿÝ¿ÿ–Ð¥aþ¬Ÿ¥‚_÷ûÇíu'ƒó„ëˆp¿]^u%ö˜7¦X™¸¹â@è! Je˜jÑ-ƒQº,øÌ\”°¿u›ø:ݰ3JÚDÜ9&ƒuÏ tëKlF¦‚OÓƒ•˜•%|7z@½¶€Kд½­²S¡™›Í,ä´Y×ÅÄ—«¿k·êÿ£A=¬°]¨ÉÖ¥ÐÌÍfrÚÌwö¶g‰Ö¦kð˜ù,Ч"g«çZÁžÊáB五X=¯‡:W¯)¶á$`¡”X«Ê|mð°0?Ö ™Î ¯<ð»ùµ†0¨¯ËWƒs¨Ö>a°\yº4™„öK®Þ7P…¯Ê ÃÌâ[)øK}•3Ù\ Mƒ|“ë­àõTãPóHߘ@˜e*,gº4›ˆ;yïÅþ^;áó\J—…p3™+ûý/Ý$¹Žjr*øï9SUØÜ+.ç0º0KÿG®Ìp…+¨Æa_FP)hz·>~Jøm?êîí„å +—„ЯÃhôÏèz¼úØW“# ÈôÅdõ¡#á1üRõ.€® ßŽ©ËÀQ<ðeWKÈTBøa¯&áüUøf÷–ë5?fõñÞ©ôÃH˜Ý›¶ÉGÔð˜/¥úñ5yq¿ÿÕÆúÑ4<î[½n fÐ:^Ô®>Ô“c¿MJhn<µ4,Cǯ\Ò¼s—5Ú'#»9‘€»Ç7 6jXš¿®ŒŠ—Ú\9Þîvð#ï4WÏÏ}÷òJ‡5N›Í,ä´Ùñ\ɖɃù±ÛýÉ”€ŸZïXk6Wž#FiÞ9Þ­¹Ó(?³JFòÀ ÀóNBÿȲ»â¡¾Ò˜BOízÎH@ƒOÿóò3¯~’oE½šrÞ ”=kZWøÀ…œ6;“€OÅ>îYC±Un xe™ ”÷ gòα Ø?Ä·§°]hÖf pk­Ñ|+IfZÒ&î`•^nl—°}ì¥<[—j›íøaëAÙ°æ³Zü%ý¬ÚµÐ:5…6kðÌø=û™•­5à}ykM^ØÞìÓýÞ*´ÿ5£Óf—;tö8îïRÓv¡ä÷Ÿ?ŸÚ…þö·ÉrÚl¸í–©UÆ®žÂrsn›–)Ûgi·PßfN…º¸ÅÒ^"yÕL{kn­ÂguÚèÐZÕW¨G8³ýÖÞzÏ - èYeíΊo6ËIè{ûLmjÖK¢û<Ðbз™›í®¿Yqßé,@ œ”’ ›^­à˜®Våm v\p%¬Iuþ)ôÇ©!®•ƒñ¶V ‰J6Ç™ ® R–”C3=ºõŒ ácG (˜š˜ªî—:«í™]ô£DýöžòÆxªP÷Y7L¤Àõ__ô9îö©‚ÎzaîDÃ{Ÿ•€ó쨛”[¹±9pÎõöU‚¬Rd‘Bo=·îÕÌø - N¸§ÛqSpƒ7Ruà3(Õ5ÎÑ®j)ÉÝËå¾®~DÎŒÊ2b¾íÊÁªŽ?t'¿Ê÷TûBž£ÔîÃ×PÅqûÙvb @¸n| dç5÷@vBDv¼¼€‚T ¯Lê"Ù¸TLŸ^QAyNÕ]ë&ëý~Ï0õÐyÐ̤r¡¦s0iLÛDc;ßNÖÄ75nBp®uD=S枆™±’*csL ö{5hrÀ†€““J“ߟ„ß↬{†æJ4ð89Ê0- Sˆ+Ò™&ƹÊ'õ6Ó“JãÐEm›À‘>˜¨§Âo£H ®¡Ú°áæ…/ƒX9´€:=`³ó·­ú/Óý­A‹õÒace׸¹J‹ÆšÐéÜl˜~<•1 Yù›éÇCS}Hƒ@ººwÇUÏùaÃMâ§ìXaÈtn.´¦  Oþf<«ȱx-´£±>0MÿéóÀžÎÍÅÀ ø&–B4©ÀøIEND®B`‚guugelhupf/doc/images/hashing/Max1.png100644 1751 0 15063 7515105045 17360 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿÿÿÿÿÿÿÿ·±OÅPtRNS@æØfÃIDATxœí]Ío£º×¶E>wðŸDbzÕî"•Î;ËHwu©ÓÜ.+ý¤›»ŒÔ™i–•ºÊûúØ&± ‚ ÉyÔóðøØüIˆ ¨Ó· £:p¼q˜vMÀ'‚ê(ÇW ˆX1«ŽžäÚ㺇˺1µ“$$®ŸdWV9©Œʬٰß]œ$ ü©u?ƒåø˜;¯aìDðd©V';í?UZ“éu[pÞF Èàþ¿òß5àµ0¢ˆLf«ÕvÇÌZ„p§D(~¡FµUÎx»D#@æî«*ù!ÜÓsô(¡@lš%\Ï’ì`. CþCš{Oè›õ®N7QíÌ[[ÖyüzDZE!ðìÄq/ªN’'~V¡~öÔŽi}Rísf]7¹Çº¸æõ›ø7ý¬ŠÜï‰ÎªćñÁ7Ëü²bk ÈCæîŽÉ¸Â7c9”n˜oöÏÀ®j»ýM «Ì/ õ3¾|3æD5L*ˆ€|yÿd>O6wO !*Íx…‡‹Õ‚Z/E>ØÌ•c …ð½ôмΈywÜÅC ŽÀb„@ÔA˜•ï±'HÑ;úÞFïã=ì,}ŠþÙNÎïë±íü›}ð$ã åÿ|´ffž&EHÐ[ÆŽ^I’“%ùé«Ífòø?õV¿}Í^œ=%Ï&û>·ÊwüÚX’± éëÆð‡Ó¼#ÓØKþ ~C˾5òhF¦»â>¢Z ÎÉ XlÌ–€Ä Ó¬n–­2Ù° éx½>P7(ª£UJ¶,å7IIR“é×îˆÍCv¢ND‚ƒï"ø0/Š«4¤Ô†í;ÙÁH†…±Â°è襂 MÚtbbJ ±‰]™fâRjá JÿŸõq²sú‘“aM©lÓâmG‘à#Ñ’ÇþîŠ8{†¢í=ÂRÐ\C¦BhšƒÖ9Ù**…g®[HkõcW1@\BïO° ÷>âá«}/ÌÍúk0ÌÐã&ö“áïÙ¼;6§L0ðѬõëAˬkñ;ÇŽB¿UN'Ã^»‡{•cš¢3Ì׊æ%ðž`»¬%D¯–Ç.ÇàÝ_Z"ÁÛòñ± ±§è+[&œâÌãš÷Uyí öѲ;ÍcŠó¥¿´8Ùä:oÙ2Ï>ø²ÿýû甯¤éøzÎd‡ró—s.^–`Óôrˆ÷½ÅždŒK³ŸÛ1Á€¥èÁ㾓ÜSVó½Þö&eˆïü¤ì§y|’:úsÝ«Åd»!ÁfGˆñ*3²Z­^¶ÛíËœã3%‡,…[=èNØGtAœ®·¢øÝY^ÏÖ“Æ-_ÍNw´‡z$#ˆóG"¢„Ò($ÔWÌé‚ô³ícOIÃŒjÞÌ‚FÊ"NÁ]1ŒG" ˆì…ÀøHíYðïÆ4eiéGί|R‘£²8¨9¶ï#;e²ÍäœÜðÜ­´a^r&'hKùÑøž9| â2ë>þfˆàîùlMX4CÌÚŒ„æat˜ÅÉ'b‡¶\mF<ûD†dŸDþ„Æ¢ÅA´Ú÷G*ìs}Ð @ ˆjð÷Õs²ƒ@ @ôSÿÄPÐEÕ/‚¤¨¢·ã¢Ï8'Ƚ7CÌdwô “KZd‚>Œ”в®l9$¶{3¬jÓê:“-=íç2¬ûL.Å –F< º6C ÎÅ { i†=Îä~@¬‹W\e 3ô¿,€gìæXœ ?úK°§+s1zYÆoÿš‡úõP¾O7‡i3}|(Ï Ã^b0TJ3ìYQ&G}Ìe•aÐ{†D•~eXŒy©„úX!ê ûHqn„g}+Ê9†ýS1O¨ooÍÓYÏ*Û‘1Ÿ5h™aåL™‰±ß8Ømæsõ´»o¯Ks¶q¿ sšn`a =£?zd‰ñßñSÔ§þôª<C±£–ÕýÊèàa²äÔ"ãoO  9Åñ«*c¯òyŸÑj‰¹‹ïú”Õ÷à ßã`ÙúéŽq<›u-é øý(— (\Ïãîn·Sz1ºèyþÏÈbµáŸó³vÑ×Kg´Þ²Çd0f)yÙ°ÐØ¶. c™å¸Ë* ‘7‹Ñ–Àšã²…+½”°ÌŽ!.„§¥Z4’÷‹ä+lö’a|Ì"F—Û‰zàâ8IÒt±zÙò îUŒ¡V—‹ƒ€)ä_­AOHöÛU’¯Ïß·ªž9èÇÖài0¸ùB€tºL¶åìv®FN-؆ۅœ¤ñA.¿bàmÞ:Á®‘L¾kÁû¿µ  ¡5hE“}“j\Çãö&þpÌ­ö VòÆ~ÜF¾èYl,¬b®³bØNeGœ|Yn_¡¹KOÒˆR¹X(—yéGD·€õö"¾RR$–þ€?a©f’0ßVÁ±ÅI™ã’ˆ×ä°¬å`9¬C,æÇ,!µÄɽÏVÞÄ@ìiǰ: ¢ÀMÈ­ Æ€ð•¦(<×#øR¾+sx7!Ø ˜ Xà(g@ì}òCÄáÛfÛFT¨ök4e¶,̸ªRtØ TC—s?»»"N ͱ,tXµåìÚ_pK>³|¨¹ùÊe¼DÙ9âp‚ÿÔA¹>Ÿ\§*Љ£TÆ1qZ&(/oÌ>›ÛÏ8È^™‰| Å}‡$[R‘¯yH„1´¿¾ƒ!ü€;]“°A캨’`oßmÔ^ º º º º º º º º º º º º º º º º ºâ|†ÙP=ƒF0ê!Cu±~+@‚ €]]]]]]]]]]]]]]]]a!Øõ-w½Ø#G!hvCô‹ ÍwCôc—!ì†p…•`Ö¬=oû;Áäq…‚Ý?LÎ<‹{€*‚çñÙ+Ø9* vÇ•»® Ï!‹´ vœÇTQ'(—Ví8- öcQqJ”­{zJð0uÜÈb¹ÂP·y X¢`?ÖH†E&J öÕÕLÇylWPæq—U¡ýIÒƒr|ºJ v˜Ç»Ïã*;¯ kÚ`wy\“`wUa%Á®ó¸’`×{:Ö­;Ëã ¨¨;® k(Øí«É¡½ç³! º!úí³„ŸÈãŽúuê”bQLzLP ÷;B-‚]V…‚ËÑþc‡U¡… ²iK‡ƒ…`~”.$´üï½U&%°|ÿïð¹»bb!ø¦îCØ™cm!xû¦oØ h¿˜XÆ1퓊Šz¹ÿÔU;RÁƒ„]“ªGݲ%¥°¼Ï[aÛ°Ü$ŒE&·]L*²ø~³—p&ý[.&‡ÊzÖMÏSU!¹ß(.'UÚ¾»0¬ô¨ï7ÝnYíòßjÈÃV‹I¶MB`Øj1©ñÒ¤K Û”°Á¡.aËvXçµóq¤IØ.Ã:‡›Ú RŒZ/A‹Å¤Áœ„A{Ť^ÛŒ)a‹îk=‚y+ ÚÚC¦fëÖ”°5 k’\An‰aÝöÁ…¹¥kÜÒ&Ïu ÍžwíTØue Fc´·ÁP›®a놸¿jî>¦­˜á¡Ÿ¤¼‚#xücn-Ü Ãúè¿·#s·®6Að^Aõ¢2=½Ö'xû6ä/É-3¬O0ŽgÃÍ’Ù|r†Ç$³ÁPñÓ?)Çõ4I†¤E†Gv…e ÇjFŸ”á±}u_†’áZQñ”fxtgâ—áø7Öž|'dx|o§d¸~Õžì-¥Awì|8!¼²QÌðtv“þbÁ¨ §Ÿñ‰ú»uhïê¾f\cs×cшà`6|‘ÍëÃ,‹™›ÈhþŠM³!Àp)>æúlaVm°óf“ Ç, â¿V[ñ±h¿Ô`w·ûˆ_„Ó)’=¤éýÓœ•q—­@ö`!äåö]#ØÊÖ?j0}Á‘ÂWÁKûŸþ^ªÁ¯?æjðÎèO“ï#Ã{Å.hLZƒBk°îuŽ› Ñ Á®w£.Ä7ƒ8¡n*zˆêF—³H-¶‘’ùÕPÚÍYMVÏþÐJɼÊå q 2¿¨S0ƒZ\CßȤºF@ jƒ×D!9TI”×(}rñÀ]4jµŸ-j`*‡Fô’ Í“¡}Êaâì}¾”¿ÚQJ-5hÖˆ©EVúŽJìY²ÚPõkóqr€ÎÔ(#2·’ýaUªæI'Iž¶5Nš¦PÙüZ gÔ¡k«ÕJ:÷åqrà †”_<«G£0 sµ÷ˆíqøœ~>št†ç†HùÀÇ ­qж…Q÷Ó͈XË·óÐX¶BViüïV”;¾,àá›z%¶¥˜ö×!yÄüZY}zHßÊo½†‚.¼¶ÕˆàòR^¥AÕÂOðÖzøá’ŠF@]ÉßÀá/å vÚß4Ô¹¢¢§¢*…«T1„6~çp}¨‘8¥P$s,4!áÃѨ´¢ã½ÿ@PÔÃRA©>nåPçF,­n/±õIgØVG!u†Pø„èÊÚuËJ ²Ó¡•F†A-é\ ޾¿ìE¿H@3íkht—FòÊ™×Ñâ¿|8Â2^& X[î<[eqÆ@:t èÐ( #P@G €Ž@:t èÐ( #P@G €Ž@:t èÐ( #P@G €Ž@:t èÐ( #P@G €Ž@:t„!ͦ9 €5‘GéC¨§9 ¬:t èÐ( #P@G €Ž@:t èÐ( #P@G €Ž@:t èÐ( #P@G €Ž@:t èÐ( #P@G €Ž@:¢™€þ‰œ+š8…½íM¦90ývhƒæ(ý:ÓÐ4›æðµ`†¦Oá©o"çŠÆn š @s?äh. b‡7Tàò*‡…˜¸ ˆ&HPA×Ö,Ä®ÍY¨ £€Xˆ]T¯Þ[¤¯]Ag¯½»÷‰\¹‚\¨0/—]À™¸îBÜDÀ`Fb5|Õ 6)Â1 Õ¯ºƒP´`Këc,ðªMP˜ßU½ªÔjÁkV°éS8ÐBW\ˆA¨œù‘n *(ÐX@,ÄŽ´¦àÕš ƒ€Xˆ.¯rº‚WZˆÞ…±tmLÀjÐQ@,Ä®ÍY¨ k{ ¦à5bçÕk¯Ý[¤¯¼qÕ]À+¯=̹îjÐÇD›«.ĦPvs¸f›LsÈáš qÞ›ìæ )x]³p~»ùFj(x Õ`3¿Ç“b¯®¯½‰€sBó!*ÈÑDÀ@J¼¶j°ñ»ð`~ƒ §Æ„2ãøš†ð»´ÆÌo6 d¦á…WƒNÍY\Aó`ÀçA(¸lÝÚ „"¬IxÑ :6¨*È¡HxÑÕ k‹t¹‚Š„—¬ s“~Â5nÅ£¹`ÝûD÷sê.·ôЩdU0“ðbôÑ+7XØ”^ª‚^º5AÁe¹‚\ÂK­ýô W)^¨‚ž:Ö+d>Íe*èkdB¥‚L‹¬ý í¨TÐxE¾x°Ž‚äsf4ž?ü H‚GR¥à”ij ³CÖQüˆ­1Î >GÄ®`< fe‚^ ®×–»ÏÝÇE=M ¡jíæ`Áì‘ü³^¯—eçaü/Œ_½œbl Uo3‚rÌÇLÀqi9†4¦à^ŠšHd þßûÏ9!…àˆ|£ /¤û­óíbóuÉ>Œmu!HxåØ¿€oqºÝ1ú­¼( /Àý xÿö÷[º%DÁ,+Ê„KxþåØ¿€Iò+™p)XŠ2“ðÜ˱cö¾6`Ž–òXyQžîμûP‚K¸ÙKh)Ê»³.Ç'$LÒÉæ0½¼(ïθŸP@p®IE¹Øwgk„'Iø$\¬‹'éð¦Â³Ä‰d&$üYfÁu©‚gú<>¹€LBæÔL¶Kâò•Ô†wÓϳ›)Û‚€0˜¤d«*vl⻳+É­ȼëY^¼w»]ÄÜ“ô{ýÓ¡%Ù“pÄ$œ(Çrµa¼S,p·Û}jböRÖÖ$!áp²Tš%¹ Ï.[A€[¦wVÎhO@&a0›¤«—íF;jkô:`ÊÌ‘ýX̰£m ȪÂáãóK:ZmµêÐÖè¥`Çw|pÃ>íöºÚ4º]Éàé6þIHºi†hm{Í`®§¢è©*f›h[Àø&ù@)LS¢¢¥Ýp¼<Á^ŃzíêØº€qü…$ ‡)!Š!BI.¯nW10ž0m­ÿÓ¶€òÚ]ç bº ¬4g§×Ö>e+â úá©0GS^v÷Èz¶eu ¸ˆ¬0“7Dè8>­YÉÆ…Т2ÿÝ"¯ýl·r$Iß0 Yi&ÿ¼¿/grRZGpZmŒ=´ ååOž˜ùGÊSÝœµØ`÷@DV˜ƒ·íö­è¼²´§VyÏÜ3 ’ûô÷óÓ/0EÝO”-aë’rÞ\]²»³=¶ígû# Ãðù÷ïç4…J‘¬˜“c¾³”j´^—ɺa•×®½õl¯d^"srˆXTà ùcµ"Š9n,ß-k¨]âÕˆcÓÈ~¶Ÿîp!W«g2ÙnýúþkYþå ,§-­ä•gû%`9’dµúóœ>oÀ ·¹Â ˜ä¿¥ÀÁÆÎÈí¸{z›ƒ’ر‰Ô“/ñíSéWƒøÖêå\JhÇýÓÛÓ\= êIæþ°'ÎËJüòg î7y¹¹ýYðž"ð¿ÅËÒËþL~%ågÏIÀŸ¬B\–|€v²"pQKååKà$¬rMTh)Ã`©y9«3Ð ÙNVvööùåKéÙßésºixör$CÙJQxv9!åòÚmìJ,ÐKæ`.KÏ^NØO U9Í!l~Öž°ÃeO–ðñ—­¥Z.Øðè¨Õª)‰¢ò³!±œ$!ÊS¦0­YÂb["ëýDÖË6L³µˆ@Bký`½Ï ©í¬rh­æ¬¤¡ÊCËÙ¹¦wR‘°ŸÁÖì«'Ð@  ¾ÝãËA˜ý ÷B#BÈ7Á$e,×.2ñ e ÊßÍN׉½…š î_ ²5_ßå¢Á´ Cf‚”ÿ0ˆQŠ oïEÜörˆ@ ˆFð5]†IÌ=¤CÈÂK*KR´«ê ŒÀO:<§¨tžx@õ?K}ѹøOE‡t¤€¡\íÐâ­:Ãêó´ìÙºäú…Ù aÓ©@6 ŒX¼ƒƒß?垨tG¹CJ)oH/kÂ#uæpãTØ,1JhÁ²îoœRù}*‘¹ÞK˜y/&Ä ¸e& p¨¤SOÁïfˆÄƒÊæxáT ož]ž}.u¦æ$³@'lžŽ´@îÇE²S€d+ÄÅ—ž»÷Ò~a¿q:UÀ:@\7šÏúѱñ”ޝ À>jö:#ûW‰?™ÄCRyTVºO® { G¢_»A9÷—yÒjèTÂ2àdïI(ßsm¢É&󄑦RG®gDê»aٌῑ&Ipd³ùÁT<Ú©Ìü[8æý[þEbæPS Á¿OÊo‡Ëpœ&,ˆ&È, R~çx§2³@1”OÜ}á¨G¡óx"¬kãÿ*µEÂê2ï’IEND®B`‚guugelhupf/doc/images/hashing/Max2.png100644 1751 0 15111 7515105045 17353 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿÿÿÿÿ‚úèÐtRNS@æØfßIDATxœí]ˮ㶲%aÙžRâ %;SíÎHïÜ©€8Órpüû—ER²Þ¢ÄâCWÒ~ÒÜKKÅbñMÈZPùÈVÿ¡þc¦¬~š&4Îñ‚ž#~ÂÛ/GªÎ+úª—$, žsœ•<‚«ž½š*á ýO“ÛL²å9žflr…ŽÓ$I¾<ÇÙë®1­yób&‹·k3»Ž‹ÞÍq4e;aöMÙI8î{»w†›ýiyš%$ðÖ¿Ÿ#:^oÝ:ðžA…"nga£Àçå•qr+fVù›&œÿÓk.ë¿cÃßY€²õôÿG°`â×÷Ú☭ ñÒ:È)¥9%,§9£ÆÞ«‘…al¶ &±™D¹8ë9<Ðs4ÆLÓÀ|§-/àÕ˜Ã$nƒ¸98æCRÙ Î çdn¼¦pr5Æžš&ünš03þÛŠ˜ñOHêGÓtùi–òLn%é\£0Jøàðx5µ¼ógy"ÜŒ‹H@#ÕX¸°ÞO仼ú”²•™Hÿ’TT²>à£ð[ ôôZÐÐc”F9ˆNá !K–‹\E‘cL¶“a‰!Þ"… †8Æá%Ÿq*¬:ÃçF7üÏ §N}gˆ“¡‡d Og ËišÍí`ÜÍ QTÜûpì 3ûv#k7ú¹}ùkk˜Ù»ÅÎMáÖn±“¡ytn˜¡<'ºXWehÜÆêCF?~JŠÍîgx²ó‹È—L37¶úV2˜Ù =7CŻêèr èc?%v†ø£SáGPæ°…ñäØý솩û!¶iŸ0Šs§ÃÞ´[5"RO{½^ƒÀšNáøÓ3°3¸ÐF‰›Ý ßkG?÷éì`Ž20ư﷮isl†ŽŒ<,M›­ìa îbú(*¹:8a‡L=ÖvPSYˆ8<8ƒœ¡dKÜ -jªÑYMkïóp†(§[¸h^”¨¹ýzC.~!ªËáß-®y¨àû¶dè•)·)ÏC€·&à¿“¤DÎo (ñ³Doa7ZJËY(ÀÎÛGœþFÎ|Gwc9Ãv†ç%Á6ÇËÂg›g56¹µ[ªƒr~îÀ@øÝ¹(^ÅãÈÌ…àò±’8žUŽâæ\»¡vâ•ä*-ƒ¦œ„¡ú‰ääËék˜ÚA¾óK©±ÜŠ-}ç°ÔåñüçòS®.ÆÊW-,¿¾`}3ªÑÐGÛ¢ÇP °jrM¡cÔd’ë½0Ȱ~•›|—` &ð÷Ç­FÆÖQܺ=BzVM^]×v&h]$ËLš˱ö$*JaýÍ«œòEs„¹šÁîym ®µC²„«,óÔãL³í oÈ„%Rø'^Ãså!Yý ÒÈÉ]2µG‚LŽÅ«çʶYý!•Å«4Œõo±À¹€ðIq4ªì9r$‚¶èÄïá³Â ^þ @c}‚å%* ‡ ITs$‡–¾ILaˆ`TÓ‹ý`é™Äž£’pHÁ‰ÈÓ ÆãiFêâxÞÓ úüîj?ÓX<ÍÐÄõ‰³Sð‹„C&ȉ,&qH8DPN~D2 b®žJ4Æ1HÐÉìë•vƒÜ/‰)  ÏqÄ4# '7¨#ð4#35  Ǫb®&„—pŒ –°ôÇdÁÌíCy‚à ŠIéɦÂÁ($œ"(­0t1™ ¨¡ ‡ô40 1I0¸„³Ã·Gpg=Mð¾y7Ó¨ ¿B4öVç†.&33Ù ØÓL‚ƒ„K¶Ãë Ü%žÏ¿î÷×à%W_žL ËÁÄìÜí=þü¿×ëõìàkRTÏ£—WÙùD0æS¿?Ëò©¯ÃœA"h‹DÐFÍv9ègµöwï¯r‚xº "Œ.,Z°þ½»0|4â<†nú–7LIˆõa¨Õnjš˜œ¯<ÐK÷`ÔÔR±Hè (Š[.·KÊ)“ûÉÈ}oÞƒñ™hìÈ­nÀÆ«]—Ä/Þ.]4° |"~)#úµC¿ˆÆ…)cLŒÆàçLdû8Íb¾…Åg瘅Ÿ›0ÑòûÉÀ À¥k²¢ˆ`ePB41HÆÆ¿óŒœÊ²#cê\>“jÕ«Þ¹û›tÚD¥öÊÈ@¿ÓÛ•)‚£æß–yߺLlÐhì|CZi|±ÇH夹\þGXã×Ñ(=«nn>Ü KHØ¢¦ ]jèn¢ah‚Ôõ[k}³4´ÅÆ FÿO ±F­ƒI‚×Wø­÷·­` {`N ¾/À¬› _Œg–^HLaÎQ7‚ápãU]¥dš`GÏ ^Jæ/%3Áp¦`ù¸ón&°Î ¿Oìæuð½€7¯`ð-DçwÕ·8¬«ž$ƒÇ¡]õ,ÁÐ0 ¶”Ì =¾or‹ƒºêùRt—Y‚r%{P#|56–"°6 óÝBï¥Lë§-Cd÷û)t)™&j#«f Þ—’‚°=OØR2gƒAöôhbÆQW[¢‡3ÂùªnéÚ2dÌä$°Î+x·ì9‚z5\¸–“A£)ìŽè³¹z*ÝÒ‡ÉÄà ¥d–`è“ Lúfn!]õ1BS7#=¡K"c0#˜…ë 6U0Ø/†y°°ß8`•[‘9$2ÃS²`M“àär£S ³?hý<-!—¥S.ƒ0ŸTè$S‚ª:Ž˜`°sšŒ ò@'ä,˜Ø¦7ݘ`¨”ì`jTÆU/U°tAb æ³0¥d‘‚!JÉ‚A>”ûð÷w5tŒE·øæs y%Ó=8 CpA¼ð·8bƒ„\ô½–~%Þ;2éLS¤.K7LFh®`€Ã{`÷¦Fý6>Î*“Õ±×±ãE V'þù,& Ã-5#Ó§§YFPdVº`2‚¥«÷b¼ —>ËñRõáþŠÉB‚ÕÅd©‚§?Twº7 —ü¦¶‚/ñ™Œ`± ~z.ÇK f…$è¯/&xÿ.ŸK_Ådqß ÷w,“Äb‚ztÛÛ©˜Ë{·Ô oå¸î埆Ðð>µÚt¢.}•ãõ˜¥ŸbbÓÃ꥘¬$øðvðéJ‚þÆLÖÞbo5òZ‚ÞgWÔž‡b²’`uê©ûb²öƒ„^ζ]KÐÛÁ±«5G$1…Õ¡}磯¯ê¸xr]LÖÌTÔ庘Ø 7âáøb‚^W«í›WcEðDþÄâ1 »)RB·{Ú”S[sªaG0ƒã9ÝŽ/ZÎúø¦æÐ;dhI0»¹»lçÍ|»¨àßC[‚Y5ÄCë™G¼j@9bhM0«ÎÏv5ØÏݪO9uÃcrÙC7’0D øüQêW."üýƒWQ†¿=˺'Ÿ!Š‚÷gý!Á¿î%'Wï°"MåïЙ!ÖVþî÷ÇeH;Ï«Áßûì¡2\: 1 9AL†oål7e’ õ¹íˆÍÄyÔ™t×RDÄÝè1'zëœJ© EÔ™è\U(›Ú!ÝfÜ©òßõó—ÜuEDä¹ü\=…œÛ€ "öbu“Å-~Ê'{‡ƒMP^`ƒÚã¼Ô4‘S±r¢=úrÆö/ÊmCa)Š"‚äGãu)á>ŸÖ2Ä'Øê3ÔÕóOy‹Ok6Mu°"çÌ›ïô}þ÷%\.£‹%CÞ|§ KݶTF7kšxëÜgA°®ZÉè„àYn Ø@©ª•º«sŒnw Xj\žºD×Õ_q3+׎–­UÝ!÷;ןˆ¶©ÂûN‡$¨ñ g+)¡4?K}§¿\Õ9“ð´ðïóÏÏ¿?ï€ßŸOñø’¯¹úrrWiß+Ï ª0Éòú“ÿä`ú yÿ>z?·tòóŸ×ë¿Ï¾zÉ®í¬œÎõ% ŠÓoÿöR…\|Ú gi/Q\«c£'8ÐЊ‹àA[$‚¶Hm‘Ú"´Å4A3úk{·~7½)SLIÜGT“h¨Þ6hßÖ hÏz?dý¥þýÜéb§2D°‚½Qµ¡Zúy-5³Ã5øC½ß ŒöGû–`Œœ&]íë# õ(¸o&·î`¹r”l¨ *4dLîÛÁ(ÍÉÂ݂܃ŠÚŽbð(w‘Ñ*&$$ ž…`²Ô•1‘@C6æ'ò¼8joWgV^À+6ÂnØÄéE!r×™*Ń×ß™e÷;*‚9Ä´À6o4`¤\8d*¯´ \`HŒ©¿Á le@0WÎqRAÙ€éež9cF† µáOÊÏ)èÅÚ †JT]ˆÎT3,!ĦòVÀß úDDS‚ÎQ?|=!Á/Uø:±Pד…ƒp„âniÔ“LÀ‘‹æSž, œ®âK8F7<³ð¼r!ýYéï.âÆ1”—XÜIYÿ®/`7¯<GèÊH ~¤2&adlÈ”Hõ{•®%—atûû\v§¼ó©,°õÙÞÐ.2‹3U¼YãÂY£Û¿J[¥iä¥ÿ‘–€2ý-R_\Ãóúâßb ¨ÒÒŽ€ÏÌg·Ø¡}øE0!PÉíÝé:…ìBˆiÄgk ªç ¹ö.°I@c$-‘´DÐI@KXèû©8±^À×+)H’€ÖX-àE(˜ñm,ðâû¸8aQ‰x;ê#jØXà3 håÉ%•aË@ÚÛ‰GÃJ@—E »¦\2AÏɉ­€É× ¨÷Ö9¼ ÚX ìLåñÑ8a# ”a¯'Ç«Z8™ ¥€©±)’±ðè&h' ¬FŽm‚–£rÉ-L‘Œí¸ðá# m‚«<Áîêz×ð#› ­€‡7Akn‚Ö=’±ªD䦭”Çé¼ ÓεÌÁ4Œ‘&xìj¤# ¥‹fé'´0™ mK$™àj3õLP xT´þS&eø¨&hc¬ðè&h#`¾MðŒ v¤Ë–9tÀh‚}-#h™à1d —9t/˜FÞɜΑ”&¨<† bï¥ÏÁT` Ø4ÁCD2è§9ðƒ™ º€G3AüóDøÛPà (MP x„HÆÁ‰6\˜àq"Ë]œ©Äd‚.<” :9Õ‹ T–ן¤´Ë,r8LP—áݼäæ\9^™àk÷GWIYWF[ß&(´Ì+r¸°6Á’üÜy‹ØÑÑ /¸ó>Rt üµz½ë•JÀ®böfu$³ó•ÎNwåï2¼k‡Glšàžw¸t&`Û÷Û¤sx@3W#;VС€9ÕeBŒ<ãƒË#ÂÛ&¸S7èRÀ– îUA§‡Ô7MPâ]*èTÀŽ îÒ :P˜à[À*èVÀ¬øõâV½+÷¨ [É·¢!àeƒLŽü¥øãZÀ]*èXÀâÛEà –ûSб€°Ñ/oÕÄ{sƒ®”ÿ5${Su™C\üÏÓrxW vgé[-sèƒëµ‚`‚»rƒ¸Ë†ÁI«kp_ :ö Í1:Yˆw¤ Û£œð°7èGÀ+èIÀÖ”·ö¢ /› JÜ‹ô&`SÁv¢ ?wª GåP±ÞcP¾Ý…ô*à[ÁR¾Ýƒ‚~Ü¡‚ž” ªãL;pƒ¾¬,å»í+è]@¡àmO ú°RPâÍ»ÁŠ€P*XªwW0„€»R0ˆ€ZA]ˆ·íÃ($R½Ù´‚ì(¸a C ¨üS½¾nx=X0•‚%¼º¾6¼¢.œ€" l(H¶ª`HAÁ‹ìZ>p›,s¶(aX¥‚çÆûíIX@òM„ÓM7'ah…‚%9·$ÜVÓ.¸€JÁÖ'×-I^@’A!~´>Ú„H2¨G>ÛŸmFÂ(üë«ø«SŒ7#aϯ_éãH‡€__÷gÙ+Æ›0 ¿ Q„9y>Ï]#Œ?,ŒA@Œryö>Ž\ˆT{ñŸûF-aËÔeKq >úv±„Ž—9,Wµñ*•¢•°oW¨C¶†¯k”"Få5²wHØÐðg¿Œ‚+|‡„•† à5¾ÝãP¸ÂfËî)+]„#Ó0V…+lÕ—çGQ|èµÛ1i­€¢·[vÅÇó½{‰¦F‰X@B¾7°øø–â€Ä¢aÔ’¬¡`U„ßó©øíõ¯RmÄ- h—ð[¢pˆ'°IY¼ëÞ»€äücðãVkï &ð¸ÞÒP;D% O7 9CG× Z-fа(þ­Äó¤âF8*a×!vvžw¯â6-»;€û¬ óë50UΩŠœ ¿« @†Q9Dmý#\©¸¡ƒæ¡Ìp,h[µ–ÕýÞ¥âIGâ(ØŽ€üóѰÀ‘-bÙú``öpQSÀO‡z’з•¼ßÿºÿþ(›?íè ~ƒ¥âvì¼kœ+I)ËGÉëoZJ¾M1ÒýR'²,Ð;°‰¿…ŽeÃLõO. %© ô¥ø­øp§ífÌ—xWÎÿÅ- hØ´•}¡ÍôO!¤Œt²ú›lXÉ¢ø*¾ *î x¿ÿ ßÐË*®¯ õÛîô?óYíN@3Œ.ì©%ýÏýþíõú—ÏçuDaSõËl*ÃÉŽ( ¸ÐÏ'KŽ!ù@?HZ" h‰$ %’€–HZ" h‰$ %’€–HZ" h ‹Ó†µïÂwV¡©/8Œ 8•KE6FòùD”ä© ³¢óe#7)xÚÑÏfeð÷lÀzGÛ€yC™É½0ceâd¶ëm?CªfYä%.z>‘QNÎõKHHHHHHHX‰¤ŒV´þ€vPët mHí(£J(ø$W2ý–éÁ·‡ˆµÑ¶ ÖM‹Z¶ ;¼Ä !KN… ’\þ/ _Ð<×o‰|­dMú%$$$¸ ð‰U–æ0X"2’Î"0?ÐæÐMÉê ³„6뱡:«"ˆ`leZ@&Ç )ÍÙ;1«Ó ‰ÂcµˆŽÉQ3Ú¨}d±jÅɰb¨ú¥ºþ§ò¯ÃŸ†¨A]ÜõB_è+V.΢ZtD­¯žRýÓ:BV12Ë¡çŸv;ÿŸOµìK ¨ƒíEY¬Wð&GruõTSªžÊ?£M‚xÍî'•Š¯Ùª,´B,Rpù†ªä*àS q{ò¡!eRþtMk|`BB±ÑNs›„Zp'ªÐ\M:Ò•d£ªtHmjíçOÁ'2 ËÉšXì˜P(Ã"Hƒ’kc±C"ùÀü?rmÛ`9oßIEND®B`‚guugelhupf/doc/images/hashing/Variance0.png100644 1751 0 20147 7515105045 20361 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿÿÿÿÿÿÿÿ·±OÅPtRNS@æØf÷IDATxœí]K¯ã6š%aË]w'a‚Áü T©¤vn”r“ì¼È]ÌŽ@n{2»´ï²éÇšŒÿíð)ñ%Š)Yrñ$å+K4utøñÓ'¾@ `Ô¯,+Ä$c}ØÜš€öžé.!™Gß„x$ ò¼²ôGá™n?˜\ø™cP¾n”e`V;‰BÂÿá"ðÎÒÓÒ )K’\x@&H®`ƒ4w"aßÚP8{Ê#;éuôÏM þw#éÉ`ÛàÛ-/ó7&M©Nòòúúx¶ðlñ÷Âv')%[’Õ'Ì`G*æ'™? ¤Ö@ò²Z E太ؠE9ö‡ Å%|£;ÊØˆÇ,€TIØl¶"#ù:G_±2Á²¤ÿƒñÑSŽÍfEªŠ£Â7H¹%níyÝ1IÉ\ö©â^· ÿûÿ&9Å §èCtç—ðkÏSû_6蹕ٰN‚ ØþV@ƒ)„ïñÖ\®²ÞqTÒ‡‹¡S!òg¸x$NHl¸ò ±¡6­ý¢òqØp9[›S3>€Ã÷ç—¿bA‰Õòçt±š Æa¾gÑÜž„eôgZ²R„z–胃¨‘±Ê¦"$ÈÛ "”ªLð ”^ßCUv'­ÈˆÑc™‘}×, $$Æ¡Ψ$ðs+¬H[aF4ÄËÈȈÄDÕ¨ž&ÛŒ ?”̰ÛVª§ÔQÎ0M–ÇÒéŸÞ`ÏÝsStaOtÍ(a^›ÔÍ&Eú6ƒâ1²dÒR$ ï’æˆCûä*"KAuìwŒ"™‡ ïm;ƒÊZ‹mì*ޏìö'=å2Ê w=ÛYº’%½b$¾õvý„iÙæçŠ‹§ óijØ~Ø‹ ·MÓk9‘a-í ŸâOÅðÑ,d0Ðö7üñ‡Oøã Äh–W:šåwe ÖÂF³ê‘žµ‘ÑKÔjç» ŸÛ£¥ƒ#äa5Ú6]†•‘^MÔž,Ï B„UÉÆ¤™Dò+'g36µ>Q~»…F³'¬Ú±`°o^ݦ†t €bþ–x0eö§P1$òR÷¬é„4ÀÇQ´Š=±«²íùCnœ!ÕJ´ؼF: ŽdHÇ,iƒÃV;‚iÛ@Qîœ<¾Ÿ‘©øk«* ¨eØ•kŠ¬ØØ¤VUD%+;/ý%¢"Dgk’E3ج͊笺Yœt"&-V.s2¤…Ŭ@l•e÷­;”4·À²ý7u·&‘‘‘‘‘‘‘‘‘‘±f,;ä×Pç¯±È ÆbÖŽl„±ÈFl„±ÈFl„±X´^—·X]^½íE§#Lµ4Úd Γ‰ g5§bé3HŠÙ׃³hO˜nÄcX¼8<á­p! e¼#´W÷ÍdV·ÝÛëºßþƲœzÜ'àe9Fèdì¹¹³yðd®ô6ÃÞ¡ŠJ=Žš1$#D–Ã7®Êr=FöÑäó1´¾°è¢xB4È B›»Ùs†;0ÉŒ¥x OÛ¼­³¹CýinîÅ«sä¾¹SÅ®c<5n÷P_€+hØ:˜^Os3 ¯Úš¸è&,œ ÷;Ô~[–³fuX¬Ñ x Aa¸, 9þKþ‚úÓÍí5ÃÏW$eƒJuw_¬àŒjošmPìKp›?›W©]¹“N%b߸V¤›Å½CG—ð¢»M(6׋ã|¦(WÚ‹u¯E’¥€Naƒ7C‚™<R¾1œ”b_Þx“QlK8:z™º ‘¾cßE_žä'©Ðý×ýPKkøê›ZÆÂµ^߃â})IýDfßÙ7jÜŽéßïå:µJÑ‘PÃäþ»ƒLÑwŠøþ2‘’ÃÆÕ4v§xÂ\‡ýtŒëG›ƒºc{hšmS÷¼ì†ܹE%ÙSÌS¨¨ÛÇFùzñǶiºu4èÑ-ù­~‚.éÄ@ýç; оO]„ [wƒ,½AñËËIhôä„ম/-Ám»ôH·IóÌÉ“Ü8o‘ÁÓŸ$^OCÅß‘h7~xa§ûþ·†/\Âð©=†±¹²ãÍÛ†,2²Wr¾@.'ÖKtÀÛÅãw6bŽWK4ÍÙÆ¿~ûŽïúåï|ã‡~ìßÎa_Õoù±÷Ÿõ ¾ê6qìÙ8ö(ŽÍ6‚ß4ÔÝüØóÿ ý؇Gà‚s&o©ý•~föª•ÊBìoÿré¼y¢yFrÐ%Ø@úB" A +ùK¦J¾ ÆÒ8fÜäîYÑ•~*Ö}L>ʲ’Í„„dWr³'«¶ÐõGð'¤ì…]dq1’:ÒÀÉàElÁvøJÑœaó>–Ôº°IPc$¯ÇÖB>Jl%¬/÷ú©$6HL-.cÐõz½°4ôåuâev„]èDìŽãà¶ÌÌxÈ)þ!Ñ•ú Ïsoâãèè‡}1Ç׬ØêjºÒ!] ÿ¸5Dfƒ1‹ÚXž× x=l/²G4cs*mÇ”%ç_p‹>¶|=Е˨'®Ä1öDÑ$ %XÑ­ùò5ùZ‡PRŒí…<KÙ¥™™ ?½FPlëç-8R¼¼«îuÄ”X–’® ˜1Ì¿A^N,# Jh¼ P‚ᅵßbÛ!\7ÔE ŒE&‹L0™`,2ÁXd‚±Èc‘ Æ"ŒE&‹L0™`,2ÁXd‚±Èc‘ Æb-ß\ÑFô„K'ÈÐ\ÐO=¯„ÊL<‰ 6M]&5¯šžÌ*¡«w~FmV ¡LK"¸˜· õTq£ÛÁV»‚rÀeÝŒŠºìYg¬&^El 6Ÿ„ͼd‚î]Í'aŸ£¦ØÏÍÆg/Áöìnv7ô‚¾Žú†p1ó3vœíEa½ > <»ÏTð~°‡ÉLŽ¿ó46ô:êö}JÍôY§£v:Âùv°13As±äùvU‚ê«gþËùl°  ~†Ët³×鹋Xr„6†s”±‹ ½s•Ð \¬ðõƒÎõ¤§ÄÁýÍWct|PáL³6¢Kà&ØÇnz•‘è•ΰ¨¿f„ ²ý~rO#Æò[Ñ/Îw0PL.¡»’´·3úŠyj Ú"FàVµÄMðÖo¯ÃŽúæ/už®Ò¸š¸ þ“»jF°‡æ´f }pZ ÝÅ£'b_‘=I%tüþMIÁá"¾q¼0@ÐÕ]2œ ÞRÝ*‡æ ¤b@Aödw˵s^/·nÆt<>°·ï"±£WÊé|¡› `ãSÐp>“ùB/ì€úò™LBAqR/#œJB‚’åõ×ç©$ô ü¼ÌDºÚN‰zsšHB‚ÈÏN#¡O#iÛõràx68v'¼åPB‚Š3DS²±À£ˆwà"!rdüÞ98Š“íúŽå_Ü„ ÆçnÓå 1ô{ªó”p^•W@ûá¼6¨¸5_+Lï =Ü77{zê'ØUb‚«÷ÈåÄ¥ìKð½7ÁÄ…ì;ŽZV¹³L˰w9í4›}g…CñaÒB&ˆèç^{™ílðm~ÛoÞ@ž²{ jå¤.ð‚rõ}¬ ZÕ|JIg†ƒEl炲MWÈ}-gŠ«“1 ЊTü¦Á`£n3ìÑ$àVþ°†¸4 ûZ«aàÓ]†C ª•8°-3Ã0‚¡=w öìË9 dàùÄr ì “C÷[Ú†»ì‡0Dé;Â$,¢'»õìϵ“pçÁqÃMíK/w¾ý'Q¥N°¾¸[SÌV‚m8·3Ç5n>¿—¾!ÏÓŒgèVÐbfPËOñÈó<£‹ÙMYʰV|?Ñ"Ž0ŠáHŠ.ì¯ãŽš¹êRÐqf%®q¤ÓNq쨥5Éç¦'ð؈ØOpÀÇm.ê0fêÇa¤ý‡N¶1ß‘|2ݦ ¬`ÈLïñ{l/ëEäc€&+boŽ}õ6Ù`‚µÞ&‡Ä¯é;Œà×üÐGPÏÞ Ø`‚ÛƒAQQrÀ’™®A÷~E¼=`‚ÍA¢'Џ‘c+Iä¦8´†•àöñ ïÞ¸‚[Êë5›[u›FùUp7H“÷Xº ¬w§çMLTÚÒ“Š»2ÞcXc#áÀSq¦ÛG‘út4{¹>u>²³O»‚D%‚ç¦y³¯çß(ßWOx÷7¿òD?^Ùïê·Í‡º®¡à– ÿÂϦûìN§ŽswL"h*ßåß’øÃù#?vþM&ÿ³ý½æ´¿~Ë6þÚ4t]c¯ÉÐPòoÅ;ö¶%ßÃB6‚ޤ6 ßø—󿳯¾yËw}ó+ßøþüG¶ñoõ_x¢÷"Q›AÓðD_‰ 騳qlümMû?ÿʯ{÷Ê7¶õ[qìßøPD=ƒ¦1ŽÍ†–ÄóOH'Ñ{k³š[¥B}ƒ¾#[ÝhÙ•m¢Rl™¼ô½#ØZ@G°K&òhÏÝ”zòî„í!ØfОƸ”ým¶°}ÒjRêÂÒ+ÐUЩ`FFF†Ôq” ó*ØaUËZ:„KŒ¦DmÈ%Ï r¿€|ìÆ" B“L¾add¤iðƒôÑBèð U鑦’«,•Æ3„½!”ÃX#ò$[ ‚%Žñv©¥ìÈm9†¶ ”ôHEVHNŽ7äéÕäéRØŸÆUÌ%Ç'~´*Õ“3yÈw:ŒÎø&:“û˹cHÓ”Î46‚saw˹„£qæÏà¥6 •»4úùÊ— R–ќؕÕbZÙKêCLÔô\Ÿvù;ùϤ¢³¨­Ä‘„¼º4âZè××Wבï=Žîz½R_IÛ È'¤Ä;µM|.sô¹Rr–!†¤å^99?ñH”Rɲ søÛð‰Åp°êuttT!Èü0WVòƒ¾S°ó¹N¦ÜWz$ÒÐ"„Ïë½²/#äٵâõÒÌ1 ùÙW¶»¶Áv+† í½ 9;|‰õ¨ƒ¥^oY|%õ ù1É>¨S?H~WÆ0$ 2qú}\~6‹¯¤G+z²3¬ ð132’ ¯«Ï@a%—zRXiÏYsw¾¾¶CÊ" žwDçãjÀ¤¤A*UÙ_~ë¾ótBbmÆqVƒ.‘¹²ïÞ H.°â‘ô&[µ"ò]¥b¤Ñ…§‘ó‚@ÊC*ˆ^w±fˆ‹ë,¶/‰a§-U¥}P³ä{´@÷îï'G0##Øã×Ý{ÝéIC%¹7f¿4P4bÀìÛ½Ñûô9+‹# ‰,`$²€‘ÈF" ‰,`$²€‘ÈF" ‰,`$²€‘ÈF" ‰,`$²€‘ÈF" ‰,`$²€‘ÈF" ‰,`$²€‘ÈF" ‰,`$²€‘ÈF" ‰,`$²€‘ÈF" ‰,`$¡¤AæY@OÈBÁJd~sô¦Ô¾ˆy" œo¦5ÁÛnöy=¼¬Á~“mÐD€n²ZÆdýl‰s ¶ ‰_nùe ‰ gá\‡Md#$`®Ã&²€‘kÌuØ@0aæ:l ‰À>‘\‡ud#(`®Ã:²€‘íÎuXC0¡æ:¬! ‰à±1¹«ÈF"XÀ\‡Ud#>>0×aYÀH„ ˜ë°‚,`$¼ð¿¹Ëðð¼" (CdÞ?Äëwe6˜ë° mŒtÿ(ýÎù¿/Ê(}÷Rð—a'xOÇ/XÀ¡ýBÀ~<äßÓ—¤aˆ€ü¯CÀ#¯Â_ŽˆAqà`.”o䥿äu—w-fZM¥¨ˆY@Ž¡:\XöñÞ, 7Á>mBá}ÅÝ h™Wè°§Û ð‹ð¶WœÛuØ.`¿Nw¬ ³@Ó{ª¶«[ ã~¤BY^<=B@—H.q× ø4aË~ uØ­ÑÝ*X…… Z¨¥÷ª Ð×… šuxP ;UWaë~z¾MÜç„ `}uØÇ¾îRÁÐ*,LPÐKœ{T0,}uØÓÁÝ¡‚L(ÿ»°¨Ãš€žÊÜá$¼_ØV‡½…¹?I÷GÐMÄ* Õ¼;Ã-ÐR‡CT¹77Èž…{öÛÁLP0H”;S04–:X-ïKÁà@ˆ:Ü ªÈ])H£‡ÞŠâžî%Á40T Ÿ² ÿÝ Âi ÕaU 4|Ê‚á€Ä¶µ*2ä*¸; Ÿ’ˆ÷äEn `BÁ *¬ÔaÕ–vù÷nn$£,Õa& ª;Ïß‹‚#žD€\‡5g†¼¼ Å(+ *ÃŽÿóÁÜGÆ ØÖaM…ïDÁ‘RÜõµ>¸ ½™«àuØb€&xnPÊ9FZÁ…ŒB}zRã9.ò>û(¨Uú¯¥M°¨‹Í×ÊNÄþø›à(8ÒR÷ÅÞ*`€ ®_Á±â:\ÔGÕwÆÆ0V#-àƒé;Ý?µ+8Z@b‚Zï&j·Lpí ÆHü—Ô9'«†(¬[Áñ>Ð —LP0Ä×}#/ ø'ýìLÉ•/CX³‚²ËîLÉ•/žY­ã,´îMµÖÕá5Ï…ˆP¢ I†BXÐYž!?XÆ xÔÆZ"õpàmdµóÁF XhC ÁP‹Ï‹P1ACÀ|2vVƒÑé2 t“š 2R˜{ÜXg@#`»Ž1Ad¤»ƒ•Öâ±s‘B±©…‚Ù¬PÁ±’KÅU¸m›àŒ@ R@¶DÖ~ù®#ì>Sð5Õ\‚#”.“*h—jŒ€kS0^@ª ²ÏaG#­LÁqª!VÙסe‚ëRpœ€Ú%>ìú–Q@áŒÌì$KKi‚kz(% q}è’ÖW¤à(*†À%­ ®GÁ$•.‰Mp-s!ÆhÖ`òqIìW2bŒ€–LpIi‚«ÊŸB@adŸ­©w(Œ°Xƒ‚#4jV[K“zAzªåG„#ì©Á›‹~€2wûbñF˜T@°M¬Êᱡ ÅÒï$áö×`l‚íbÓü'ߨ±ìj. ÃÁÃ{‹€ Ê ‚…¡6JßcŒ´K@ðùýÅâc\²BõK' eE7 W ›ý¾S(¸Ü{‰jåð<Ãdk°Ùì'Qp±M°tÖ`¬^ À$ .ÕC4:R¾íI«Ìƒ‘ÈLŽe.é* ³3L¥à‘þ·4D ˆÌ4¸"[Œ :„+PÀ¡Ì1‰‚D¼ãâ$Œ°G“ilQX–„z¸@† \–„azÖ`‚)\’„“ 8­‚Ë‘0L@ïL0­‚K‘0HÀ½ :‚b C«§ô='3Ââxó¶® ƒj0AOÓŒÇ[^(Ðñ" Y؃¦´±(¸#Ý$C?ôíº·$' Ì,Ý$»DF(æÌßÒ C4k0>ÁæúÞh姆›ª3Üìž àˆŒ±©¯µÕ&¼3ܦ*{ h™xò°¾^ëýƒ]Âqƒú«òìgÔ[ãüº+i#u f’šáRT™ùÇrtÀG ¬ýéL8Žüm”³a¬€á·€^ “ÞM8ÍùªñÈih̹\ŽÊЂÎÎô†ÞQŽ®vu+1ã ¢¹È\RŒ0¦ÎÙ%< HûR9G¢Þ„Ì#Dqg¬®Ÿm#:°qÉZµÂ©ês°€ñ×µm0ôH? µGÍ,õD^(€°ÀVÆ”Áb¨€îêK¤9 ’ øÞ2P…^Rz÷…7¹sóê‡M?ãõÜ”,¦ÎÎ.y?/Õ­%@Àã ]&CÂ-9&ª0þ[ˆ¨É_¨AŽ[`ñÔä(£ô°ižOGFT“V>‹Š[±KñMó×fSv«£Ô1ÖQZ|àh·"`ób÷»úëú?Ö&’…Ø’ýt×Ö]±-vkÐfÜVG©•>fÚZ`ÛÏÒÝ|œã‘|zú‰^£r™õ//?ïõßyÜWH¢uMj4¿·p·âœ[NÛæÑq?ß 1{½©³ ³.+Å,mõx|yù³vE¦bM_ÔDoοž_ÔD¿¿¿ÖŸÔDï¯×où¶©È£¸f ~ýW¢ÖWҮϢtÛÒá0ýC¿€ÉÐ4¿éN©þ¶ÖÖ™yüöåšhûËùåYMôáßü®%"ש%Â*k‰œÞ´—¥êÛ\*¶Í:ýçæ¯ Röìp¢G5ÑÏXS5ÑïÒŠâñúþC»ü„€åÕiF¥†fE·ì*¼ ™WiTŒ¹+¶œ¼§« šÐdPY.Ù¦€¥± šÕß* AÖ’È’“ñF["¯ò2©Û²2\™©©?,o?4sóÐ4ÓRÿÍÍD+ÛU[©Jƒ¹=‘žyFFFFFFFÆüˆˆ‰ï¥ø(Û¥–ÿWvé2TPí*&ýKÞDÇ¿ò}ÍN_&ÊçS¢|††ôû"…g÷ÁùL”B%zgb7IéV9>}¢ â»0¾›ƒqY<°ÅqDÀã ‘A%_R„1jÒïb›hØàÈl´C !בêYÿ0Œ øûÄã70& 6êìBc@IÀà RÄ·äPiÆ·ô‡@/¡± $4èuBz9T†°0ŒY ÝQÌ‚@X dñ$ÿMxP), ô1³ á> Ô«2fEö_.R=æ›VŠ’ä2¸€e;¬rdüÆgö”%àíJ?l·5œÒ3wéRÚ“:œ ÑŽÇ"JgTü&Úñ¨€¿$ƒp¡òoŽkç¿Ð0ŠÀåcD„)ù±àX•bÌöÈøMX ±øµ vB{f†Èýå%/8y87ÙàÿóÚ.Õã¹JIEND®B`‚guugelhupf/doc/images/hashing/Variance1.png100644 1751 0 15720 7515105045 20363 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿÿÿÿÿÿÿ!œ¹tRNS@æØfcIDATxœí]MoÛ¸Ö&aÅÒRïï0 d2K]Dmf ãÞ,uÑ©'K©¼I¬ŸýòÔ7eÓ"%ËÎyÚ8²D3’‡ß„ å¯þàÏ-”L›4 H/€#¼Š€+Ý€™V0ûë1ò€ß­ÇxBÀÝ€ÇSG\ò¤ÐÕñ8Ê”YhǸMtÿôN7 Ùê\Ýk$Ä{á¿TbµäqÓ¥’BGÇÕÃ/!$m‡V“ò”‘ q·‡ŠˆÉ&^¯ÉÊË5’WÆï´îøÂ—ð —¢ü¥m‡AÇÖU~T^øÅ ßbvPJJü€>Õ.½Ú º¾bZXÊÄ«VKxc²°ý¯Ãê3­€N¦p,õ‚9¡†¿Çñ¨û—ã£!N/¥\½–C¤g´ÕüæÌwÑÓ‘.ŽŽ§s¿#‰V¤ ç-×=ìMr¬˜oæeúxXî—Çë§És’jQaò¾ODî7ù¤LÒú/[u¥þ0/h ølyQ¶ ¸{"¿ï°ïGÛïNh–öÐñ©29¥ßж÷xŒ Fˆ^_„þÀòçP”!Fx…Ôª}1Žá‘îÈÓ#Ôu%µ#´IQDh‘"Dèf)B„š-ý yµ¡5¾™c¯y\2Ôn+éFÂKb1½%ón%Ê»Õ68Ö#Œˆ…ÞÆW~øÙÆ,c7"tbf=™Q|íD&qb”Þ¬-VÙðèTyY´– ©*œ#£D'GÈ¢|l>=ÅWÌÌ<É-FÈñyA¶Y&$I 'DoØçXû2zId-¡Ñ»¡Ubz`/ëB÷`7ˆ~æ§?ÖOäs´É3ó6ݶ…h5+z¢oöÄ‘Œ~¥ü'x·î¡ZMÂñËxdgH AãÙ!"׆©¾fH¹[Lƒjæ ¥í;¼'HÌf*úÐØ³æÑR* F,i@1æ3o‹èÈ£?CI™Å)åéHe!ÑË«óõfèáP1@\,û—^#"Âö[/6Gì¢L[ã=㥰%ÇtDl=ÚD‘(Úu±·!/¶¸ÔK$NLjfcup%¶cô,% -ÓE{Rî±GƒõÉÍ‘%S,áˆÑ[›mWU³>iÕvÌr<óÑà¶¶KE͵P'À¶åp<©eÜØLWKÖ+iZÎl2t·¶ê¿[¢·È±½~Ï ÌÊÚN³‚•´¶+ÕÆ¢é8÷¶SúñÑtvG QDV‰m§vk»5´°îƒþm»æ¿%®ÕR‚Ä$"»7›1Â\žýKí­Î“#XFÄݹr¥”¥,Z¥iÆ¢U¬•Šö1bžË9p®…("gq¤©æò›#ˆYŒ7Ï™üҩ~°øœ‡¹yòÊ)2æTùÔ•õzÍø.‹é1æ±ò¹0¡Ç,æ'øyq§—!ÝÕ8íwqÁ—¿€+}ó)Ó zçæ=Ŭ rRBùLõ®œÜ£ê_á<“ªwÎ;„.ø|¿¶*Îåôˆ‹ [„í­R§3©æ v&h~IÚ·’pRÊ…¦…gç€ÓٸüÚ˜ÓTÄÕ¼s1ز|(2y•ñË­&EȵjNóâúð>`¶0%|¦’Oá‡]Ãï¢LôË÷ „á“»xè Šóå†(~5é³ví—!èyfô¡5IúÛF|½õìS š š š š š š š š š! ³oïÏ44444444Åœ RJ&ß ô$à0„ Á¡0[Ÿ³%ˆ ÚÂl Î>‰gO°@¹ê)·>¿ßåì«‚ ·ËÈ,×1–I¼=0g¸ ¶\pH’ŒÌHÂr²R-“$‹9JXŒÈËñsŦGMÁfwÏNÂf9˜¬f&asÂU?s“°¡ _ê4/ ;U]¶˜™„-‚Y†wçaÒƒŽ³àþZ,£]Z_Š=]o+/­.|ŠAgŸ3À‚¤Ìò´aè*èe[¹wv–%³Q@é°ò%RÌ3°BµGÍÞ<Íáܯ— û <­­lin†Þ6‰Øùà&›ŠHz:ÕÞ g®Yúd C¾5Uí<z“Ø!ᢡguû‰èo;©< ö¼ʆ{¼D‰À˹¼°#ö7¢4<Ãc].ˆ(*”é QsbQ¬•ž¡Î0„Ã’9 ¡°94ŒÜû‡]O_"êt¿Á*à•,m&Of‚Žwq&ÇA‹` ËþÉVâ´å~k‹è·ã1éÁ)]ÀIwܧ4ÄÓú¨ÂÅÊÍÈ”É|r'zÄ î­G12zùˆ„/iÖOÑÎÞ Æ!œ‡4$îö9[n6IëÙ–´˜—Ž“,ÂU ›$<¥|t€ù› ¸¥xÖ6¼2ȉ™[ [B¸»œ,º#š-Q4irînãEx³† ‘äY³©ŸZHjc‚±8ã9^¬×O7k¯Ê=A“-=ìÕ=|‰H³– Gäæ)oärÁÔM·'3a02gº¾»%<û@™|(2IukÍ:üì!Âí“¿?}ý*wJƒg9ßpÅ­’¿´„S –ÏíwQ=ëFU>ƒäçXðÎ&p¼^³—O`´ÿ¬ù&4^žÿ݉! O7ÑÁ/ñäUÊ‹‡/òÖ/w·E°‚̇w-é =Vûv÷P Õ­ÎEõÌWl˜2ûáÄå¡Ü&©0=q°×œl­Ø¿ÉׂÚÅî#„a®¾SRsnùþIÔ÷ËYpÝ0°ñl‚&Û QþBZËÖ~áåÓÁ"<¾‡±£³éè̆Á%À[ô¹uÁîIâ˜<¨Ðxñ™•°;à_ú`ƒ`‚¼äÕ5 °¿a3âÂÑcq¾ßÿlb@‘ ,;þ»¹D]& åís€*8Fäs¶š‹* FÑ e’P%qäDðkF¨$ǹLΤ}΂7—c1ûÝ­™Tw=#x™…ötæp¸G}±AüèÓ9aÿjˆåOÈ)ûinÎ’ÆÇ –’·s4Q4†ÂJ ½ä µÆê\YXŸ£¥7˜¸-.™œ¡AWdÕÝÍzâdÖŽý!<Ã!äšïŒK£šÓ´ê¨™Ö 5 ‡ò4žÔ µ§ð3Ò…W8©„ús¶çéÓ%XÏ$êã ù„Y<‘½$!“&²>AY%‹SA§“ð¤y3Ì ½M6“œ@ü®•7umRÔ\ ±ÊeûsªD¦åo)fÌ ŤŸ›‰2Êis·"Š¢f–~"­—𻦙¯~âì7g‘®V¢:QŸ:=ï1…*ùfš|ròüÁ”—†ÉTÔ8}‚cÍPQN‘Èf`2†«é ÃS ÂÁ×ø¯Él4‡5"‹í»Ì)ccØ$[Æp÷’ÀÕè‰n"'3c²XnIv€¡ñ`Ÿáb*û+ýí­ï¹˜ñlã՜ڛx',i?5Ÿ3n¾\ÃyLɼ Žn=Ûº©kÈÐÂz’‡¿£ÅŠù9”‹ËMõF¶¦fhƒ`±Ì²"O;18O6‚dÏ¡‚àA~&Ïé>•—ÓšMÚ[2“Û_ÌŸ8äQŒOP\|Îÿ\‹»g6ã™y;ÈÝ¥®p{9|d‚q, ŠžY¦æ§ç79¢û'f×í³‚é¾³´Ï©VÇ–]Ç`ŸÌãõóóZˆêÝÿ¯y^NLe›¶`Ù]}ˆ~õìPïv÷Y9x/2ˆúö¯Œí~Ç^ž ª,D=AhËp<|‰ÄÅò·b²ñK1Æá¥*‚Ÿ=Çÿ¿ÜÝÊ[a(/þ/ÿ_LK‡©Pªë™ëBѵA¿PšÒöñýî­î³òžOÛψ"Ðá-–PT„*P­;ò;TkYê@ÎÓÊÊüY}² AËU¿A5œVï>ë ^=+nÑR‡`¶'Î ˆù"ðeñ[Ç3Û…Biïó%~ J>¿V›œ”ièû¼x÷) ˆz}ýAY%äƒbð Ä „-@Ó•eŸg¸Ù‹—¸Ç´îÚŠpµÐ0¤â7v§í†é d-Ö  è3Ÿ‘½øÔoxÀ¬ýÊËÛCaxkÜçOðE)üq*‹@‰ÍfÃîó¦7L\A~x"/GyÐÀ÷».:w…‡òb«ÐeÚ<–qÃÃøèNŨqHe«Ú/þKÈ"¿ær¬¦«¤QˆÙà—¦Ñ¹§Ær}t(Q @…ð®p¸€÷‡ º1à¦[^~ñ¿%§ä‹"™±‚wÉ/«hp¤ :ãía¢ Ú—™úL8(ʈn·[½‹¸|píÍœS¿_Ù!«°W0ï=eí@ 8Ó¬%Ï»AÄoQyÕÞ‹p¥(÷¥›ÏîÜWñظwm¨ _ú²aâ‹ìí×¾¸/[å5 [„©Å%HC@þ-R~¹šå—¯ÄP (ÂÒ–€µ{>UÆsµ¸Bû˜( a4䮼ÐÐàžÓˆÏ¥Áçž™Eû PgXP( !P@C €†@ 4 hÐ( !P@C €†@ 4 hÐ( !P@C €†@ 4 hÐ( !P@C €†@ 4 hÐ( !P@C €†hKJQ@m4-o肳ôOAC@±„<X4 hÐ( !P@C €†@ 4 hÐ( !P@C €†@ 4 hÐ( !P@C €†@ 4 hÐ( !P@C €†@ 4 hдùFžæ€j£- ³ôOCK@<ÍáT`hкތMäRBOÀ›5?ÞÑ…¶>ÍäB¡- æa5¸€Aû®ªFT¤´­˜J@4A%„¶MPé¢ ªÀôÛ%¡R@4A¨øÑÈÂh‚Jœ…ÑUjY š 2 ·Ð# š`\@] Dìâ¤,Œ&Ø…¾#Í&؆(ÿôja‚&ØÅ©=Òh‚-PÈÁGËÀEÈÀ¯Ð[гÀEø›M°Ñnßíø[º“¢ 6¡çH{iºßÉk4Áôéå.Ýý”×h‚ h:ÒËÝÏÝB^£ Ö¡ëH/7[‰K4Á:4é%SÐ#RA4ÁÀìŒ)uËÀ8Ž?';© š` Âé‘,ìÀÏ’d²D¬ gLÀÈ!KW*ˆ&XA¯%ÂtØÏÒ#BA4Áš²20f¿—™(ÑKœÐa¡ š`Sº³"öã® š`“ú# òbMP¢5ÉüÈiûɸ‚h‚-FÅ —pÑN°Pp…&XàÔ1P0ã ¢ rœ¼Ì!"RA4AŽÓ׉DP'¬ÄYç€ m"Bö  š `€€¼\âÊ!K½˜‚ÞvÞ…wc0º0 Z+ .6wá·æê‘%ÿÿÁ0l±aÄŠÁp£€u~³ÇÀÕšñ~|û¶êåÇ3ÀÁË]#ò²Û&¼U\fã¥ç}<‡ èÝ~·•=3yŒ5ó>/¸öÒ4Ý¡ ÌÆñ˜‹m‹Ù…`°€Îïéî‘ Šlì1?Z&. ù8.!­a¦ì£™ €0Ò 6GÛ?š‚&’˜Ô”ÙøƒeâáÂHgTWecÐðƒÕ#†ÛžD …~¬LlºoLTSúg@Âì#ebãw"¦ t¨y]̬ðCƒæ;EìgÛpfÖ©´°õSD*%Þ¾&ti°±wVD: fËõµ³²ùø„M!ß|ˆ;»· Wµ;¢ckýtõÃÇ–¶¿ã ‚ºRDéË\ý–Q¶öÔ„ÑwÀÓ•gdk02Ã4­ (}™§+?¶·ƒ¥CîÓ¦‚…;}ÕÙØâ ŽËdvXÞ™˜\õD$›{¨ÆiüXwg>‚‚6Œ “•¹3¥?S¤»Ú‚Ъ€Ä‰"Þ;SáPЮ€¼“š÷ÎH#„LœÈÇ×™­–ÐGíDZ%E]ò^•ƒ×©àqs#$ÒóMV=¹ÆlÜÐÊi…® {IV=¹BÛÚ9ÍŒ0…º„©—eµ9[×çS·´uš!Œ•„ák¸Êë=üצàh‡ˆ.ÑAã-“êÁúºòñˆ§9D,Ë.¯V_YA8æqÌïïS±gT­ ¼¹ª‚pÜóD"è Yñ¦]UÞ\UA8ò,LÀT4LcדG0rSV²ÈÇ‚ðZŒpl‰p IúcÕšóq% N `$$t4zg®%Ot(—5N®¯ œìT/.áâ~›mÜJÄ+ÈÆ‹Æ% I¾ßW÷.ÌsÒsåxG×~·ß'å­‹ÏÆÌ“Å>M_½¼j˜\x6žüdÃ8Ü¿Þ2¯ÐÍ3yç²³ñä:aüp +³J9Ïù­›KÎÈÓ ?0½Ÿ„+ÖDñ¸!>÷÷ÆÆyd?RÃ%k)çùúùbóñ9Ow…J™eå¿v$ÝåoC$dêŸç=×îÒ´óòêABˆÆDuÞÓsfœý|a¦¡“îÒ¿™?7›<ßl6 õ’cŸL·é <»€ Îçôçí‚OŒKÓ'"ü›Œ܃ÁMá•Ë—NÁ±sp†ágÈÌ¢÷ÿ¿)«›÷iV<•6YÛÄmivN3œƒ€eÅ\ˆ¸~bÍæ%æõPKimõÄͳI8 å·¸`¹ù~ŸîE–†Ä£å;±7¿*us° &âú¿{¹r‘ÃÝåù;Ót †Øû¹³H8Gâ?"ÈΕŠ,WïD¦^ò2Qw;¹!ÎUÀðKAÞ†ãœàˆw füÏÓ=º*ÏfÔòq¦Fò‡ð2'^¿Õ6ŒcEã&IÞê}1Ͻ¼äïÆõg*`¬šáFY‰YU0%ºY8M %GÁ¥(r5T×Õr¨Åë÷o¯LÈçÊáITXÚ§E›¼$áãí··×?ÅÈÚdÆl£sǦA^Ž€r—¸ÿXY$T5ÿÆÆÙrSmæÅ·ÿoEUV4µ*g¨Q^Ž€]8`…ò:b>¼±ª¦1ʲygMÁ÷—BNŽd¯ª|:V©%éµ(ñ-| a™ŸÖäáýëkT ²y©¬³BºßzÉ:'a~{Òþ“]M/Y@ZU53ͯ뷯ÄñºžU>ÿÖÃä È›©hù•8dq•¦W&`· ìhÊlóûÛÛw‘çK]ß¾+æxMÓYMܬԴ®€êe ¡·ÂOU'P×(ð®õªB'…´oÿ¼ý×Kƒõ›Ðôõ,ýËPqÀ‘BÀî-^œ6o=¼þh§j þÅæ_Ô{Ô$º–«Ò1Ú'÷)oÑnnQÜò;Q*n)£ º'Xv?× D»LŸó;y1èRT:èAñA¿£?Ï¥}K! âV÷s’„’ö´Ä×ù{*iç–âúªƒ«Õ^Ëqø:%¿®€]^ãe•BtH¨>§`Õý\'vÚ-ûµ@ bz GÒZ¼ÐòmàkÑ׎úTwÙI'ßúòÇÎöׇÒÀhÓËvD)›ù/W &K@¡µø²ùÌ/h­i~-dEýÂ6B@1È8v–t&Dgõ‰Hè­z#®g½öê­É"ñÃüß7‰G èó~cJ«žiÙ9ëWñB ±úÄçݧ´VŸÏ„¡PÐ(c üûËZŸò? |é.(!U#øâT8,1(ûD¨OË/N©ü|é! Ù`¬v†âøÏ¤Pzܧǣ§àŠâ‹SîB‰tÊãM%öçÙµª¿Ÿ#"…²0þðx¤‚//\uþFv× ‡O40XÝ®~ aœ~p<Ç€e øØ°5 ;³Ï¥AÌ®¹J¼f•d­ª<ê>‰)߬İõ€(.ÅŒL_8ÒTêÈõ ˆ¾VLL¤~1qâä(.ÂÁ·åß“ò¯Ãe8Í Èo£¸P`¨ÿ*&è0×G )IEND®B`‚guugelhupf/doc/images/hashing/Variance2.png100644 1751 0 20421 7515105045 20356 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿÿÿÿÿÿÿ!œ¹tRNS@æØf IDATxœí]9븲&ay ¥Ò€úäZ'o`¦ ùuèèù†Jîÿì+.ZHq'E-­oæØZØtécU±¸ˆ`IÀž™_£Šj´ÀoÎè”ú1“ëÆÛ'¸Ò'7hÚi&Sjs¸Ødäô87öm¯"Ú”oú?îõ6kÿm~.«lRu°Ê²U 0‹+hØ·–¢Ó8¥ˆ~Ùh‡}¦)³òÜÿ¼þÇ3C™{¡ë¾5sŒ± ½wÚÑ>ûdȘÔZéÚ¢o©²OüÀ§9ÅE0†¤¾ì_ï' ÇzáF²ƒ¼»{e9 ÆzÂyOZ@ÞV¡™olf%‚¸ù`ñ4ȘÂIáìùsgz‘²ila‹½SñHþ4ñÕ¢£ ôcŠ£¬Ý¿,ó<9ý:òÅ ZŽ*à®ÐkQÙV¹MÊÚñg­´ã­×7FÕ½!8¢!„¤Ý V<]G~ߪ5p3§ü`)cfAd§ÄZ’½±ÄfœÂ­÷Ê6%pŽÍœª>Ê™^Χ—½ó"÷ UN–ÿi@ÉWîžàŒ k?ÉßBïÜ þda1ñ• WŒÐ¥dÕÙ„}ÏJ yðÎ'Fަš¼¾b™ÉÚ¢j8a ÅÌ×Ú#CŠåăä‘`}º2­q¸@)L•b÷3U€E]Ípíº6NOàl!Ô̧ͻ§oVYÉŠºA¾Y¨ªHM1i Ø)‹>éàÉ­ôhÎ6SÝð·›S‰??]†ôÈê~ê;R–cçÓÄÉpÌä }—8yÖÕø¬}Ý…8¤†PàzO|ó¹ñÏi€ÐÍòê¼Å¬Aõ‹¿òÇ7«1„jñ†Ø·ûm–êí‹¿EÒléñk³ú +UÔj’jiV—ó4BÑŸâ‘Ë¡àO»Œ…ßÉ…T;,òöü¸©zGJpg,†NÅ+¤C©´ë†¥Â•Ùð„õiáK:pG….hOÞZu#ÏBEÍÛ Z.òœdÔ’c|˜ûeÜbÑ2 ©¾½Ã? :8¼8ì&è­c¨xV\cw‘DÏpX[ÃĆŒß©?GÄŸ†—Êü=Ù±±~ã[¡­DÁP#6“{a¥rÝľ>ü5ÖîÀRa"¢°\#ÔVâkò<Ö»^/Ö7{Ñp-œƒYaÑ-ìЦ‘<ú©„Ìö€ÍÌÉp¸Å]i=(äæ4uyñW6ƒ™™:…À€zg`ÊpÅOó¢¯obnaQ‡· ¬+¢ý,ZVè7s–dÝrNÞ¸,ð\/œ†¥*9ýîæ Âáè_ ¥)òEÞ Ý´ÿŠŠµ{è9×ñ€7Ö>çèéßÁ×™ïƒñXy¯~NÙºéc.-¤n$”ȶt1#uòÒ¢õ@ŠëKî$½ºBÂÇä`]¸JŽV…µñ&óúh$¸a9 y™Ò·Ð,i¤‰x‘–¥°‘\[I¡b<Ÿ2 WdÂ×Çc,!¢'©Ic„?¦.K队 X_ ¥é’cé²r‚À奾Nx,Uî_“ÑÑ5j ÃðÇòe©žU $p!fËr<®~Ó¸µ­€œ„¤·HÞ]†B,àHÂ+ÑÃu½ &ÛÛa]ïE 6øÉ.RÆ5[QFœ&$K›ÀL$ãúÂkd š_¦ÃµëÒÇäÀ†2r‡"ÙB}êݼIãT­ë¼£êK,!?]T=Íz¹`ÇúE­ÙD´óœM£v5KEd¤F;ìuu_Wôës,"J+‚¡ˆ«þˆYóM3ÄWE ì ¥s8H›zÙÖ .éæ®1²[´š¸Q–ƒ·Á"¢˜„#ãlKúûó¾˜0Rˆï˜~ýói ®ã•²Õâo5¿$øì·«P"­©TØÛp•^äWÜCQ“V Ð"²(QM/!ý_Ä­ù̳£*þôl^!uݜͷ±c<ÞÖÕ–‹›r:‡h‘1“šÙ<µ„Ùt}XýÌܤ’‚¯šÁMÿúPúFi5‡ˆ7ðBɅТ.+NB ÃKX髿©Gä^€`§Ýaˆ.Úý£–3çQdsÑÙtòV©¬œ±ˆ¯FõÉN·²^^jï½ÀöÒ½.HYÔÉIì¬ebß4¦¯XªSù¦¸õÝL¯]Ó³xúx#J{:_Yà¼ú«@·AÃwq)7s‰ð^H&vhsÊèRÒQÞ† NGÙ•ɸ€×Ñn- z’uu:ýÒôk¿à Ÿä‹)ãíkÔmð8íyJ+c볪T¾]¼IVHÎcUêG€$F³@£ïc’ö6ó—²"ĸýi¸ó–?Õo§__J*›FÊdrÔ¤.ÄšTš©sÓ¡þ(ÁéW ÎJBW eV»«è] YùN¾ê’ÊÙ ¨&ty´t¾÷'Ó)w+#´•”®o²úÙåÚ×7Y=ƒ¾Èµ§N€ÚS5"nnL, Ã~Û|O » Õ¹æìÀx(ò~Õ”­5lEÌsº¼ d»‰­MÆ £,Ë·‚¬”Tà9·dý$È´¦C]×YèF“æëëë3o«^V’PŒÝ÷ý~G8 Ô¤™àd‰eê½i¬®‘q€áù|>r¢]­JeÄ ⥔ Ù’+Ìëõjr¬ƒX‰K³U XõKA€¢…â"vËbM6´ÄfEöžlãIؽ@ºpMTB¼Y×6`Ë•±EÿX¸Ò_ç’ 8ˆ1¿¸4ɤî‘xE|L·틸趹%[Í’ÂÅW~Ê^¿ö‡Õ«.±Æu‚ X,²ü£  ähU8 Å^L7fê)à3ÙÄ¡½2˜ž>’5QwÊ`Â&¾7ƒ©¬ÄOÀ„3G¼LUÊ^^ÁÊL /ýÇüP ¸–w©Ô "åß\GŸ³C- ‰ÂÅTS˜tþ\€§‘ÓCÀ´}í ¦ÔCÀ®hW+`Z蔹š¸$Vb`¥A ƒ€ Ï"6é Òüm+1 8¡0õˆ£ÑŠ‘æS”¶³›I=‘}ã~pŠ«òd&(\ËFÝL¬œ± Àï(HðМÍ&ßå¦\ÞF.Ú))œ_`;; ˜úcWÄ BS“\džB7|™”èü¡«êÆëÞë¶Ì™:Ç%|Ín%†`¡éT{ú,,àƒ¯ï–X¶O/àuùu« E<¢-ãyŒkcÈ`îb7 xùB©,K Mw„fE£€…rœ[1•ÓÉö½L`m!`G¡BÛfVB•€çáEÕkÃ4‡…¿1@—ä_(þR 8}§A®„3 ®±bá¥é…ÖÏÕ8¢ ‡æ–F(¼êJr^jUJ_ÏYÂZ HxB²,æµ[µ‹ÎÍ [ÿ%Îp2vPŽD›L¬ø«éúŒhNY¤0„[#gÝ(ó˜ÕJ Šõ]z%4¬‹/¢p{ä¨ÌðŠâúØK@) ä-Ó,°‰ì¿Ì*‘D@a"S¯™ý8‰Le;äHËxF+ÑXq¤5%†F@\Æ ‘$—•Pç+ömÖÂ…”Ûqb%4Uu²ÚdŠë|ï;¦µ š&šqÕs£€v_G›Œ@1¯â'ô-R%Á±4l)$IJºktNÓõ¤$”æï$¶òé4»¶7Œ ÄsÖ.É©yç£ÏGïti»4£¼Ø?ÀHÒïQ#ÙÃ4°è~ CN M G×r(Íg·Ø¡~¤ÅAàQ€cæBfopï9‰v^ñG†„­ƒ@â 0HJàŒû.†”^ŸNkmHjÂ1·BZ ’jàÌÛÏ/‚”>fÞ}~$$·;LH ánw*˜Ž@FÝÞT0îk7HFàUøÞ ’ø˜ìŽ^€yK)®’£]ÀU/ž{ z÷à ÄK]{`ÌÚ¾lØC‘ÇÏŒIÛ— úø@äü+þÜi-õ« ;ÏàÃzæ[—|ÃðÐ@ƒÏÑØX¥‚fo >•ȧäÚuè $…ìEøyÊüOw@>]TpÓ6ìÆÈŒAF²—aÓ*èªìØø)*èHà©Ä»óHƒ@Jz2½?@ƒÏûcܲ û(gðú1ˆì¥Ø° z(eðßçžMwò3TЧalÉ*’þ~ˆ zÕ›QÏBÐët['ö- fF "k1¶kÃ~q –ÁѬd-ÇfUÐ3îö40hoÄ›UAßiÆ <lú#d-ÈVUБÀŒý³eÐ^wA $»9è Ì”281²d«6,hxO¤%/£Z2¸{4íæ0˜°mUŒl%Ù¨ úT"ûÖVÅ û¶VÁD ƒÈV”mÚ°_SѯΈ¥ vF¼sôŒ+úeU‘ [Y6¹N¼o ]Ñ/-ƒ ý²UÁë&WÚ÷%°«Š;7Î {‡gkðž\ä ²Êkàö$lÓKfØ0ÈÜ ¥or­x̄ܗ¶ÓÛ„‰®*F]·E!ÕÀbrÉ6 6äÓm¹ÁMQHØÊáä’*úÕ±†Aä&Õ†BBHÿù˜0p`ÐyÕÕÍÔ&!& & ž%R7ˆ\Û …Œ@O ììÝàY¢ƒ„AõÏ·á ™ ‹—ìQ‘OÆàW‹)… þ@Ž‚alBB`€ŠUqKß×$M<·0Ø…¡&Ü38lµ1rcí®0 îÀ3H!øÂWN!eË»&àG™Μ%cýŒcÍvþ¦˜TÅÆ6À_Wí ñ RP%BP‘Ï ƒc ›Ä®t[É(Ø1ø6½Ñ;ClÄÈ#çÏ•nÌIÛÂÓK®¨È§„ÁžÂ¸øî'„±fƒéþÀóW%$RKnÀå>½gçc•ž08î@ªbŒÂæâ·!Ó€2HwÀ * $Áõ¥i‚t¬‘Át‡LK v†¯Ï&¨*+d0F Ý!£*lÎÿ€`×Ö׊ãÀbrɲFÝÍ'^x_ R¶` ÓjÍ™ÔÈÿÀÚŒª ª(‰¥2Å98¢^ƒ‘Z" ùÈZuÎdeäÿ+ªJbêŠéÄfŒüc= F'ÔôPÍ`Û´{W%«a0.ù<)«’OÒµ°ç ÐXc÷ŠsŒ»9XÁ’yH;aP и%l¼í"œ™‰ÀÎŒUa$Wàgñ´6VUÆt6a³ýpf>åƒu=h?õöœ‘@UøSÏàmëáÌœêFJ@Ï`ÈàÂãîóÈ¡œÁnRu(ƒËVÆ3È¿\, cðµåþ­¹ df¬gð¶á€p~uý[=ƒ ûÇb& š±œÁ~W ƒË½&–‚@ϼ… x.øšX©Ëû·F ¸B¼ÕÓ2a"I<#5üóíŸ?ö‹è`*ÕÃøû…‚~c %LF`ëó:¿Á Wô#é•0­·¨$\ƒb\^ÓH­„) ¬2\›HêñÕ’ïG„I l͸²€fòrÎ]¾ ¯’*aZ»aã‰N_oº¸Ã”J˜ÜÒš‰KÞ±»#àmÊé”0%…Be"]rá‚ÀÍ“ÃdJ¸¬iÇ+¡|=ÜÑåiʉ”p¥­c9ƒt.æP¾Ÿ¬i”p!™sJø© lxÚš²3ƒI”p1)…œOß“ípÇìy¸ÃJ¸ ÔŽûÊäŒßÖV¦½{6ôfoÛ-I íj”ð,]1 Ã7ŸÎj8wò2;fÇg ¬JZ ÄÞtrUÃy•piiûdì JˆØDš;þtSÃV çSÃå $®p\™¨•+¢[[yÆW@ {Klt®®KzÜ]j”猯z®‚@â G[•h+“—»u·æo& WB`ë ÇZ)!Æý»±JGÈ»ÎQ¬†@}”åûÈÚ(!Æå…È /«6ñ)\åûÇûØ+“·×½ùc™6v\¸"ñ?2úÙ_²UBš×[#«áʤ]Ö}PcU™`ü—–CëÉ +-Ón)‘*™ØU&ÐY†öF«PDã¼' ¬†ÝîkövL`Ïa ¶L»9$QCjÉ–ÍkcT(ëñ´jÈ,ÙQ [¼^¤ÍlF°®˜@ª†š<õ¸Yrø|ùÃUˆ’ù4_>$Úqx š[¸v‰bgxvv…„Ct/•aý•pý¢†¿´þZ´~ß&eÏ“ÃMˆÕ°üU¾OÖµÄíö¢øp¸Û–òïwì }Ô°Õ¿ï›]pãÌáv,Ëwظ{Ct£Ó¯[‘9õÕ©¡·AßÝ€9ô3e`ÜØs¸G zºC€9´2fK7H `¾yÖÊvÆlÃá6 ˜Ã6ÄþÀ¡1S6K æÐß”;cF@»vƒaDoÓ‚ŽÃS%ª<Ó¢åPfïœ@€g·‘Íïºöýû6Ì~Ý5ÕÊŽM¸™Ù%ÝŠÃ8ÌÆ`VõÊû 0kM¸&avIÈtúã.Ì”EÇŸÞïø€²Hz²9…tQÍWkÑö©wB ~ƒ¢?Ã,öoå ‘Î¶ÝúEd•pJÐrzêjfæ!ƒÆ–ÅÖÂíÛô»%€°øÖªäßlaM¦.hU±ÑÜÞ7-‹å_eÅ_äRËhƒ~:|”e[Iגו-<ä6aL`EŽ2Ê#w××CRüé[Žü…žÇ²5ðN3݃ñA  T?¦’ Æõã©?š@ ¬Ÿu)6±ìƒ@ɨäwÈ3¸ÇƒÀ(•ø¨o%*ph@]ÿ> OÕÜ?4à pfâ 0Ù2ÎÒ7ЛÜ ùᵈeÞŒ`-’:ÝMKà+F®ÕhHÞSß…:cÉa¡¹MΆ»…ÖJõw¡>kgäZ#‡@ǹ–@mÙ„†²Ñg­û[hò‘n0i`®¹­¿kxŽênçڧ̵¾]ÿ·‘ù;pàÀÆœ¨wy!ä8¤ïÓàA¸+(M俬›Ž²÷ÁÜzË9ì›mëX¢duh¹ÊóV!ù¿=(Šœž’.jâÚ¦åð‚bó#gLf†z#à•ÌÝSÛŒÀJCEYôDNƨ2­è?Z-äÃÌIŸ6õü£áq…ªª\é2XœðÎÇ€²¤ æ¤ âòü„¢,%)„¤+]ÕO&Ùµ*ˆ;„)~ùt/Âö!û{Ha¥‚×¼ _DÜ PG ¨=ò±cðŒ$ôÁq•³^uUÆtRߘIDATÑp¾ýùöXMU ÓÀ6MîŸÓ@ȬoŸœ°A‚‚ÏB»öÙ•ÝÿTqrò÷Þù˜pøÀülÄZ嶉”ÏÖ@ª…J¤f¢•䨪4†O a°­… :'Á#‹-âù¤Ûä4†ŒGÂgìðAÃâ7à“ÅVA5Ì«ÁÏ ÉãÜÂ0ªäBá™ÅFqø@küý$Ölôí IEND®B`‚guugelhupf/doc/images/hashing/Variance3.png100644 1751 0 12321 7515105045 20357 0ustar mneumannwheel‰PNG  IHDR€à°øñAPLTEÿÿÿÿÿÿkw+ÄtRNS@æØfmIDATxœíÙŽ«°²†±Bîmý Ù¢ûÞG¡ï[ê•÷•M•&C eÀú–V îâwyÄCQ¬Eà«\ý{@EáÿX´U|!ñB¡ûÓ|á<3”! ¸ð¢Nù§í…¿±ÆÜ÷² uì…uì……¾G^XƆ;öÂ÷A.÷GýFÌm“Âìï‡8£{ÿÂ[ì…òS?p3?±ÖÁxš’çQÔåëo1_šÚ‰t•ÿ'órHiP& è¬}%„P¢J(_±u3†a>š™²e%‘™þ‚ ‹â{!³¼«%´D_¨“_¸€h+«¸úk\ Ò £¯Lïm@Üý,ÐhgÆ5D÷õì6ÔØ[j´/£ïäÿ-_¿ð{Ú_ˆõ¬7ÉÂ×þºo‰êf…’Ô7u¦:æìYRÓÃo‚z[R w¤¬x ƒ”ª9@ë[n Èr€ È&aƒ"l›£kH±.¨ËŘÒD`B!À[tí92À¤là‚vÀÛð’÷·m§a•:ÀTÎèL1­…b‚$عåÚ›äûîÆryƒ˜¹ÓÌì¹M û–Ô6þTã ä8@bf¶SöEPrÂÂjuÓ·¬ÕœÓ°Ò‰nú{™Q±üS7®ôŠ 8Þm —uñ*~ô\xý'‚ï÷_ÔÚIƒ…‚ö?”.dá¾õK2Õ;7gÈÕ8æ6o!Ì áeе¤Ì#SOMÅÐ9ŽcÔ&=j"m€€W² Ž$ëÍ< 7B3 Ã0oÉ¥JÁ0 ÜâP@€iƒ;)I¥ÅGqªg\ÛÕC‰¸¥¦|–™)©žy’?ð ©ýлMüØËH>É4ÔåÌ[JéÔ™l™Ì=·äùCòÁ׫žÍ Óç©©Ó§–GZ_ã¾vBÂ3©šÛoþ7ŸáÝ ,ýwpX¸wáÕ;ˆÃñì»[ÔDúsnˆ¥»B´Ëžìɇ¹Í4*Õb.̲-6 H… ¤ÂRa©°TØ@*l 6€(2¯ò‹MÖ‚H H… ¤ÂRa©°TØ@*l 6 H%sU¡2·0wOdà=ùX£Dd« œ­Žl dS‘­ÙGqÐÀ³Bgà Æìf4[ÕÑ꘣„}ÌHBGÇ@ë„~¸× ç'á0›ÉFBGßÀG~v L>ë•Jè9IVÓîÆƒËôA–Ì1*‹·™¶ž¾Ù¹áHÁÆÂ*õ¬\ãêV•×,ßðô½ŒrÃl+¬Ž±Pù”yó²0Å6³!®žˆ º¹¸)g­%œHJW*¯âD*.ÛzÃÁYÎT6ƒEŠ+—ŒêÉ|°ì˜tÆþB¦3ê²êLo=Ι’¤nнã+_3j(—Û*öAé9ë²8bš-ö »;¢ˆ\¦¡ßÆVH*l <‚|Ví»'•˲<®µe lrTÙÍ´"îká‚|°M-{:⢌º¬íìþ-\V’T_»§–e‚‚èŒû=½]U×øP`WVnõã¶Ow,¡6Sꢺÿnm$±ºUþ¼^Û¶X¨õÁòû«qÉ ÚUÛød² kýüÓÍ[rAÓÕ¨Ÿ¸ÛTý4ý’™¹A• ¬k]”)ÔܲMþ‰åã—ªn‹Wjš3П›[/¤=7ÊŸ3þY´–õëU×5ä†ÂÝOU$ÜagUe?<ÿþß|øÏ÷—;9#H¶Ì¯ú2zx+üÇñ:¢=4úО“g²n¤3çÄ/3å\¯ñA•—¯¹õ¯¤ùlL»Ê:LÌæ4íô‡€Å—`*CtÙ™Lº)I!³•¸×žlŽ6/²¹®¾CÝ@ÀiH¸º "u–Ù£cf&òX)§Ï팘R°>£ðÝ’î{‹¾AŠ…¹zW L®ö›]®ÌØ. ØY°l÷¥ËÐÀŽgŠÞ5{YÙ Ài(ʬ,&;Qìk°nŸ‰\Ul°U<ÃìCùê X+9 É1P‰\E̾IÀRa©°TØ@*l 6 H… ¤ÂRa©°TØ@*l 6 H… ¤ÂRa©ÿ–y/¶;âÆÀla©°TØ@*l 6 H… ¤ÂRa©°TØ@*l 6 H… ¤ÂRa©°TÎb`¾vze±ó¤ÁHœ<áe-ç4ðð5u;œÒÀWN;]RÁC$r†T(@2Šã°ŽâŒ6 Èw<ŠeÂÀ|œð ©8¤`>Nxƒdã„¢È|ÃÓ©Œ:'<­fC(›Ám rI%!«Å‹SnȤ‚¹¤’3(8A&NΨ3Ú n"£ÎÇ ÏŽl„µ{‹\œ0d Îb;K(ŠÁÀ[.N4°ÔùlG2Ðú`NNÅí—8Íd²óé´·¬ Ì'ŽýcˆA¬á%‡TÒ5pP(çQc˜›ÓôÈÁ ÏPYƒÉ$'œVPYdzQ|ËÞÀ�ì˜äÇ/¡íÉkºßvMÉ l#yO c 4 ¼XÅGXo >&’ã ì5ðöK'íº[o‡˜õÜp7 Åà}}DW7,fŒž^³e.‘E©¸ì¸SeYIRv Ü),,ê°WذS eiYÜ(h%ÌSÁý‹<ÒÃÄ="y¹ºÍ ÷ˆå êäF̱&Šõžn¸Êµÿ´}$¯K$Ú»áæéde*օͱ7—pm6Ó)R¶eu>X»ö#yµåóËøáÆ‘¼ÞÀª¶¹Í瑩7°(K] fj ø` ÙͶ“i‰#šÊÍ£úûÞÐBòШ&šÿÆ2}ìVù¬ô†~˜`pÙó©oÛY˜ÂÀª†ä¼Q~˜À@^êÛF£ “lL¼oѨO8À±¾o‘#¦y«·GꇤV²¾U÷¤6¦ÃZëªøñqMŽóMÙÖO¨D Kæi`QUu׋ß[X@}çY<ÿtAóÉm tÍ¢þ§áýl<Ð*Xs¼­µq‘œ3r¯n{ •7=³MSðùrå ø§a®ß×@À& úe€/ß_öœW·´Óª& ô=þsæ·çæŒÏù¿S°üy½i¬Å¨o{?ûèUòQÜ*Ñ5Aľ¿ì§ª²þ¯ú¯;¥Ã^|ðœŒ}P:¥…~(ücÈΡñ9LŠá¹"pÑ|„ú“ÞZW9Ú©erdj'Iͤ¼¨ÔLŸí/ÂÏY¼ ³¢ÑPJÌÞ¥ª¯ñp ¢)„$(¯`˜S‘a˜]€ÇÃRtÖ°9¾êB Ù©ÁÙÊŸy½›lÒ­Ü,c©ªê¡àK%ðÏÀ‹ÍÖšVx-àccÔB%\§L^p¿²É¨ðG'¦^_„Ðq 'aþVádA64I¢`Ð\0zc¿†Båð&à«PÉÀž çÆÒ=ÿ—Ù1µdä”S*—aŒ››Ý®âêÅûÒûó‡a©¼×óLƒqÈæŽ Œ²ËÉÈîws¼mx^a3¢ Çy`ïØÕè 7(¤-R¤IÞ²sãxÞÿÎ\ë®é„eÿ=ñú+z¤½¹Ž*ó­a͵b `ç˜Áp.Ëýc_X@†IGÓüRWÏt·Z«9õtž ‰53ÉYû –Œ†$Âa‰°€DX@",  H„$Âa‰°€DX@",  H„$Âa‰°€DX@",  H„$Âa‰°€DX@",  H„$Âa‰°€DX@"¢÷Öcài‹ý/VÀ˜÷†žú‰Ì,_4œa‰°€DX@",  H„$Âa‰°€DX@",  H„$Âa‰°€DX@",  H„$Âa‰°€DX@",  H„$Âa‰°€DX@", ‘Á4»! ÍP@Ém–17dY çDX@", ‘Xyè X@"ÑþnmÉI‰]0L|!Â.Tã!Ùƒ€€B  VcØCº`P@vÁ( æ„áŠ4»`aþÇ$avÁ ’0  圆Ø$<`B@vÁ1(`¬² ŽY”„ÙÇ,¨Hì‚CLþW ì‚c–öH³ ‚ßæ·ª?± ˆóÀV@vÁ¦-<<:# »`Ÿ¸ŠtG@vÁ>q鮀ì‚=T¤ö]°Ë’Š´U]°Ë¢Š´U]°ÔGÏ”¦¼™7°ƒñ@ñ& —ö¿QÓp‡8,ñ_á1»`K\K¤¯D‚ì‚-Ñ5~2‰˜]г 7Fã+*È.èY  IÃ&³ :–ôvdt,êP5 b"f´ ¦9¼ÙCã+(È.h oÈ2Ù¥¯ñd4,°ÍÙ K*µ ² "‹ç‰h|½± Z–O´ÑøÚ(øJoÍ Y1SIãëƒ]Y! É﯆ôöœŽ5såPÁ[õ]}§·çt¬šl¨ #`Å©xålMíŸt~|efåtWíº‚+ìtÌ|xI²vÂ5wmYVÏX׿ üäd¼~Ê¿6oŸÞA½Z@xJ×>*þÜŒp½€îQñ‡?l'è¶‚]QüÑCfhëÆtŸt‚‚¨!qᾂŸXŸ¡®\ÔU:¸>NBòÒO=üý«ê«Wšœ;ï/`õõü‚”Ü:µ‚û ÿ±nÝvÓ¼NœŽ°°í§á‰›ÆGnÐ ÏMR¾ŸWÂcw¸.†°~ÅYLJoÞhˆ½†ðùŒÓ° ›šM˧“0 ýp\”)gÛŸ…€æ?fˆ á©$ÌB@—Bÿ!Ì÷¿ê/4X8Crp<…ª^ÿô‘FD“¡€øäó¯zV÷èÛ^¶D ”\F% µ«¤m>ã¡”kMãg#+ëÇ0 “ÃɬÑJÄ’Á·tX…”²[ÄÙNT|éV]ÛÏv¤¾)A—üÐ (Lÿ´l{ô{5åna›sÁ‹ú5 6•ˆF@¨4”Cš^aéëfR*x& ÜíšÑ5ê PÀÈÞÝÃpe4—Kû‰Ò6 jck7ùâ<\Á8TÀ\·º©˜™†@s“ªÓ%ßz`sÕ‰ü¡ó@e~€—ãÛá/PmÓ²k$t—äÊæÿV=â¸á˜¬üIEND®B`‚guugelhupf/doc/todo.sgml100644 1751 0 3031 7515105045 14757 0ustar mneumannwheelTODO Features PositonInvertedList Zusätzliche Tokenizer (z.B. welche die Datum erkennen, Abkürzungen wie z.B. "I.B.M" etc.) ISO_LATIN CharTable Query-Daemon Archive-Unpacking, Webcrawler Filetype-recognition Query-Webinterface, GUI Approximate Matching Index-Utilities, z.B. Index-Joining Incremental Indexing Stemming Boolean-Query, ADJ, NEAR-matching Performance Benutze Memoized-Sets für Indizierer/Invertierte Liste (DocIdMap). Für gleiche Wörter eines Dokumentes gilt dann O(1) anstatt O(log n). Andere Refactoring Decouple Code Rewrite in OO-Sprache (MOBY?) Benutze popt für Kommandozeile (default Arguments etc.) guugelhupf/doc/install.sgml100644 1751 0 2106 7515105045 15462 0ustar mneumannwheelInstallation Requirements Unix-Umgebung (FreeBSD, Linux oder Windows/Cygwin) MLton Ruby Compilieren der gh_* Programme Compilation Manager installieren: &prompt.user; cd src/compilation-manager && su -c cp CM.rb /usr/local/bin GH_BASE Umgebungsvariable setzen: &prompt.user; export GH_BASE=`pwd` Compilieren unter Unix: &prompt.user; cd src/lib/gh/test && make Compilieren unter Windows/Cygwin: &prompt.user; cd src/lib/gh/test && make EXE_EXT=.exe guugelhupf/doc/design_impl.sgml100644 1751 0 4072 7515105045 16312 0ustar mneumannwheel ]>
Guugelhupf - Design und Implementation Michael Neumann
neumann@s-direktnet.de
Werner Maier
werner@maier-web.net
2002 Michael Neumann
Zielsetzungen von Guugelhupf Performance. Durchsatzgeschwindigkeit in der Größenordnung GB/h. Guugelhupf soll in erster Linie ein Framework und keine konkrete Implementierung sein. Ausprobieren neuer/alter Paradigmen/Sprachen (wie z.B. strukturiertes Programmieren in SML anstelle von OOP). &arch; &limits; &todo; &install; ⟨ &hashing; Evaluierung vorhandender Suchmaschinen und Frameworks Namazu udmsearch Isearch Amberfish (tm)Commercial successor of Isearch Lucene ht://Dig Glimpse TO BE WRITTEN
guugelhupf/doc/Makefile100644 1751 0 2342 7515105045 14572 0ustar mneumannwheel# # $FreeBSD: doc/de_DE.ISO8859-1/books/handbook/Makefile,v 1.14 2002/03/13 00:44:20 ue Exp $ # $FreeBSDde: de-docproj/books/handbook/Makefile,v 1.16 2002/03/11 22:07:13 mheinen Exp $ # # Build the FreeBSD Handbook in its German translation. # MAINTAINER=neumann@s-direktnet.de DOC?= design_impl FORMATS?= html-split #-split HAS_INDEX=false INSTALL_COMPRESSED?= gz INSTALL_ONLY_COMPRESSED?= # Images from the cross-document image library #IMAGES_LIB= callouts/1.png #IMAGES_LIB+= callouts/2.png #IMAGES_LIB+= callouts/3.png #IMAGES_LIB+= callouts/4.png #IMAGES_LIB+= callouts/5.png # # SRCS lists the individual SGML files that make up the document. Changes # to any of these files will force a rebuild # # SGML content SRCS+= design_impl.sgml IH=images/hashing IMAGES= ${IH}/Variance0.png ${IH}/Variance1.png ${IH}/Variance2.png ${IH}/Variance3.png IMAGES+= ${IH}/Empty0.png ${IH}/Empty1.png IMAGES+= ${IH}/Max0.png ${IH}/Max1.png ${IH}/Max2.png IMAGES+= ${IH}/Time0.png ${IH}/Time1.png # Entities #SRCS+= chapters.ent #SRCS+= newsgroups.ent # alle Kapitel bauen #CHAPTERS?= ${SRCS:M*chapter.sgml} #SGMLFLAGS+= ${CHAPTERS:S/\/chapter.sgml//:S/^/-i chap./} DOC_PREFIX?= /usr/doc .include "${DOC_PREFIX}/share/mk/doc.project.mk" guugelhupf/doc/arch.sgml100644 1751 0 36475 7515105045 14771 0ustar mneumannwheelArchitektur von Guugelhupf Guugelhupf besteht, grob gesehen, aus zwei Teilen: Dem Indizierer und dem Abfrage/Query Programm. Der Indizierer Aus einer beliebigen Anzahl von Dateien erstellt der Indizierer einen schnell zu durchsuchenden Index. Der Indizierer unterteilt sich selbst wieder in die folgenden Teile: Dem Analyser, bestehend aus: Einem Tokenizer, und beliebig viele Filter (z.B. LowercaseFilter, StopwordFilter etc.). Die Datenstruktur zum Aufbau und Speicherung des Index. Tokenizer In der vorliegenden Version von Guugelhupf ist nur eine Art von Tokenizer implementiert, den wir CharTableTransform-Tokenizer nennen, da er aus einer Lookup-Tabelle besteht, die jedes ASCII Zeichen auf ein anderes ASCII Zeichen abbildet/transformiert, mit der Besonderheit, daß der ASCII-Wert 0 eine spezielle Bedeutung hat, und zwar ist in diesem Fall das Zeichen ein Non-Word Zeichen und kann "weggeschmissen" werden. Ein Wort ist somit definiert als alle Zeichen die von zwei ASCII 0 Zeichen umgrenzt werden. Auch wird mit dieser Art von Tokenizer eine anschließende Konvertierung zu Kleinbuchstaben gespart, indem man eine entsprechende Tabelle verwendet. Der Code hierfür findet sich im Verzeichnis tokenizer/char-table-transform. Drei Unterschiedliche Ausprägungen des CharTableTransform-Tokenizers sind vorhanden: Memory Mapped (MmapTokenizer) Stream (StreamTokenizer) String (StringTokenizer) wobei der Memory Mapped Tokenizer der schnellste ist, nicht zuletzt weil ein großer Teil in C geschrieben ist. Der StreamTokenizer operiert auf Streams (d.h. auch StdIn oder Sockets möglich die nicht unbedingt eine feste Länge besitzen, wie dies für den Einsatz des MmapTokenizer erforderlich ist) und ist vollständig in SML geschrieben. Aufgrund des Fehlens der Funktion TextIO.openString in MLton wird der StringTokenizer benötigt, der auf Strings operiert, wie der Name schon sagt. Zur Erzeugung von Character-Tables kann das Ruby-Script createCharTable.rb im tools Verzeichnis verwendet werden. Es erzeugt dann entweder SML-Sourcecode, der in ein Programm einkompiliert werden kann, oder eine Binär-Datei, die erst zur Laufzeit geladen wird. Das Eingabe-File für eine Englische Character-Table könnte z.B. wie folgt aussehen: add LOWERCASE add UPPERCASE, LOWERCASE add DIGITS Dies gibt an, daß alle kleinen Buchstaben (LOWERCASE) auf sich selbst abgebildet werden sollen, ebenso wie alle Ziffern (DIGITS). Großbuchstaben (UPPERCASE) jedoch werden auf die entsprechenden Kleinbuchstaben abgebildet. Alle anderen Zeichen besitzen den Wert 0 und werden somit nicht zur Bildung eines Wortes verwendet. Alle Tokenizer haben ein Subset von Funktionen mit gleichem Typ (z.B. nextToken), verdeutlicht wird das durch die Signatur TOKEN_STREAM (definiert in Datei gh-defs.sml). Dies trifft auch für alle Filter zu. Filter Filter haben als Eingabe einen TokenStream und liefern einen veränderten TokenStream als Ausgabe. Die vorliegende Version implementiert nur einen Stopword-Filter, der haufige Wörter einer Sprache eleminiert. Die zu eleminierenden Wörter können z.B. aus einer Datei gelesen werden, die in jeder Zeile ein Wort enthält. Diese Wörter werden dann einmalig in eine Hashtabelle eingefügt, um später eine schnelle Abfrage zu gewähleisten, ob das Token ein Stopwort ist oder nicht. Der Sourcecode des Stopword-Filters befindet sich im Verzeichnis filter in der Datei stopword-filter.sml. Die Index Datenstruktur Die gebräuchlichste Datenstruktur für einen Wort-Index ist wohl eine Invertierte Wort Liste. Diesen Weg haben auch wir gewählt. Zwei Ausprägungen sind denkbar: FrequencyInvertedList PositionInvertedList wobei die FrequencyInvertedList nur die Häufigkeiten des Auftretens eines Wortes in einem Dokument speichert, die PositionInvertedList dagegen alle Positionen eines Worten in einem Dokument. Letzteres benötigt sehr viel mehr Speicherplatz, und ist in der Vorliegenden Version noch nicht implementiert. Vorteilhaft dagegen wäre die Möglichkeit der Abfrage ob Wörter in einem bestimmten Abstand zu anderen Wörtern stehen oder ob diese benachbar sind. Unsere Invertierte Liste verwendet eine Hashtabelle (Datei ds/hash-table.sml), und bildet damit die Wörter auf eine Splay-Tree Map ab. Letztere Datenstruktur bildet die Dokument-ID auf die Häufigkeit des Auftretens ab. Es findet also folgende Abbildung statt:
token -> docid -> frequency
Bzw. für die PositionInvertedList:
token -> docid -> position list
Der Sourcecode für die Invertierte Liste ist in Datei ds/inv-list.sml. Dateiformat des Index Im Folgenden ist die Struktur eines abgespeicherten Index beschrieben. Der Header ist eine 28-Byte große Struktur, die wie folgt aufgebaut ist: Byte-Position Daten-Typ Beschreibung 0 - 15stringKennung (z.B. "FREQ_INV_LIST ") 16 - 19dwordVersion (z.B. 0x00010000 für 1.0) 20 - 23dwordFlags (z.Z. unbenutzt) 24 - 27dwordGröße des Index (Hashtabelle) Daraufhin folgt die Hashtabelle (4*Größe des Index Bytes). Jedes Bucket der Hashtabelle enthält entweder den Offset (relativ zum Ende der Hashtabelle) auf die Token mit demselben Hashwert, oder bei einem leeren Bucket den Wert -1 (0xFFFFFFFF). Die Daten (d.h. Token und Häufigkeiten) folgen direkt der Hashtabelle. Jedes nicht-leere Bucket wird in der Daten-Sektion wie folgt abgebildet: Byte-Position Daten-Typ Beschreibung x - (x+3)dwordAnzahl der folgenden Tokens Daraufhin folgt für jedes Token folgende Struktur: Byte-Position Daten-Typ Beschreibung y - (y+3)dwordAnzahl der Dokumente (in denen das Token vorkommt) y+4byteLänge des Token-Textes (y+5) - (y+5+n)stringToken-Text Hierauf folgt Anzahl der Dokumente-mal: Byte-Position Daten-Typ Beschreibung z - (z+3)dwordDokument-ID (z+4) - (z+7)dwordHäufigkeit des Auftretens in diesem Dokument
Das Indizier-Programm gh_index Das Programm gh_index ist die Schnittstelle für den Benutzer zum Anlegen eines Indexes. Der Sourcecode befindet sich in Datei test/index.sml. Man ruft es wie folgt von der Kommandozeile auf: &prompt.user; gh_index hashSize charTableFile stopwordFile indexFile docDB Die verschiedenen Parameter sind in der folgenden Tabelle erklärt: Parameter Beschreibung hashSizeHiermit kann man die Größe der Hashtabelle angeben. charTableFileDie zu verwendente Char-Table Datei. stopwordFileDatei, die zu verwendende Stopwörter enthält. indexFileIndex-Datei, die erzeugt werden soll. docDBName der Dokument-Datenbank (ohne Endung .db). gh_index   erwartet auf der Standard-Eingabe die Namen der Dateien, die es indizieren soll. Es können auch zwei Dateinamen, getrennt durch ein TAB-Zeichen angegeben werden, woraufhin gh_index   die erste verwendet um die Datei zu tokenizen, die zweite jedoch in der Dokument-Datenbank abspeichert. Dies hat denn Sinn und Zweck, daß z.B. auch Webseiten indiziert werden können (müssen zuvor als lokale Datei gespeichert werden), ohne daß dabei die URL verloren geht. Die Dokument-Datenbank ist eine DBM Hash-Datenbank, in der zu jeder Dokument-ID der zugehörige Dateiname steht. Eine DocDB kann auch für mehrere Indexe verwendet werden. Der Sourcecode des DBM-Interface für SML ist im Verzeichnis dbm zu finden (Dateien dbm.c und dbm.sml). Das Hilfprogramm gh_conv Da gh_index eine Liste von Dateinamen von StdIn ließt, muß man diese irgenwie generieren. Unter UNIX bietet sich hierfür das Programm find an: &prompt.user; find . -type f -print liefert rekusiv eine Liste aller Dateien beginnend vom aktuellen Verzeichnis. Mit Hilfe von Pipes kann man dies nun gh_index   auf StdIn umleiten: &prompt.user; find . -type f -print | gh_index ... Das Ruby-Skript gh_conv   kann dies ebenso, dient aber vorwiegend zur Konvertierung sowie Filterung verschiedener Dateitypen. So ermittelt gh_conv   den Dateityp durch das UNIX-Hilfsprogramm file  und reagiert entsprechend dem Dateityp. Es kann: Dateien ignorieren (:REJECT); sinnvoll bei Videos, Bildern oder Audio-Files Dateien as is passieren lassen (:ASIS) ein externes Datei-Konvertierung Programm aufrufen (z.B. lynx, ps2ascii oder catdoc) Aktionen für weitere Dateitypen können hinzugefügt werden, indem man im Programm (Datei test/gh_conv) das Array FILE_TYPE_LIST entsprechend erweitert. Aufrufen kann man es auf zwei Arten: &prompt.user; find . -print | gh_conv | gh_index .... &prompt.user; gh_conv /start/dir | gh_index .... Das Abfrage/Query Programm Das Query-Programm besteht aus einem RankingParser (Files query/ranking-parser.sml und query/ranking.lex), der Struktur RankedQuery (File query/ranked-query.sml) sowie dem Sourcecode für das Programm selbst (test/query.sml). Der eigentliche Zugriff auf die Index-Datei findet in der InvertedList Struktur in Datei ds/inv-list.sml statt, oder falls eine extrem schnelle und Resourcen-schonende Version benötigt wird, kann ds/query-inv-list.c verwendet werden. Der RankingParser transformiert eine Eingabe, wie etwa folgende:
(+10)IR -diode +information retrieval
in eine Liste von RankedTerm's:
[RankedTerm(IR, 10), RankedTerm(diode, -1), RankedTerm(information, 1), RankedTerm(retrieval, 1)]
RankedQuery führt eine Abfrage auf den Index durch und berechnet für jedes auftretende Dokument einen Score. Die Dokumente sortiert nach absteigendem Score werden dann schließlich vom Hauptprogramm gh_query ausgegeben. Das Query-Programm gh_query Das Programm gh_query ist die Schnittstelle für den Benutzer zum Abfragen eines Indexes. Der Sourcecode befindet sich in Datei test/query.sml. Man ruft es wie folgt von der Kommandozeile auf: &prompt.user; gh_query charTableFile stopwordFile indexFile docDB Die verschiedenen Parameter sind in der folgenden Tabelle erklärt: Parameter Beschreibung charTableFileDie zu verwendente Char-Table Datei. stopwordFileDatei, die zu verwendende Stopwörter enthält. indexFileIndex-Datei, die abgefragt werden soll. docDBName der Dokument-Datenbank (ohne Endung .db). gh_query   erwartet auf der Standard-Eingabe eine Abfrage (siehe weiter oben). Daraufhin gibt das Programm auf der Standard-Ausgabe die passenden Dokumente geordnet nach höchstem Score aus.
guugelhupf/mk/ 40755 1751 0 0 7515105216 12676 5ustar mneumannwheelguugelhupf/mk/mlton.mk100644 1751 0 104 7515105045 14430 0ustar mneumannwheelinclude ${GH_BASE}/mk/top.mk CFLAGS += -I$(MLTON_LIB)/self/include guugelhupf/mk/top.mk100644 1751 0 102 7515105045 14077 0ustar mneumannwheelCFLAGS += -O3 -fomit-frame-pointer MLTON_LIB=/usr/local/lib/mlton guugelhupf/src/ 40755 1751 0 0 7515105216 13056 5ustar mneumannwheelguugelhupf/src/compilation-manager/ 40755 1751 0 0 7515105216 17004 5ustar mneumannwheelguugelhupf/src/compilation-manager/CM.rb100644 1751 0 10246 7515105045 17750 0ustar mneumannwheel#!/usr/bin/env ruby # # A simple compilation manager for MLton, which supports Groups, # Packages, Objectfiles and arbitrary commands. # # Copyright (c) 2001, 2002 Michael Neumann # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL # THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # class Source attr_reader :path def initialize(path, dir=nil) if path.strip[0,1] == '!' then @command = path.strip[1..-1].strip @dir = dir || Dir.pwd # execute command print @command, " (#{@dir})\n" cur_dir = Dir.pwd Dir.chdir(@dir) print `#{@command}` Dir.chdir(cur_dir) else if path[0,1] == "/" or dir.nil? @path = path else @path = File.join(dir, path) end raise "File <#@path> do not exists!" unless File.exists? @path end end def kind if @command then :COMMAND else name = File.basename @path suffix = name.split(".").last case suffix.downcase when 'sml', 'sig', 'fun' :SML when 'cm' :CM when 'o' :OBJ else raise "unknown file type" end end end def content(object_files = []) case self.kind when :SML File.readlines(@path).to_s when :CM CM.new(@path).content(object_files) when :COMMAND # do nothing else raise "file type does not support content" end end end class CM attr_reader :kind, :package, :sources, :path def initialize(path) @path = path @dir = File.split(@path).first @lines = File.readlines(@path).collect{|l| l.strip}.select{|l| not (l.empty? or l[0,1] == '#') } parse end def content(object_files = []) content = "" if @kind == :PACKAGE content << "structure #{ @package } = struct\n" end @sources.each {|src| if src.kind == :OBJ then object_files << src.path elsif src.kind == :COMMAND # do nothing else content << src.content(object_files) end } if @kind == :PACKAGE content << "end;\n" end content end private def parse header = @lines.first case header when /^Package\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+is$/i @kind, @package = :PACKAGE, $1 when /^Group is$/ @kind = :GROUP else raise "Unsupported CM header format" end @sources = @lines[1..-1].collect {|l| Source.new(l, @dir) } end end if __FILE__ == $0 def usage "USAGE: #$0 cm-file [output-file]" end cm_file = ARGV.shift || (raise usage) output_file = ARGV.shift if output_file.nil? if File.exists? "x.sml" raise usage else output_file = "x.sml" end end object_files = [] c = Source.new(cm_file).content(object_files) File.open(output_file, "w+") {|f| f << c } command = "mlton #{output_file} #{object_files.join(' ')}\n" print command print `#{command}` end guugelhupf/src/gh/ 40755 1751 0 0 7515105216 13454 5ustar mneumannwheelguugelhupf/src/gh/test/ 40755 1751 0 0 7515105216 14433 5ustar mneumannwheelguugelhupf/src/gh/test/query.sml100644 1751 0 2167 7515105045 16420 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) open Tokenizer.CharTableTransform fun main (name, args) = let val usage = "USAGE: " ^ name ^ ": charTableFile stopwordFile indexFile docDB" val _ = if List.length args <> 4 then raise Fail(usage) else () val [charTableFile, stopwordFile, indexFile, docDB] = args val db = DBM.openReadOnly docDB val q = RankedQuery.query {strm = TextIO.stdIn, charTable = mkCharTableFromFile charTableFile, stopwordTable = SimpleAnalyser.TS.mkTableFromFile stopwordFile, outputFn = (fn s => TextIO.output (TextIO.stdErr, s)), file = indexFile, hash = Hashing.sdbmHash, scoreFn = (fn {freq, rank} => freq * rank) } fun output (docid, rank) = let val file = DBM.lookup db (Misc.Pack.packWord docid) in print (Int.toString rank ^ "\t" ^ file ^ "\n") end in List.app output q end val _ = main(CommandLine.name (), CommandLine.arguments ()) guugelhupf/src/gh/test/index.sml100644 1751 0 3174 7515105045 16361 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) open Tokenizer.CharTableTransform fun main (name, args) = let val usage = "USAGE: " ^ name ^ ": hashSize charTableFile stopwordFile indexFile docDB" val _ = if List.length args <> 5 then raise Fail(usage) else () val [hashSize, charTableFile, stopwordFile, indexFile, docDB] = args (* NOTE: no two or more programs are allowed to access the docDB concurrently *) val db = DBM.new docDB val _ = DBM.insertIfNew db ("nextId", Misc.Pack.packWord 0w1) (* init if db is new *) val inx = DefaultIndex.new {size = (valOf o Int.fromString) hashSize, stopwordFile = stopwordFile, charTable = mkCharTableFromFile charTableFile, outputFn = (fn str => TextIO.output (TextIO.stdErr, str))} (* format of a line: file [\t url] *) fun scanDocument line = let fun isTab c = (c = #"\t") val file :: url = String.tokens isTab (String.chop line) val url = if url = [] then file else List.hd url val docid = Misc.Pack.unpackWord (DBM.lookup db "nextId") val _ = DBM.replace db ("nextId", Misc.Pack.packWord (docid + 0w1)) (* increase nextId *) val _ = DBM.insert db (Misc.Pack.packWord docid, url) (* store filename under docid in db *) in DefaultIndex.indexDocument inx (file, docid) end val strm = TextIO.stdIn in TextIO.appLines scanDocument strm; DefaultIndex.writeIndex inx indexFile; DBM.close db end val _ = main(CommandLine.name (), CommandLine.arguments ()) guugelhupf/src/gh/test/gh_conv100644 1751 0 7513 7515105045 16104 0ustar mneumannwheel#!/usr/bin/env ruby # # Copyright (c) 2001, 2002 Michael Neumann # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL # THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # require "tempfile" # TODO: load FILE-TYPE-LIST from file FILE_TYPE_LIST = [ ["application/msword", proc{|f, t| `catdoc '#{f}' > '#{t}'`}], ["text/html", proc{|f, t| `lynx -dump '#{f}' > '#{t}'`}], ["application/postscript", proc{|f, t| `ps2ascii '#{f}' > '#{t}'`}], ["application/pdf", proc{|f, t| `ps2ascii '#{f}' > '#{t}'`}], ["image/*", :REJECT], ["audio/*", :REJECT], ["video/*", :REJECT], ["*/*", :ASIS] ] class Dir def Dir.recurse(dir, command) Dir[File.join dir, "*"].each do |f| if FileTest.directory? f Dir.recurse(f, command) elsif FileTest.file? f command.call(f) end end end end module MIME =begin def MIME.parse(str) mime, *attrs = str.split(";") h = {} attrs.each do |a| k, v = a.split("=") h[k.strip] = v.strip end [mime.strip, h] end =end def MIME.parse(str) if str =~ /^\s*([-\w\/_]+)/ mime = $1 [mime, {}] else raise "malformed MIME" end end def MIME.match(mime, pattern) x, y = mime.split("/") p, q = pattern.split("/") b1 = (p == "*" ? true : p == x) b2 = (q == "*" ? true : q == y) b1 && b2 end def MIME.file(filename) MIME.parse(`file -i -b '#{filename}'`) end end class Tempfile def self.tempname o = new("gh") o.close o.path end end def convert(filename) mime, _ = MIME.file(filename) STDERR.puts "= file #{filename} recognized as #{mime}" FILE_TYPE_LIST.each do |pattern, action| if MIME.match(mime, pattern) then return \ case action when :ASIS STDERR.puts "! accepted asis" filename when :REJECT STDERR.puts "! rejected" nil when Proc STDERR.puts "! convert" tmpfile = Tempfile.tempname action.call(filename, tmpfile) tmpfile else raise end end end raise end if __FILE__ == $0 def usage STDERR.puts "#$0 [directory]" end def process(file) new_file = convert(file) begin STDOUT.puts "#{new_file}\t#{file}" if new_file rescue Exception => e puts STDERR.e.inspect sleep 1 retry end end if ARGV.size > 1 usage exit -1 end dir = ARGV[0] if dir Dir.recurse(dir, proc {|f| process(f)}) else STDIN.each do |f| f.chomp! process(f) if FileTest.file? f end end end guugelhupf/src/gh/test/query.cm100644 1751 0 57 7515105045 16160 0ustar mneumannwheelGroup is common.cm ../query/query.cm query.sml guugelhupf/src/gh/test/index.cm100644 1751 0 57 7515105045 16122 0ustar mneumannwheelGroup is common.cm ../index/index.cm index.sml guugelhupf/src/gh/test/common.cm100644 1751 0 302 7515105045 16314 0ustar mneumannwheelGroup is # independent packages ../smlnj/lib.cm ../misc/all.cm ../io/io.cm ../hashing/hashing.cm ../dbm/dbm.cm ../gh-defs.sml # other ../ds/ds.cm ../filter/filter.cm ../tokenizer/tokenizer.cm guugelhupf/src/gh/test/Makefile100644 1751 0 425 7515105045 16151 0ustar mneumannwheel# on Windows: make EXE_EXT=.exe all: gh_index$(EXE_EXT) gh_query$(EXE_EXT) gh_index$(EXE_EXT): rm -f x.sml CM.rb index.cm mv x $@ rm x.sml gh_query$(EXE_EXT): rm -f x.sml CM.rb query.cm mv x $@ rm x.sml clean: rm -f x.sml gh_index$(EXE_EXT) gh_query$(EXE_EXT) guugelhupf/src/gh/hashing/ 40755 1751 0 0 7515105216 15075 5ustar mneumannwheelguugelhupf/src/gh/hashing/hashing.cm100644 1751 0 77 7515105045 17100 0ustar mneumannwheelPackage Hashing is sdbm-hash.sml !make sdbm-hash.o sdbm-hash.o guugelhupf/src/gh/hashing/sdbm-hash.sml100644 1751 0 62 7515105045 17513 0ustar mneumannwheelval sdbmHash = _ffi "sdbm_hash" : string -> word; guugelhupf/src/gh/hashing/sdbm-hash.c100644 1751 0 474 7515105045 17171 0ustar mneumannwheel/* * Copyright (c) 2002 Michael Neumann */ #include "libmlton.h" unsigned long sdbm_hash(Pointer str) { unsigned long hash = 0; int length = GC_arrayNumElements(str); while (length--) hash = (*((unsigned char*)str)++) + (hash << 6) + (hash << 16) - hash; return hash; } guugelhupf/src/gh/hashing/Makefile100644 1751 0 126 7515105045 16611 0ustar mneumannwheelinclude ${GH_BASE}/mk/mlton.mk OBJS=sdbm-hash.o all: $(OBJS) clean: rm -f $(OBJS) guugelhupf/src/gh/dbm/ 40755 1751 0 0 7515105216 14216 5ustar mneumannwheelguugelhupf/src/gh/dbm/test/ 40755 1751 0 0 7515105216 15175 5ustar mneumannwheelguugelhupf/src/gh/dbm/test/test.rb100644 1751 0 57 7515105045 16540 0ustar mneumannwheelrequire "dbm" db = DBM.new "test" p db.to_hash guugelhupf/src/gh/dbm/test/test.sml100644 1751 0 603 7515105045 16745 0ustar mneumannwheelfun insertIds db w = let val _ = DBM.insertIfNew db (Misc.Pack.packWord w, "haha") in if w = 0w0 then () else insertIds db (w - 0w1) end val db = DBM.new "test"; val _ = DBM.insertIfNew db ("hallo", "leute"); val _ = DBM.insertOrReplace db ("wie", "super"); val _ = print ((DBM.lookup db "wie") ^ "\n"); val _ = insertIds db 0w1000; val _ = DBM.close db; guugelhupf/src/gh/dbm/test/test.cm100644 1751 0 57 7515105045 16534 0ustar mneumannwheelGroup is ../../misc/misc.cm ../dbm.cm test.sml guugelhupf/src/gh/dbm/dbm.cm100644 1751 0 43 7515105046 15334 0ustar mneumannwheelGroup is dbm.sml !make dbm.o dbm.o guugelhupf/src/gh/dbm/dbm.c100644 1751 0 3374 7515105046 15231 0ustar mneumannwheel/* * Copyright (c) 2002 Michael Neumann * * $Rev: 52 $ */ #include #include #include #include #include "libmlton.h" const Word DBM_O_RDONLY = O_RDONLY; const Word DBM_O_RDWR = O_RDWR; const Word DBM_O_CREAT = O_CREAT; const Word DBM_O_EXCL = O_EXCL; const Word DBM_O_FSYNC = O_FSYNC; const Word DBM_O_NOFOLLOW = O_NOFOLLOW; const Word DBM_INSERT_C = DBM_INSERT; const Word DBM_REPLACE_C = DBM_REPLACE; /* NOTE: base must be null-terminated */ Word DBM_open(Pointer base, Word flags, Word mode) { return (Word) dbm_open(base, flags, mode); } void DBM_close(Word db) { dbm_close((DBM*) db); } Int DBM_store(Word db, Pointer key, Pointer data, Word flags) { return dbm_store((DBM*) db, (datum){key, GC_arrayNumElements(key)}, (datum){data, GC_arrayNumElements(data)}, flags); } Int DBM_delete(Word db, Pointer key) { return dbm_delete((DBM*) db, (datum){key, GC_arrayNumElements(key)}); } /* * Return -1 if no element to fetch, otherwise size of data. * The pointer to the data is stored in "buf". */ Int DBM_fetch(Word db, Pointer key, Pointer buf) { datum res; assert (GC_arrayNumElements(buf) >= sizeof(Word)); res = dbm_fetch((DBM*) db, (datum){key, GC_arrayNumElements(key)}); if (NULL == res.dptr) return -1; *((Word*)buf) = (Word)res.dptr; return res.dsize; } void DBM_fetch_buf(Pointer buf, Pointer data) { Word res; int sz = GC_arrayNumElements(data); assert (GC_arrayNumElements(buf) >= sizeof(Word)); res = *((Word*)buf); memcpy((void*)data, (void*)res, sz); } Word DBM_error(Word db) { return dbm_error((DBM*) db); } Int DBM_clearerr(Word db) { return dbm_clearerr((DBM*) db); } Int DBM_dirfno(Word db) { return dbm_dirfno((DBM*) db); } guugelhupf/src/gh/dbm/dbm.sml100644 1751 0 10722 7515105046 15615 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann * * $Rev: 101 $ *) structure DBM :> sig type dbm val new : string -> dbm val new2 : (string * word * word) -> dbm val openReadOnly : string -> dbm val close : dbm -> unit val insertIfNew : dbm -> (string * string) -> unit val insertOrReplace : dbm -> (string * string) -> unit val insert : dbm -> (string * string) -> unit val replace : dbm -> (string * string) -> unit val delete : dbm -> string -> unit val deleteIfExist : dbm -> string -> unit val find : dbm -> string -> string option val lookup : dbm -> string -> string val inDomain : dbm -> string -> bool val hasKey : dbm -> string -> bool end = struct structure Prim = struct type dbm = word val dbm_open = _ffi "DBM_open" : (string * word * word) -> dbm; val dbm_close = _ffi "DBM_close" : dbm -> unit; val dbm_store = _ffi "DBM_store" : (dbm * string * string * word) -> int; val dbm_delete = _ffi "DBM_delete" : (dbm * string) -> int; val dbm_fetch = _ffi "DBM_fetch" : (dbm * string * Misc.CStruct.t) -> int; val dbm_fetch_buf = _ffi "DBM_fetch_buf" : (Misc.CStruct.t * CharArray.array) -> unit; val dbm_error = _ffi "DBM_error" : dbm -> word; val dbm_clearerr = _ffi "DBM_clearerr" : dbm -> int; val dbm_dirfno = _ffi "DBM_dirfno" : dbm -> int; val O_RDONLY = _ffi "DBM_O_RDONLY" : word; val O_RDWR = _ffi "DBM_O_RDWR" : word; val O_CREAT = _ffi "DBM_O_CREAT" : word; val O_EXCL = _ffi "DBM_O_EXCL" : word; val O_FSYNC = _ffi "DBM_O_FSYNC" : word; val O_NOFOLLOW = _ffi "DBM_O_NOFOLLOW" : word; val DBM_INSERT = _ffi "DBM_INSERT_C" : word; val DBM_REPLACE = _ffi "DBM_REPLACE_C" : word; end; exception Error of (OS.syserror * string) type dbm = Prim.dbm fun failure errno = let val syserr = Posix.Error.fromWord errno val errmsg = Posix.Error.errorMsg syserr in raise Error (syserr, errmsg) end fun new2 (base: string, flags: word, mode: word) = let val db = Prim.dbm_open (base ^ "\000", flags, mode) in if db = 0w0 then failure (Word.fromInt (MLton.errno())) else db end fun new (base: string) = new2 (base, Word.orb (Prim.O_RDWR, Prim.O_CREAT), 0w420 (* 0644 *)) fun openReadOnly (base: string) = new2 (base, Prim.O_RDONLY, 0w420) val close = Prim.dbm_close fun genInsert (db, key, data, mode) = let val res = Prim.dbm_store (db, key, data, mode) in case res of ~1 => failure (Prim.dbm_error db) | 1 => raise Error (0, "dbm: Could not insert entry") | _ => () end fun insertIfNew (db: dbm) (key: string, data: string) = genInsert (db, key, data, Prim.DBM_INSERT) fun insertOrReplace (db: dbm) (key: string, data: string) = genInsert (db, key, data, Prim.DBM_REPLACE) val insert = insertIfNew fun delete (db: dbm) (key: string) = let val res = Prim.dbm_delete (db, key) in case res of ~1 => failure (Prim.dbm_error db) | 1 => raise Error (0, "dbm: Could not delete entry") | _ => () end fun deleteIfExist (db: dbm) (key: string) = let val res = Prim.dbm_delete (db, key) in case res of ~1 => failure (Prim.dbm_error db) | _ => () end fun find (db: dbm) (key: string) = let val c = Misc.CStruct.new 4 (* buffer for one word *) val res = Prim.dbm_fetch (db, key, c) in if res = ~1 then NONE else let val buf = CharArray.array (res, #"\000") in Prim.dbm_fetch_buf (c, buf); SOME (CharArray.extract (buf, 0, NONE)) end end fun lookup (db: dbm) (key: string) = case find db key of NONE => raise Error (0, "dbm: Could not fetch entry") | SOME (d) => d fun inDomain (db: dbm) (key: string) = let val c = Misc.CStruct.new 4 (* buffer for one word *) val res = Prim.dbm_fetch (db, key, c) in if res = ~1 then false else true end val hasKey = inDomain (* synonym *) fun replace (db: dbm) (key: string, data: string) = if not (inDomain db key) then raise Error (0, "dbm: Failed to replace entry. Entry do not exist!") else insertOrReplace db (key, data) end guugelhupf/src/gh/dbm/README100644 1751 0 120 7515105046 15145 0ustar mneumannwheelInterface to DBM hash database for MLton. For more information see "man 3 dbm". guugelhupf/src/gh/dbm/Makefile100644 1751 0 120 7515105046 15725 0ustar mneumannwheelinclude ${GH_BASE}/mk/mlton.mk OBJS=dbm.o all: $(OBJS) clean: rm -f $(OBJS) guugelhupf/src/gh/token-stream0.sml100644 1751 0 722 7515105046 16741 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) (* generic definitions for all Tokenizer/TokenStreams *) structure TokenStream0 = struct fun map nextToken f t = case nextToken t of NONE => [] | SOME(tok) => f tok :: map nextToken f t fun toList nextToken = map nextToken (fn x => x) fun app nextToken f t = case nextToken t of NONE => () | SOME(tok) => (f tok; app nextToken f t) end guugelhupf/src/gh/smlnj/ 40755 1751 0 0 7515105216 14577 5ustar mneumannwheelguugelhupf/src/gh/smlnj/hash-string.sml100644 1751 0 1035 7515105046 17640 0ustar mneumannwheel(* hash-string.sml * * COPYRIGHT (c) 1992 by AT&T Bell Laboratories *) structure HashString : sig val hashString : string -> word end = struct fun charToWord c = Word.fromInt(Char.ord c) (* A function to hash a character. The computation is: * * h = 33 * h + 720 + c *) fun hashChar (c, h) = Word.<<(h, 0w5) + h + 0w720 + (charToWord c) (* NOTE: another function we might try is h = 5*h + c, which is used * in STL. *) fun hashString s = CharVector.foldl hashChar 0w0 s end (* HashString *) guugelhupf/src/gh/smlnj/splaytree.sml100644 1751 0 11022 7515105046 17436 0ustar mneumannwheel(* splaytree.sml * * COPYRIGHT (c) 1993 by AT&T Bell Laboratories. See COPYRIGHT file for details. * * Splay tree structure. * *) structure SplayTree : SPLAY_TREE = struct datatype 'a splay = SplayObj of { value : 'a, right : 'a splay, left : 'a splay } | SplayNil datatype 'a ans_t = No | Eq of 'a | Lt of 'a | Gt of 'a fun splay (compf, root) = let fun adj SplayNil = (No,SplayNil,SplayNil) | adj (arg as SplayObj{value,left,right}) = (case compf value of EQUAL => (Eq value, left, right) | GREATER => (case left of SplayNil => (Gt value,SplayNil,right) | SplayObj{value=value',left=left',right=right'} => (case compf value' of EQUAL => (Eq value',left', SplayObj{value=value,left=right',right=right}) | GREATER => (case left' of SplayNil => (Gt value',left',SplayObj{value=value,left=right',right=right}) | _ => let val (V,L,R) = adj left' val rchild = SplayObj{value=value,left=right',right=right} in (V,L,SplayObj{value=value',left=R,right=rchild}) end ) (* end case *) | _ => (case right' of SplayNil => (Lt value',left',SplayObj{value=value,left=right',right=right}) | _ => let val (V,L,R) = adj right' val rchild = SplayObj{value=value,left=R,right=right} val lchild = SplayObj{value=value',left=left',right=L} in (V,lchild,rchild) end ) (* end case *) ) (* end case *) ) (* end case *) | _ => (case right of SplayNil => (Lt value,left,SplayNil) | SplayObj{value=value',left=left',right=right'} => (case compf value' of EQUAL => (Eq value',SplayObj{value=value,left=left,right=left'},right') | LESS => (case right' of SplayNil => (Lt value',SplayObj{value=value,left=left,right=left'},right') | _ => let val (V,L,R) = adj right' val lchild = SplayObj{value=value,left=left,right=left'} in (V,SplayObj{value=value',left=lchild,right=L},R) end ) (* end case *) | _ => (case left' of SplayNil => (Gt value',SplayObj{value=value,left=left,right=left'},right') | _ => let val (V,L,R) = adj left' val rchild = SplayObj{value=value',left=R,right=right'} val lchild = SplayObj{value=value,left=left,right=L} in (V,lchild,rchild) end ) (* end case *) ) (* end case *) ) (* end case *) ) (* end case *) in case adj root of (No,_,_) => (GREATER,SplayNil) | (Eq v,l,r) => (EQUAL,SplayObj{value=v,left=l,right=r}) | (Lt v,l,r) => (LESS,SplayObj{value=v,left=l,right=r}) | (Gt v,l,r) => (GREATER,SplayObj{value=v,left=l,right=r}) end fun lrotate SplayNil = SplayNil | lrotate (arg as SplayObj{value,left,right=SplayNil}) = arg | lrotate (SplayObj{value,left,right=SplayObj{value=v,left=l,right=r}}) = lrotate (SplayObj{value=v,left=SplayObj{value=value,left=left,right=l},right=r}) fun join (SplayNil,SplayNil) = SplayNil | join (SplayNil,t) = t | join (t,SplayNil) = t | join (l,r) = case lrotate l of SplayNil => r (* impossible as l is not SplayNil *) | SplayObj{value,left,right} => SplayObj{value=value,left=left,right=r} end (* SplayTree *) guugelhupf/src/gh/smlnj/splaytree-sig.sml100644 1751 0 1444 7515105046 20205 0ustar mneumannwheel(* splaytree-sig.sml * * COPYRIGHT (c) 1993 by AT&T Bell Laboratories. See COPYRIGHT file for details. * * Signature for a splay tree data structure. * *) signature SPLAY_TREE = sig datatype 'a splay = SplayObj of { value : 'a, right : 'a splay, left : 'a splay } | SplayNil val splay : (('a -> order) * 'a splay) -> (order * 'a splay) (* (r,tree') = splay (cmp,tree) * where tree' is tree adjusted using the comparison function cmp * and, if tree' = SplayObj{value,...}, r = cmp value. * tree' = SplayNil iff tree = SplayNil, in which case r is undefined. *) val join : 'a splay * 'a splay -> 'a splay (* join(t,t') returns a new splay tree formed of t and t' *) end (* SPLAY_TREE *) guugelhupf/src/gh/smlnj/hash-table-fn.sml100644 1751 0 12702 7515105046 20045 0ustar mneumannwheel(* hash-table-fn.sml * * COPYRIGHT (c) 1992 by AT&T Bell Laboratories. * * A hash table functor. It takes a key type with two operations: sameKey and * hashVal as arguments (see hash-key-sig.sml). * * AUTHOR: John Reppy * AT&T Bell Laboratories * Murray Hill, NJ 07974 * jhr@research.att.com *) functor HashTableFn (Key : HASH_KEY) : MONO_HASH_TABLE = struct structure Key = Key open Key structure HTRep = HashTableRep datatype 'a hash_table = HT of { not_found : exn, table : (hash_key, 'a) HTRep.table ref, n_items : int ref } fun index (i, sz) = Word.toIntX(Word.andb(i, Word.fromInt sz - 0w1)) (* Create a new table; the int is a size hint and the exception * is to be raised by find. *) fun mkTable (sizeHint, notFound) = HT{ not_found = notFound, table = ref (HTRep.alloc sizeHint), n_items = ref 0 } (* remove all elements from the table *) fun clear (HT{table, n_items, ...}) = (HTRep.clear(!table); n_items := 0) (* Insert an item. If the key already has an item associated with it, * then the old item is discarded. *) fun insert (tbl as HT{table, n_items, ...}) (key, item) = let val arr = !table val sz = Array.length arr val hash = hashVal key val indx = index (hash, sz) fun look HTRep.NIL = ( Array.update(arr, indx, HTRep.B(hash, key, item, Array.sub(arr, indx))); n_items := !n_items + 1; HTRep.growTableIfNeeded (table, !n_items); HTRep.NIL) | look (HTRep.B(h, k, v, r)) = if ((hash = h) andalso sameKey(key, k)) then HTRep.B(hash, key, item, r) else (case (look r) of HTRep.NIL => HTRep.NIL | rest => HTRep.B(h, k, v, rest) (* end case *)) in case (look (Array.sub (arr, indx))) of HTRep.NIL => () | b => Array.update(arr, indx, b) (* end case *) end (* return true, if the key is in the domain of the table *) fun inDomain (HT{table, ...}) key = let val arr = !table val hash = hashVal key val indx = index (hash, Array.length arr) fun look HTRep.NIL = false | look (HTRep.B(h, k, v, r)) = ((hash = h) andalso sameKey(key, k)) orelse look r in look (Array.sub (arr, indx)) end (* find an item, the table's exception is raised if the item doesn't exist *) fun lookup (HT{table, not_found, ...}) key = let val arr = !table val hash = hashVal key val indx = index (hash, Array.length arr) fun look HTRep.NIL = raise not_found | look (HTRep.B(h, k, v, r)) = if ((hash = h) andalso sameKey(key, k)) then v else look r in look (Array.sub (arr, indx)) end (* look for an item, return NONE if the item doesn't exist *) fun find (HT{table, ...}) key = let val arr = !table val sz = Array.length arr val hash = hashVal key val indx = index (hash, sz) fun look HTRep.NIL = NONE | look (HTRep.B(h, k, v, r)) = if ((hash = h) andalso sameKey(key, k)) then SOME v else look r in look (Array.sub (arr, indx)) end (* Remove an item. The table's exception is raised if * the item doesn't exist. *) fun remove (HT{not_found, table, n_items}) key = let val arr = !table val sz = Array.length arr val hash = hashVal key val indx = index (hash, sz) fun look HTRep.NIL = raise not_found | look (HTRep.B(h, k, v, r)) = if ((hash = h) andalso sameKey(key, k)) then (v, r) else let val (item, r') = look r in (item, HTRep.B(h, k, v, r')) end val (item, bucket) = look (Array.sub (arr, indx)) in Array.update (arr, indx, bucket); n_items := !n_items - 1; item end (* remove *) (* Return the number of items in the table *) fun numItems (HT{n_items, ...}) = !n_items (* return a list of the items in the table *) fun listItems (HT{table = ref arr, n_items, ...}) = HTRep.listItems (arr, n_items) fun listItemsi (HT{table = ref arr, n_items, ...}) = HTRep.listItemsi (arr, n_items) (* Apply a function to the entries of the table *) fun appi f (HT{table, ...}) = HTRep.appi f (! table) fun app f (HT{table, ...}) = HTRep.app f (! table) (* Map a table to a new table that has the same keys and exception *) fun mapi f (HT{table, n_items, not_found}) = HT{ table = ref(HTRep.mapi f (! table)), n_items = ref(!n_items), not_found = not_found } fun map f (HT{table, n_items, not_found}) = HT{ table = ref(HTRep.map f (! table)), n_items = ref(!n_items), not_found = not_found } (* Fold a function over the entries of the table *) fun foldi f init (HT{table, ...}) = HTRep.foldi f init (! table) fun fold f init (HT{table, ...}) = HTRep.fold f init (! table) (* modify the hash-table items in place *) fun modifyi f (HT{table, ...}) = HTRep.modifyi f (!table) fun modify f (HT{table, ...}) = HTRep.modify f (!table) (* remove any hash table items that do not satisfy the given * predicate. *) fun filteri pred (HT{table, n_items, ...}) = n_items := HTRep.filteri pred (! table) fun filter pred (HT{table, n_items, ...}) = n_items := HTRep.filter pred (! table) (* Create a copy of a hash table *) fun copy (HT{table, n_items, not_found}) = HT{ table = ref(HTRep.copy(! table)), n_items = ref(!n_items), not_found = not_found } (* returns a list of the sizes of the various buckets. This is to * allow users to gauge the quality of their hashing function. *) fun bucketSizes (HT{table, ...}) = HTRep.bucketSizes (! table) end (* HashTableFn *) guugelhupf/src/gh/smlnj/hash-key-sig.sml100644 1751 0 1304 7515105046 17701 0ustar mneumannwheel(* hash-key-sig.sml * * COPYRIGHT (c) 1993 by AT&T Bell Laboratories. See COPYRIGHT file for details. * * Abstract hash table keys. This is the argument signature for the hash table * functor (see hash-table-sig.sml and hash-table.sml). * * AUTHOR: John Reppy * AT&T Bell Laboratories * Murray Hill, NJ 07974 * jhr@research.att.com *) signature HASH_KEY = sig type hash_key val hashVal : hash_key -> word (* Compute an unsigned integer key from a hash key. *) val sameKey : (hash_key * hash_key) -> bool (* Return true if two keys are the same. * NOTE: if sameKey(h1, h2), then it must be the * case that (hashVal h1 = hashVal h2). *) end (* HASH_KEY *) guugelhupf/src/gh/smlnj/ord-key-sig.sml100644 1751 0 404 7515105046 17522 0ustar mneumannwheel(* ord-key-sig.sml * * COPYRIGHT (c) 1993 by AT&T Bell Laboratories. See COPYRIGHT file for details. * * Abstract linearly ordered keys. * *) signature ORD_KEY = sig type ord_key val compare : ord_key * ord_key -> order end (* ORD_KEY *) guugelhupf/src/gh/smlnj/lib.cm100644 1751 0 327 7515105046 15746 0ustar mneumannwheelGroup is smlnj.sml hash-key-sig.sml hash-string.sml mono-hash-table-sig.sml hash-table-rep.sml hash-table-fn.sml ord-key-sig.sml ord-map-sig.sml binary-map-fn.sml splaytree-sig.sml splaytree.sml splay-map-fn.sml guugelhupf/src/gh/smlnj/hash-table-rep.sml100644 1751 0 15203 7515105046 20227 0ustar mneumannwheel(* hash-table-rep.sml * * COPYRIGHT (c) 1993 by AT&T Bell Laboratories. * COPYRIGHT (c) 1996 AT&T Research. * * This is the internal representation of hash tables, along with some * utility functions. It is used in both the polymorphic and functor * hash table implementations. * * AUTHOR: John Reppy * AT&T Bell Laboratories * Murray Hill, NJ 07974 * jhr@research.att.com *) structure HashTableRep : sig datatype ('a, 'b) bucket = NIL | B of (word * 'a * 'b * ('a, 'b) bucket) type ('a, 'b) table = ('a, 'b) bucket array val alloc : int -> ('a, 'b) table (* allocate a table of at least the given size *) val growTable : (('a, 'b) table * int) -> ('a, 'b) table (* grow a table to the specified size *) val growTableIfNeeded : (('a, 'b) table ref * int) -> bool (* conditionally grow a table; the second argument is the number * of items currently in the table. *) val clear : ('a, 'b) table -> unit (* remove all items *) val listItems : (('a, 'b) table * int ref) -> 'b list val listItemsi : (('a, 'b) table * int ref) -> ('a * 'b) list val appi : ('a * 'b -> 'c) -> ('a, 'b) table -> unit val app : ('a -> 'b) -> ('c, 'a) table -> unit val mapi : ('a * 'b -> 'c) -> ('a, 'b) table -> ('a, 'c) table val map : ('a -> 'b) -> ('c, 'a) table -> ('c, 'b) table val foldi : ('a * 'b * 'c -> 'c) -> 'c -> ('a, 'b) table -> 'c val fold : ('a * 'b -> 'b) -> 'b -> ('c, 'a) table -> 'b val modify : ('b -> 'b) -> ('a, 'b) table -> unit val modifyi : (('a * 'b) -> 'b) -> ('a, 'b) table -> unit val filteri : ('a * 'b -> bool) -> ('a, 'b) table -> int val filter : ('a -> bool) -> ('b,'a) table -> int val copy : ('a, 'b) table -> ('a, 'b) table val bucketSizes : ('a, 'b) table -> int list end = struct datatype ('a, 'b) bucket = NIL | B of (word * 'a * 'b * ('a, 'b) bucket) type ('a, 'b) table = ('a, 'b) bucket array fun index (i, sz) = Word.toIntX(Word.andb(i, Word.fromInt sz - 0w1)) (* find smallest power of 2 (>= 32) that is >= n *) fun roundUp n = let fun f i = if (i >= n) then i else f(i * 2) in f 32 end (* Create a new table; the int is a size hint and the exception * is to be raised by find. *) fun alloc sizeHint = Array.array(roundUp sizeHint, NIL) (* grow a table to the specified size *) fun growTable (table, newSz) = let val newArr = Array.array (newSz, NIL) fun copy NIL = () | copy (B(h, key, v, rest)) = let val indx = index (h, newSz) in Array.update (newArr, indx, B(h, key, v, Array.sub(newArr, indx))); copy rest end in Array.app copy table; newArr end (* conditionally grow a table; return true if it grew. *) fun growTableIfNeeded (table, nItems) = let val arr = !table val sz = Array.length arr in if (nItems >= sz) then (table := growTable (arr, sz+sz); true) else false end (* remove all items *) fun clear table = Array.modify (fn _ => NIL) table (* return a list of the items in the table *) fun listItems (table, nItems) = let fun f (_, l, 0) = l | f (~1, l, _) = l | f (i, l, n) = let fun g (NIL, l, n) = f (i-1, l, n) | g (B(_, k, v, r), l, n) = g(r, v::l, n-1) in g (Array.sub(table, i), l, n) end in f ((Array.length table) - 1, [], !nItems) end (* listItems *) fun listItemsi (table, nItems) = let fun f (_, l, 0) = l | f (~1, l, _) = l | f (i, l, n) = let fun g (NIL, l, n) = f (i-1, l, n) | g (B(_, k, v, r), l, n) = g(r, (k, v)::l, n-1) in g (Array.sub(table, i), l, n) end in f ((Array.length table) - 1, [], !nItems) end (* listItems *) (* Apply a function to the entries of the table *) fun appi f table = let fun appF NIL = () | appF (B(_, key, item, rest)) = (f (key, item); appF rest) in Array.app appF table end (* appi *) fun app f table = let fun appF NIL = () | appF (B(_, key, item, rest)) = (f item; appF rest) in Array.app appF table end (* app *) (* Map a table to a new table that has the same keys *) fun mapi f table = let fun mapF NIL = NIL | mapF (B(hash, key, item, rest)) = B(hash, key, f (key, item), mapF rest) val newTbl = Array.tabulate ( Array.length table, fn i => mapF (Array.sub(table, i))) in newTbl end (* transform *) (* Map a table to a new table that has the same keys *) fun map f table = let fun mapF NIL = NIL | mapF (B(hash, key, item, rest)) = B(hash, key, f item, mapF rest) val newTbl = Array.tabulate ( Array.length table, fn i => mapF (Array.sub(table, i))) in newTbl end (* map *) fun foldi f init table = let fun foldF (NIL, accum) = accum | foldF (B(hash, key, item, rest), accum) = foldF(rest, f(key, item, accum)) in Array.foldl foldF init table end fun fold f init table = let fun foldF (NIL, accum) = accum | foldF (B(hash, key, item, rest), accum) = foldF(rest, f(item, accum)) in Array.foldl foldF init table end (* modify the hash-table items in place *) fun modify f table = let fun modifyF NIL = NIL | modifyF (B(hash, key, item, rest)) = B(hash, key, f item, modifyF rest) in Array.modify modifyF table end fun modifyi f table = let fun modifyF NIL = NIL | modifyF (B(hash, key, item, rest)) = B(hash, key, f(key, item), modifyF rest) in Array.modify modifyF table end (* remove any hash table items that do not satisfy the given * predicate. Return the number of items left in the table. *) fun filteri pred table = let val nItems = ref 0 fun filterP NIL = NIL | filterP (B(hash, key, item, rest)) = if (pred(key, item)) then ( nItems := !nItems+1; B(hash, key, item, filterP rest)) else filterP rest in Array.modify filterP table; !nItems end (* filteri *) fun filter pred table = let val nItems = ref 0 fun filterP NIL = NIL | filterP (B(hash, key, item, rest)) = if (pred item) then ( nItems := !nItems+1; B(hash, key, item, filterP rest)) else filterP rest in Array.modify filterP table; !nItems end (* filter *) (* Create a copy of a hash table *) fun copy table = Array.tabulate (Array.length table, fn i => Array.sub(table, i)); (* returns a list of the sizes of the various buckets. This is to * allow users to gauge the quality of their hashing function. *) fun bucketSizes table = let fun len (NIL, n) = n | len (B(_, _, _, r), n) = len(r, n+1) in Array.foldr (fn (b, l) => len(b, 0) :: l) [] table end end (* HashTableRep *) guugelhupf/src/gh/smlnj/mono-hash-table-sig.sml100644 1751 0 5337 7515105046 21160 0ustar mneumannwheel(* mono-hash-table-sig.sml * * COPYRIGHT (c) 1992 by AT&T Bell Laboratories. * * The result signature of the hash table functor (see hash-table.sml). * * AUTHOR: John Reppy * AT&T Bell Laboratories * Murray Hill, NJ 07974 * jhr@research.att.com *) signature MONO_HASH_TABLE = sig structure Key : HASH_KEY type 'a hash_table val mkTable : (int * exn) -> 'a hash_table (* Create a new table; the int is a size hint and the exception * is to be raised by find. *) val clear : 'a hash_table -> unit (* remove all elements from the table *) val insert : 'a hash_table -> (Key.hash_key * 'a) -> unit (* Insert an item. If the key already has an item associated with it, * then the old item is discarded. *) val inDomain : 'a hash_table -> Key.hash_key -> bool (* return true, if the key is in the domain of the table *) val lookup : 'a hash_table -> Key.hash_key -> 'a (* Find an item, the table's exception is raised if the item doesn't exist *) val find : 'a hash_table -> Key.hash_key -> 'a option (* Look for an item, return NONE if the item doesn't exist *) val remove : 'a hash_table -> Key.hash_key -> 'a (* Remove an item, returning the item. The table's exception is raised if * the item doesn't exist. *) val numItems : 'a hash_table -> int (* Return the number of items in the table *) val listItems : 'a hash_table -> 'a list val listItemsi : 'a hash_table -> (Key.hash_key * 'a) list (* Return a list of the items (and their keys) in the table *) val app : ('a -> unit) -> 'a hash_table -> unit val appi : ((Key.hash_key * 'a) -> unit) -> 'a hash_table -> unit (* Apply a function to the entries of the table *) val map : ('a -> 'b) -> 'a hash_table -> 'b hash_table val mapi : ((Key.hash_key * 'a) -> 'b) -> 'a hash_table -> 'b hash_table (* Map a table to a new table that has the same keys *) val fold : (('a * 'b) -> 'b) -> 'b -> 'a hash_table -> 'b val foldi : ((Key.hash_key * 'a * 'b) -> 'b) -> 'b -> 'a hash_table -> 'b val modify : ('a -> 'a) -> 'a hash_table -> unit val modifyi : ((Key.hash_key * 'a) -> 'a) -> 'a hash_table -> unit (* modify the hash-table items in place *) (** Also mapPartial?? *) val filter : ('a -> bool) -> 'a hash_table -> unit val filteri : ((Key.hash_key * 'a) -> bool) -> 'a hash_table -> unit (* remove any hash table items that do not satisfy the given * predicate. *) val copy : 'a hash_table -> 'a hash_table (* Create a copy of a hash table *) val bucketSizes : 'a hash_table -> int list (* returns a list of the sizes of the various buckets. This is to * allow users to gauge the quality of their hashing function. *) end (* MONO_HASH_TABLE *) guugelhupf/src/gh/smlnj/test.sml100644 1751 0 1405 7515105046 16371 0ustar mneumannwheelval sdbm_hash = _ffi "sdbm": string * int -> word; structure StringHashKey : HASH_KEY = struct type hash_key = string fun hashVal h = (*HashString.hashString h*) sdbm_hash (h, String.size h) fun sameKey (a, b) = (a = b) end; structure HashTbl = HashTableFn (StringHashKey); exception NotFound; val ht : int HashTbl.hash_table = HashTbl.mkTable (500000, NotFound); (*val ht : (string, int) MyHT.t = MyHT.new (op =) *) val is = TextIO.openIn "wordlist.a"; val line_nr = ref 1; fun loop() = if TextIO.endOfStream is then () else ( let val str = TextIO.inputLine is in HashTbl.insert ht (str, !line_nr); line_nr := !line_nr + 1; loop() end ) val _ = loop(); val _ = print (Int.toString (MLton.size ht)); print "\n"; guugelhupf/src/gh/smlnj/ord-map-sig.sml100644 1751 0 10047 7515105046 17553 0ustar mneumannwheel(* ord-map-sig.sml * * COPYRIGHT (c) 1996 by AT&T Research. See COPYRIGHT file for details. * * Abstract signature of an applicative-style finite maps (dictionaries) * structure over ordered monomorphic keys. *) signature ORD_MAP = sig structure Key : ORD_KEY type 'a map val empty : 'a map (* The empty map *) val isEmpty : 'a map -> bool (* Return true if and only if the map is empty *) val singleton : (Key.ord_key * 'a) -> 'a map (* return the specified singleton map *) val insert : 'a map * Key.ord_key * 'a -> 'a map val insert' : ((Key.ord_key * 'a) * 'a map) -> 'a map (* Insert an item. *) val find : 'a map * Key.ord_key -> 'a option (* Look for an item, return NONE if the item doesn't exist *) val inDomain : ('a map * Key.ord_key) -> bool (* return true, if the key is in the domain of the map *) val remove : 'a map * Key.ord_key -> 'a map * 'a (* Remove an item, returning new map and value removed. * Raises LibBase.NotFound if not found. *) val first : 'a map -> 'a option val firsti : 'a map -> (Key.ord_key * 'a) option (* return the first item in the map (or NONE if it is empty) *) val numItems : 'a map -> int (* Return the number of items in the map *) val listItems : 'a map -> 'a list val listItemsi : 'a map -> (Key.ord_key * 'a) list (* Return an ordered list of the items (and their keys) in the map. *) val listKeys : 'a map -> Key.ord_key list (* return an ordered list of the keys in the map. *) val collate : ('a * 'a -> order) -> ('a map * 'a map) -> order (* given an ordering on the map's range, return an ordering * on the map. *) val unionWith : ('a * 'a -> 'a) -> ('a map * 'a map) -> 'a map val unionWithi : (Key.ord_key * 'a * 'a -> 'a) -> ('a map * 'a map) -> 'a map (* return a map whose domain is the union of the domains of the two input * maps, using the supplied function to define the map on elements that * are in both domains. *) val intersectWith : ('a * 'b -> 'c) -> ('a map * 'b map) -> 'c map val intersectWithi : (Key.ord_key * 'a * 'b -> 'c) -> ('a map * 'b map) -> 'c map (* return a map whose domain is the intersection of the domains of the * two input maps, using the supplied function to define the range. *) val mergeWith : ('a option * 'b option -> 'c option) -> ('a map * 'b map) -> 'c map val mergeWithi : (Key.ord_key * 'a option * 'b option -> 'c option) -> ('a map * 'b map) -> 'c map (* merge two maps using the given function to control the merge. For * each key k in the union of the two maps domains, the function * is applied to the image of the key under the map. If the function * returns SOME y, then (k, y) is added to the resulting map. *) val app : ('a -> unit) -> 'a map -> unit val appi : ((Key.ord_key * 'a) -> unit) -> 'a map -> unit (* Apply a function to the entries of the map in map order. *) val map : ('a -> 'b) -> 'a map -> 'b map val mapi : (Key.ord_key * 'a -> 'b) -> 'a map -> 'b map (* Create a new map by applying a map function to the * name/value pairs in the map. *) val foldl : ('a * 'b -> 'b) -> 'b -> 'a map -> 'b val foldli : (Key.ord_key * 'a * 'b -> 'b) -> 'b -> 'a map -> 'b (* Apply a folding function to the entries of the map * in increasing map order. *) val foldr : ('a * 'b -> 'b) -> 'b -> 'a map -> 'b val foldri : (Key.ord_key * 'a * 'b -> 'b) -> 'b -> 'a map -> 'b (* Apply a folding function to the entries of the map * in decreasing map order. *) val filter : ('a -> bool) -> 'a map -> 'a map val filteri : (Key.ord_key * 'a -> bool) -> 'a map -> 'a map (* Filter out those elements of the map that do not satisfy the * predicate. The filtering is done in increasing map order. *) val mapPartial : ('a -> 'b option) -> 'a map -> 'b map val mapPartiali : (Key.ord_key * 'a -> 'b option) -> 'a map -> 'b map (* map a partial function over the elements of a map in increasing * map order. *) end (* ORD_MAP *) guugelhupf/src/gh/smlnj/binary-map-fn.sml100644 1751 0 32001 7515105046 20066 0ustar mneumannwheel(* binary-map-fn.sml * * COPYRIGHT (c) 1993 by AT&T Bell Laboratories. See COPYRIGHT file for details. * * This code was adapted from Stephen Adams' binary tree implementation * of applicative integer sets. * * Copyright 1992 Stephen Adams. * * This software may be used freely provided that: * 1. This copyright notice is attached to any copy, derived work, * or work including all or part of this software. * 2. Any derived work must contain a prominent notice stating that * it has been altered from the original. * * * Name(s): Stephen Adams. * Department, Institution: Electronics & Computer Science, * University of Southampton * Address: Electronics & Computer Science * University of Southampton * Southampton SO9 5NH * Great Britian * E-mail: sra@ecs.soton.ac.uk * * Comments: * * 1. The implementation is based on Binary search trees of Bounded * Balance, similar to Nievergelt & Reingold, SIAM J. Computing * 2(1), March 1973. The main advantage of these trees is that * they keep the size of the tree in the node, giving a constant * time size operation. * * 2. The bounded balance criterion is simpler than N&R's alpha. * Simply, one subtree must not have more than `weight' times as * many elements as the opposite subtree. Rebalancing is * guaranteed to reinstate the criterion for weight>2.23, but * the occasional incorrect behaviour for weight=2 is not * detrimental to performance. * *) functor BinaryMapFn (K : ORD_KEY) : ORD_MAP = struct structure Key = K (* ** val weight = 3 ** fun wt i = weight * i *) fun wt (i : int) = i + i + i datatype 'a map = E | T of { key : K.ord_key, value : 'a, cnt : int, left : 'a map, right : 'a map } val empty = E fun isEmpty E = true | isEmpty _ = false fun numItems E = 0 | numItems (T{cnt,...}) = cnt (* return the first item in the map (or NONE if it is empty) *) fun first E = NONE | first (T{value, left=E, ...}) = SOME value | first (T{left, ...}) = first left (* return the first item in the map and its key (or NONE if it is empty) *) fun firsti E = NONE | firsti (T{key, value, left=E, ...}) = SOME(key, value) | firsti (T{left, ...}) = firsti left local fun N(k,v,E,E) = T{key=k,value=v,cnt=1,left=E,right=E} | N(k,v,E,r as T n) = T{key=k,value=v,cnt=1+(#cnt n),left=E,right=r} | N(k,v,l as T n,E) = T{key=k,value=v,cnt=1+(#cnt n),left=l,right=E} | N(k,v,l as T n,r as T n') = T{key=k,value=v,cnt=1+(#cnt n)+(#cnt n'),left=l,right=r} fun single_L (a,av,x,T{key=b,value=bv,left=y,right=z,...}) = N(b,bv,N(a,av,x,y),z) | single_L _ = raise Match fun single_R (b,bv,T{key=a,value=av,left=x,right=y,...},z) = N(a,av,x,N(b,bv,y,z)) | single_R _ = raise Match fun double_L (a,av,w,T{key=c,value=cv,left=T{key=b,value=bv,left=x,right=y,...},right=z,...}) = N(b,bv,N(a,av,w,x),N(c,cv,y,z)) | double_L _ = raise Match fun double_R (c,cv,T{key=a,value=av,left=w,right=T{key=b,value=bv,left=x,right=y,...},...},z) = N(b,bv,N(a,av,w,x),N(c,cv,y,z)) | double_R _ = raise Match fun T' (k,v,E,E) = T{key=k,value=v,cnt=1,left=E,right=E} | T' (k,v,E,r as T{right=E,left=E,...}) = T{key=k,value=v,cnt=2,left=E,right=r} | T' (k,v,l as T{right=E,left=E,...},E) = T{key=k,value=v,cnt=2,left=l,right=E} | T' (p as (_,_,E,T{left=T _,right=E,...})) = double_L p | T' (p as (_,_,T{left=E,right=T _,...},E)) = double_R p (* these cases almost never happen with small weight*) | T' (p as (_,_,E,T{left=T{cnt=ln,...},right=T{cnt=rn,...},...})) = if ln < rn then single_L p else double_L p | T' (p as (_,_,T{left=T{cnt=ln,...},right=T{cnt=rn,...},...},E)) = if ln > rn then single_R p else double_R p | T' (p as (_,_,E,T{left=E,...})) = single_L p | T' (p as (_,_,T{right=E,...},E)) = single_R p | T' (p as (k,v,l as T{cnt=ln,left=ll,right=lr,...}, r as T{cnt=rn,left=rl,right=rr,...})) = if rn >= wt ln then (*right is too big*) let val rln = numItems rl val rrn = numItems rr in if rln < rrn then single_L p else double_L p end else if ln >= wt rn then (*left is too big*) let val lln = numItems ll val lrn = numItems lr in if lrn < lln then single_R p else double_R p end else T{key=k,value=v,cnt=ln+rn+1,left=l,right=r} local fun min (T{left=E,key,value,...}) = (key,value) | min (T{left,...}) = min left | min _ = raise Match fun delmin (T{left=E,right,...}) = right | delmin (T{key,value,left,right,...}) = T'(key,value,delmin left,right) | delmin _ = raise Match in fun delete' (E,r) = r | delete' (l,E) = l | delete' (l,r) = let val (mink,minv) = min r in T'(mink,minv,l,delmin r) end end in fun mkDict () = E fun singleton (x,v) = T{key=x,value=v,cnt=1,left=E,right=E} fun insert (E,x,v) = T{key=x,value=v,cnt=1,left=E,right=E} | insert (T(set as {key,left,right,value,...}),x,v) = case K.compare (key,x) of GREATER => T'(key,value,insert(left,x,v),right) | LESS => T'(key,value,left,insert(right,x,v)) | _ => T{key=x,value=v,left=left,right=right,cnt= #cnt set} fun insert' ((k, x), m) = insert(m, k, x) fun inDomain (set, x) = let fun mem E = false | mem (T(n as {key,left,right,...})) = (case K.compare (x,key) of GREATER => mem right | EQUAL => true | LESS => mem left (* end case *)) in mem set end fun find (set, x) = let fun mem E = NONE | mem (T(n as {key,left,right,...})) = (case K.compare (x,key) of GREATER => mem right | EQUAL => SOME(#value n) | LESS => mem left (* end case *)) in mem set end fun remove (E,x) = raise LibBase.NotFound | remove (set as T{key,left,right,value,...},x) = ( case K.compare (key,x) of GREATER => let val (left', v) = remove(left, x) in (T'(key, value, left', right), v) end | LESS => let val (right', v) = remove (right, x) in (T'(key, value, left, right'), v) end | _ => (delete'(left,right),value) (* end case *)) fun listItems d = let fun d2l (E, l) = l | d2l (T{key,value,left,right,...}, l) = d2l(left, value::(d2l(right,l))) in d2l (d,[]) end fun listItemsi d = let fun d2l (E, l) = l | d2l (T{key,value,left,right,...}, l) = d2l(left, (key,value)::(d2l(right,l))) in d2l (d,[]) end fun listKeys d = let fun d2l (E, l) = l | d2l (T{key,left,right,...}, l) = d2l(left, key::(d2l(right,l))) in d2l (d,[]) end local fun next ((t as T{right, ...})::rest) = (t, left(right, rest)) | next _ = (E, []) and left (E, rest) = rest | left (t as T{left=l, ...}, rest) = left(l, t::rest) in fun collate cmpRng (s1, s2) = let fun cmp (t1, t2) = (case (next t1, next t2) of ((E, _), (E, _)) => EQUAL | ((E, _), _) => LESS | (_, (E, _)) => GREATER | ((T{key=x1, value=y1, ...}, r1), (T{key=x2, value=y2, ...}, r2)) => ( case Key.compare(x1, x2) of EQUAL => (case cmpRng(y1, y2) of EQUAL => cmp (r1, r2) | order => order (* end case *)) | order => order (* end case *)) (* end case *)) in cmp (left(s1, []), left(s2, [])) end end (* local *) fun appi f d = let fun app' E = () | app' (T{key,value,left,right,...}) = ( app' left; f(key, value); app' right) in app' d end fun app f d = let fun app' E = () | app' (T{value,left,right,...}) = ( app' left; f value; app' right) in app' d end fun mapi f d = let fun map' E = E | map' (T{key,value,left,right,cnt}) = let val left' = map' left val value' = f(key, value) val right' = map' right in T{cnt=cnt, key=key, value=value', left = left', right = right'} end in map' d end fun map f d = mapi (fn (_, x) => f x) d fun foldli f init d = let fun fold (E, v) = v | fold (T{key,value,left,right,...}, v) = fold (right, f(key, value, fold(left, v))) in fold (d, init) end fun foldl f init d = foldli (fn (_, v, accum) => f (v, accum)) init d fun foldri f init d = let fun fold (E,v) = v | fold (T{key,value,left,right,...},v) = fold (left, f(key, value, fold(right, v))) in fold (d, init) end fun foldr f init d = foldri (fn (_, v, accum) => f (v, accum)) init d (** To be implemented ** val filter : ('a -> bool) -> 'a map -> 'a map val filteri : (Key.ord_key * 'a -> bool) -> 'a map -> 'a map **) end (* local *) (* the following are generic implementations of the unionWith, intersectWith, * and mergeWith operetions. These should be specialized for the internal * representations at some point. *) fun unionWith f (m1, m2) = let fun ins f (key, x, m) = (case find(m, key) of NONE => insert(m, key, x) | (SOME x') => insert(m, key, f(x, x')) (* end case *)) in if (numItems m1 > numItems m2) then foldli (ins (fn (a, b) => f (b, a))) m1 m2 else foldli (ins f) m2 m1 end fun unionWithi f (m1, m2) = let fun ins f (key, x, m) = (case find(m, key) of NONE => insert(m, key, x) | (SOME x') => insert(m, key, f(key, x, x')) (* end case *)) in if (numItems m1 > numItems m2) then foldli (ins (fn (k, a, b) => f (k, b, a))) m1 m2 else foldli (ins f) m2 m1 end fun intersectWith f (m1, m2) = let (* iterate over the elements of m1, checking for membership in m2 *) fun intersect f (m1, m2) = let fun ins (key, x, m) = (case find(m2, key) of NONE => m | (SOME x') => insert(m, key, f(x, x')) (* end case *)) in foldli ins empty m1 end in if (numItems m1 > numItems m2) then intersect f (m1, m2) else intersect (fn (a, b) => f(b, a)) (m2, m1) end fun intersectWithi f (m1, m2) = let (* iterate over the elements of m1, checking for membership in m2 *) fun intersect f (m1, m2) = let fun ins (key, x, m) = (case find(m2, key) of NONE => m | (SOME x') => insert(m, key, f(key, x, x')) (* end case *)) in foldli ins empty m1 end in if (numItems m1 > numItems m2) then intersect f (m1, m2) else intersect (fn (k, a, b) => f(k, b, a)) (m2, m1) end fun mergeWith f (m1, m2) = let fun merge ([], [], m) = m | merge ((k1, x1)::r1, [], m) = mergef (k1, SOME x1, NONE, r1, [], m) | merge ([], (k2, x2)::r2, m) = mergef (k2, NONE, SOME x2, [], r2, m) | merge (m1 as ((k1, x1)::r1), m2 as ((k2, x2)::r2), m) = ( case Key.compare (k1, k2) of LESS => mergef (k1, SOME x1, NONE, r1, m2, m) | EQUAL => mergef (k1, SOME x1, SOME x2, r1, r2, m) | GREATER => mergef (k2, NONE, SOME x2, m1, r2, m) (* end case *)) and mergef (k, x1, x2, r1, r2, m) = (case f (x1, x2) of NONE => merge (r1, r2, m) | SOME y => merge (r1, r2, insert(m, k, y)) (* end case *)) in merge (listItemsi m1, listItemsi m2, empty) end fun mergeWithi f (m1, m2) = let fun merge ([], [], m) = m | merge ((k1, x1)::r1, [], m) = mergef (k1, SOME x1, NONE, r1, [], m) | merge ([], (k2, x2)::r2, m) = mergef (k2, NONE, SOME x2, [], r2, m) | merge (m1 as ((k1, x1)::r1), m2 as ((k2, x2)::r2), m) = ( case Key.compare (k1, k2) of LESS => mergef (k1, SOME x1, NONE, r1, m2, m) | EQUAL => mergef (k1, SOME x1, SOME x2, r1, r2, m) | GREATER => mergef (k2, NONE, SOME x2, m1, r2, m) (* end case *)) and mergef (k, x1, x2, r1, r2, m) = (case f (k, x1, x2) of NONE => merge (r1, r2, m) | SOME y => merge (r1, r2, insert(m, k, y)) (* end case *)) in merge (listItemsi m1, listItemsi m2, empty) end (* this is a generic implementation of filter. It should * be specialized to the data-structure at some point. *) fun filter predFn m = let fun f (key, item, m) = if predFn item then insert(m, key, item) else m in foldli f empty m end fun filteri predFn m = let fun f (key, item, m) = if predFn(key, item) then insert(m, key, item) else m in foldli f empty m end (* this is a generic implementation of mapPartial. It should * be specialized to the data-structure at some point. *) fun mapPartial f m = let fun g (key, item, m) = (case f item of NONE => m | (SOME item') => insert(m, key, item') (* end case *)) in foldli g empty m end fun mapPartiali f m = let fun g (key, item, m) = (case f(key, item) of NONE => m | (SOME item') => insert(m, key, item') (* end case *)) in foldli g empty m end end (* functor BinaryMapFn *) guugelhupf/src/gh/smlnj/smlnj.sml100644 1751 0 137 7515105046 16516 0ustar mneumannwheelstructure LibBase = struct exception NotFound exception Impossible of string end; guugelhupf/src/gh/smlnj/splay-map-fn.sml100644 1751 0 30544 7515105046 17744 0ustar mneumannwheel(* splay-map-fn.sml * * COPYRIGHT (c) 1993 by AT&T Bell Laboratories. See COPYRIGHT file for details. * * Functor implementing dictionaries using splay trees. * *) functor SplayMapFn (K : ORD_KEY) : ORD_MAP = struct structure Key = K open SplayTree datatype 'a map = EMPTY | MAP of { root : (K.ord_key * 'a) splay ref, nobj : int } fun cmpf k (k', _) = K.compare(k',k) val empty = EMPTY fun isEmpty EMPTY = true | isEmpty _ = false (* return the first item in the map (or NONE if it is empty) *) fun first EMPTY = NONE | first (MAP{root, ...}) = let fun f (SplayObj{value=(_, value), left=SplayNil, ...}) = SOME value | f (SplayObj{left, ...}) = f left | f SplayNil = raise Fail "SplayMapFn.first" in f (!root) end (* return the first item in the map and its key (or NONE if it is empty) *) fun firsti EMPTY = NONE | firsti (MAP{root, ...}) = let fun f (SplayObj{value=(key, value), left=SplayNil, ...}) = SOME(key, value) | f (SplayObj{left, ...}) = f left | f SplayNil = raise Fail "SplayMapFn.firsti" in f (!root) end fun singleton (key, v) = MAP{nobj=1,root=ref(SplayObj{value=(key,v),left=SplayNil,right=SplayNil})} (* Insert an item. *) fun insert (EMPTY,key,v) = MAP{nobj=1,root=ref(SplayObj{value=(key,v),left=SplayNil,right=SplayNil})} | insert (MAP{root,nobj},key,v) = case splay (cmpf key, !root) of (EQUAL,SplayObj{value,left,right}) => MAP{nobj=nobj,root=ref(SplayObj{value=(key,v),left=left,right=right})} | (LESS,SplayObj{value,left,right}) => MAP{ nobj=nobj+1, root=ref(SplayObj{value=(key,v),left=SplayObj{value=value,left=left,right=SplayNil},right=right}) } | (GREATER,SplayObj{value,left,right}) => MAP{ nobj=nobj+1, root=ref(SplayObj{ value=(key,v), left=left, right=SplayObj{value=value,left=SplayNil,right=right} }) } | (_,SplayNil) => raise LibBase.Impossible "SplayMapFn.insert SplayNil" fun insert' ((k, x), m) = insert(m, k, x) fun inDomain (EMPTY, _) = false | inDomain (MAP{root,nobj}, key) = (case splay (cmpf key, !root) of (EQUAL, r as SplayObj{value,...}) => (root := r; true) | (_, r) => (root := r; false) (* end case *)) (* Look for an item, return NONE if the item doesn't exist *) fun find (EMPTY,_) = NONE | find (MAP{root,nobj},key) = (case splay (cmpf key, !root) of (EQUAL, r as SplayObj{value,...}) => (root := r; SOME(#2 value)) | (_, r) => (root := r; NONE)) (* Remove an item. * Raise LibBase.NotFound if not found *) fun remove (EMPTY, _) = raise LibBase.NotFound | remove (MAP{root,nobj}, key) = (case (splay (cmpf key, !root)) of (EQUAL, SplayObj{value, left, right}) => if nobj = 1 then (EMPTY, #2 value) else (MAP{root=ref(join(left,right)),nobj=nobj-1}, #2 value) | (_,r) => (root := r; raise LibBase.NotFound) (* end case *)) (* Return the number of items in the table *) fun numItems EMPTY = 0 | numItems (MAP{nobj,...}) = nobj (* Return a list of the items (and their keys) in the dictionary *) fun listItems EMPTY = [] | listItems (MAP{root,...}) = let fun apply (SplayNil, l) = l | apply (SplayObj{value=(_, v), left, right}, l) = apply(left, v::(apply (right,l))) in apply (!root, []) end fun listItemsi EMPTY = [] | listItemsi (MAP{root,...}) = let fun apply (SplayNil,l) = l | apply (SplayObj{value,left,right},l) = apply(left, value::(apply (right,l))) in apply (!root,[]) end fun listKeys EMPTY = [] | listKeys (MAP{root,...}) = let fun apply (SplayNil, l) = l | apply (SplayObj{value=(key, _),left,right},l) = apply(left, key::(apply (right,l))) in apply (!root, []) end local fun next ((t as SplayObj{right, ...})::rest) = (t, left(right, rest)) | next _ = (SplayNil, []) and left (SplayNil, rest) = rest | left (t as SplayObj{left=l, ...}, rest) = left(l, t::rest) in fun collate cmpRng (EMPTY, EMPTY) = EQUAL | collate cmpRng (EMPTY, _) = LESS | collate cmpRng (_, EMPTY) = GREATER | collate cmpRng (MAP{root=s1, ...}, MAP{root=s2, ...}) = let fun cmp (t1, t2) = (case (next t1, next t2) of ((SplayNil, _), (SplayNil, _)) => EQUAL | ((SplayNil, _), _) => LESS | (_, (SplayNil, _)) => GREATER | ((SplayObj{value=(x1, y1), ...}, r1), (SplayObj{value=(x2, y2), ...}, r2) ) => ( case Key.compare(x1, x2) of EQUAL => (case cmpRng (y1, y2) of EQUAL => cmp (r1, r2) | order => order (* end case *)) | order => order (* end case *)) (* end case *)) in cmp (left(!s1, []), left(!s2, [])) end end (* local *) (* Apply a function to the entries of the dictionary *) fun appi af EMPTY = () | appi af (MAP{root,...}) = let fun apply SplayNil = () | apply (SplayObj{value,left,right}) = (apply left; af value; apply right) in apply (!root) end fun app af EMPTY = () | app af (MAP{root,...}) = let fun apply SplayNil = () | apply (SplayObj{value=(_,value),left,right}) = (apply left; af value; apply right) in apply (!root) end (* fun revapp af (MAP{root,...}) = let fun apply SplayNil = () | apply (SplayObj{value,left,right}) = (apply right; af value; apply left) in apply (!root) end *) (* Fold function *) fun foldri (abf : K.ord_key * 'a * 'b -> 'b) b EMPTY = b | foldri (abf : K.ord_key * 'a * 'b -> 'b) b (MAP{root,...}) = let fun apply (SplayNil : (K.ord_key * 'a) splay, b) = b | apply (SplayObj{value,left,right},b) = apply(left,abf(#1 value,#2 value,apply(right,b))) in apply (!root,b) end fun foldr (abf : 'a * 'b -> 'b) b EMPTY = b | foldr (abf : 'a * 'b -> 'b) b (MAP{root,...}) = let fun apply (SplayNil : (K.ord_key * 'a) splay, b) = b | apply (SplayObj{value=(_,value),left,right},b) = apply(left,abf(value,apply(right,b))) in apply (!root,b) end fun foldli (abf : K.ord_key * 'a * 'b -> 'b) b EMPTY = b | foldli (abf : K.ord_key * 'a * 'b -> 'b) b (MAP{root,...}) = let fun apply (SplayNil : (K.ord_key * 'a) splay, b) = b | apply (SplayObj{value,left,right},b) = apply(right,abf(#1 value,#2 value,apply(left,b))) in apply (!root,b) end fun foldl (abf : 'a * 'b -> 'b) b EMPTY = b | foldl (abf : 'a * 'b -> 'b) b (MAP{root,...}) = let fun apply (SplayNil : (K.ord_key * 'a) splay, b) = b | apply (SplayObj{value=(_,value),left,right},b) = apply(right,abf(value,apply(left,b))) in apply (!root,b) end (* Map a table to a new table that has the same keys*) fun mapi (af : K.ord_key * 'a -> 'b) EMPTY = EMPTY | mapi (af : K.ord_key * 'a -> 'b) (MAP{root,nobj}) = let fun ap (SplayNil : (K.ord_key * 'a) splay) = SplayNil | ap (SplayObj{value,left,right}) = let val left' = ap left val value' = (#1 value, af value) in SplayObj{value = value', left = left', right = ap right} end in MAP{root = ref(ap (!root)), nobj = nobj} end fun map (af : 'a -> 'b) EMPTY = EMPTY | map (af : 'a -> 'b) (MAP{root,nobj}) = let fun ap (SplayNil : (K.ord_key * 'a) splay) = SplayNil | ap (SplayObj{value,left,right}) = let val left' = ap left val value' = (#1 value, af (#2 value)) in SplayObj{value = value', left = left', right = ap right} end in MAP{root = ref(ap (!root)), nobj = nobj} end (* the following are generic implementations of the unionWith, intersectWith, * and mergeWith operetions. These should be specialized for the internal * representations at some point. *) fun unionWith f (m1, m2) = let fun ins f (key, x, m) = (case find(m, key) of NONE => insert(m, key, x) | (SOME x') => insert(m, key, f(x, x')) (* end case *)) in if (numItems m1 > numItems m2) then foldli (ins (fn (a, b) => f(b, a))) m1 m2 else foldli (ins f) m2 m1 end fun unionWithi f (m1, m2) = let fun ins f (key, x, m) = (case find(m, key) of NONE => insert(m, key, x) | (SOME x') => insert(m, key, f(key, x, x')) (* end case *)) in if (numItems m1 > numItems m2) then foldli (ins (fn (k, a, b) => f(k, b, a))) m1 m2 else foldli (ins f) m2 m1 end fun intersectWith f (m1, m2) = let (* iterate over the elements of m1, checking for membership in m2 *) fun intersect f (m1, m2) = let fun ins (key, x, m) = (case find(m2, key) of NONE => m | (SOME x') => insert(m, key, f(x, x')) (* end case *)) in foldli ins empty m1 end in if (numItems m1 > numItems m2) then intersect f (m1, m2) else intersect (fn (a, b) => f(b, a)) (m2, m1) end fun intersectWithi f (m1, m2) = let (* iterate over the elements of m1, checking for membership in m2 *) fun intersect f (m1, m2) = let fun ins (key, x, m) = (case find(m2, key) of NONE => m | (SOME x') => insert(m, key, f(key, x, x')) (* end case *)) in foldli ins empty m1 end in if (numItems m1 > numItems m2) then intersect f (m1, m2) else intersect (fn (k, a, b) => f(k, b, a)) (m2, m1) end fun mergeWith f (m1, m2) = let fun merge ([], [], m) = m | merge ((k1, x1)::r1, [], m) = mergef (k1, SOME x1, NONE, r1, [], m) | merge ([], (k2, x2)::r2, m) = mergef (k2, NONE, SOME x2, [], r2, m) | merge (m1 as ((k1, x1)::r1), m2 as ((k2, x2)::r2), m) = ( case Key.compare (k1, k2) of LESS => mergef (k1, SOME x1, NONE, r1, m2, m) | EQUAL => mergef (k1, SOME x1, SOME x2, r1, r2, m) | GREATER => mergef (k2, NONE, SOME x2, m1, r2, m) (* end case *)) and mergef (k, x1, x2, r1, r2, m) = (case f (x1, x2) of NONE => merge (r1, r2, m) | SOME y => merge (r1, r2, insert(m, k, y)) (* end case *)) in merge (listItemsi m1, listItemsi m2, empty) end fun mergeWithi f (m1, m2) = let fun merge ([], [], m) = m | merge ((k1, x1)::r1, [], m) = mergef (k1, SOME x1, NONE, r1, [], m) | merge ([], (k2, x2)::r2, m) = mergef (k2, NONE, SOME x2, [], r2, m) | merge (m1 as ((k1, x1)::r1), m2 as ((k2, x2)::r2), m) = ( case Key.compare (k1, k2) of LESS => mergef (k1, SOME x1, NONE, r1, m2, m) | EQUAL => mergef (k1, SOME x1, SOME x2, r1, r2, m) | GREATER => mergef (k2, NONE, SOME x2, m1, r2, m) (* end case *)) and mergef (k, x1, x2, r1, r2, m) = (case f (k, x1, x2) of NONE => merge (r1, r2, m) | SOME y => merge (r1, r2, insert(m, k, y)) (* end case *)) in merge (listItemsi m1, listItemsi m2, empty) end (* this is a generic implementation of mapPartial. It should * be specialized to the data-structure at some point. *) fun mapPartial f m = let fun g (key, item, m) = (case f item of NONE => m | (SOME item') => insert(m, key, item') (* end case *)) in foldli g empty m end fun mapPartiali f m = let fun g (key, item, m) = (case f(key, item) of NONE => m | (SOME item') => insert(m, key, item') (* end case *)) in foldli g empty m end (* this is a generic implementation of filter. It should * be specialized to the data-structure at some point. *) fun filter predFn m = let fun f (key, item, m) = if predFn item then insert(m, key, item) else m in foldli f empty m end fun filteri predFn m = let fun f (key, item, m) = if predFn(key, item) then insert(m, key, item) else m in foldli f empty m end end (* SplayDictFn *) guugelhupf/src/gh/query/ 40755 1751 0 0 7515105216 14621 5ustar mneumannwheelguugelhupf/src/gh/query/ranking.lex100644 1751 0 1542 7515105046 17064 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) datatype lexresult = EOF | TERM of string | RANKING of int | RANKING_POS | RANKING_NEG fun eof () = EOF %% %structure RankingLex %reject %s TRM; sign = [-+]; digit = [0-9]; ws = [\ \t\n]; nws = [^\ \t\n]; %% {ws}+ => (lex()); {nws}+ => (YYBEGIN(INITIAL); TERM(yytext)); "(" {sign}? {digit}{1,3} ")" => ( let val rank = String.substring (yytext, 1, String.size yytext - 2) in YYBEGIN(TRM); RANKING (valOf (Int.fromString rank)) end); [^-+\ \t\n] {nws}* => (TERM(yytext)); "+" => (YYBEGIN(TRM); RANKING_POS); "-" => (YYBEGIN(TRM); RANKING_NEG); guugelhupf/src/gh/query/ranked-query.sml100644 1751 0 12337 7515105046 20071 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) (* tokenizes and filters stopwords *) structure SimpleAnalyser = struct open DS open Tokenizer.CharTableTransform structure Tokenizer = StringTokenizer structure TS = StopwordFilterFn(structure TS = Tokenizer) fun tokenize (str: string, charTable) : string list = let val ts = Tokenizer.new (str, charTable) fun tokenTerm (Token tok) = #term tok in Tokenizer.map tokenTerm ts end (* return two lists, the first which contains the passed tokens, the second the filtered ones *) fun filter (l: 'a list, itemFn: 'a -> string, stopwordTable: TS.table) : ('a list * 'a list) = case l of [] => ([], []) | (x :: xs) => (let val (a, b) = filter (xs, itemFn, stopwordTable) in if HashTable.hasKey stopwordTable (itemFn x) then (a, x :: b) else (x :: a, b) end) end structure RankedQuery = struct open DS open RankingParser (* maps doc_id's to ranks *) structure DocIdMap = SplayMapFn (struct type ord_key = doc_id val compare = Word.compare end) fun analyseQuery {strm, charTable, stopwordTable, outputFn} = let val p = outputFn val q = RankingParser.parse strm val _ = (print "! parsed: "; RankingParser.output q p) fun fld (RankedTerm (term, rank), lst) = let val terms = SimpleAnalyser.tokenize (term, charTable) in lst @ List.map (fn t => RankedTerm(t, rank)) terms end val q = List.foldl fld [] q val _ = (print "! tokenized: "; RankingParser.output q p) val (q, stopwords) = SimpleAnalyser.filter (q, (fn (RankedTerm (i,_)) => i), stopwordTable) val _ = (print "! filtered: "; RankingParser.output q p) val _ = (print "! stopwords: "; List.app (fn (RankedTerm (i,_)) => print (i ^ " ")) stopwords; print "\n") in q end fun doQuery (rankedTerms, file, hash) = let val res = FrequencyInvertedList.lookupTermsInFile { file = file, hash = hash, terms = List.map (fn (RankedTerm(s,_)) => s) rankedTerms } fun comb (RankedTerm (term, rank), (t, queryResult)) = let val _ = assert (term = t) in (term, rank, queryResult) (* queryResult: (doc_id * word) list *) end in ListPair.map comb (rankedTerms, res) end (* each document is ranked as follows: "frequency * rank" *) fun rankQuery l scoreFn = let val map : (int ref) DocIdMap.map = DocIdMap.empty fun sort c [] = [] | sort c (s::xs) = sort c (List.filter (fn x => c(x, s) = LESS) xs) @ (s :: sort c (List.filter (fn x => c(x, s) = EQUAL) xs)) @ sort c (List.filter (fn x => c(x, s) = GREATER) xs) fun rank ((_, rank, queryResult), map) = let fun rank2 ((docid, freq), map) = let val score = scoreFn {freq = Word.toInt freq, rank = rank} in case DocIdMap.find (map, docid) of NONE => DocIdMap.insert (map, docid, ref score) | SOME(rank) => (rank := (!rank) + score; map) end in List.foldl rank2 map queryResult end val rankedMap = List.foldl rank map l val rankedList = List.map (fn (docid, rank) => (docid, !rank)) (DocIdMap.listItemsi rankedMap) (* docid, rank *) in sort (fn ((_, rank), (_, rank2)) => Int.compare (rank2, rank)) rankedList end fun outputQuery l outputFn = let val wordToString = Int.toString o Word.toInt val intToString = Int.toString (* docid rank/score *) fun output (docid, rank) = outputFn (wordToString docid ^ " " ^ intToString rank ^ "\n") in List.app output l end fun query {strm, charTable, stopwordTable, outputFn, file, hash, scoreFn} = let val l = analyseQuery {strm = strm, charTable = charTable, stopwordTable = stopwordTable, outputFn = outputFn} val l = doQuery (l, file, hash) val l = rankQuery l scoreFn in l end end (* TEST *) (* val q = RankedQuery.query {strm = TextIO.stdIn, charTable = Tokenizer.CharTableTransform.German, stopwordTable = SimpleAnalyser.TS.mkTableFromFile "../filter/stopWords.en", outputFn = (fn s => TextIO.output (TextIO.stdErr, s)), file = "test.index", hash = Hashing.sdbmHash, scoreFn = (fn {freq, rank} => freq * rank) } val _ = RankedQuery.outputQuery q print *) guugelhupf/src/gh/query/query.cm100644 1751 0 123 7515105046 16361 0ustar mneumannwheelGroup is !make ranking.lex.sml ranking.lex.sml ranking-parser.sml ranked-query.sml guugelhupf/src/gh/query/query-inv-list.c100644 1751 0 7444 7515105046 20004 0ustar mneumannwheel/* * Copyright (c) 2002 Michael Neumann */ /* * - load header and index into main memory * - mmap the data */ #include #include #include #include #include #include "libmlton.h" /********************************************************/ /* Helper Functions */ /********************************************************/ /* * Call this function before reading/writing any data, * quite after opening the file. */ int getFileLength(int handle) { int length; length = lseek(handle, 0, SEEK_END); lseek(handle, 0, SEEK_SET); return length; } unsigned long sdbm_hash(char* str, int length) { unsigned long hash = 0; while (length--) hash = (*((unsigned char*)str)++) + (hash << 6) + (hash << 16) - hash; return hash; } /********************************************************/ const Word headerSize = 28; const Word emptyBucket = 0xFFFFFFFF; struct FreqInvList_Header { char kennung[16]; Word version; Word flags; Word indexSize; }; struct FreqInvList_Struct { struct FreqInvList_Header header; int fd; Word* index; void* data; int dataSize; }; /* * read the header and index, and mmap the data. */ void FreqInvList_open(struct FreqInvList_Struct* s, char *filename) { ssize_t cnt; int fileLength; int offs; /* TODO: can remove? */ if (sizeof(struct FreqInvList_Header) != headerSize) { perror("FreqInvList_open: Wrong header struct!"); exit(-1); } /* open file */ s->fd = open(filename, O_RDONLY); if (-1 == s->fd) { perror("FreqInvList_open: Failed to open index!"); exit(-1); } fileLength = getFileLength(s->fd); /* read header */ if (read(s->fd, (void*) &s->header, headerSize) != headerSize) { perror("FreqInvList_open: Couldn't read header struct!"); exit(-1); } /* TODO: check header, version etc. */ /* alloc memory for index */ s->index = calloc(s->header.indexSize, sizeof(Word)); if (NULL == s->index) { perror("FreqInvList_open: Failed to alloc memory for index!"); exit(-1); } /* read index */ cnt = read(s->fd, (void*) s->index, s->header.indexSize * sizeof(Word)); if (cnt != s->header.indexSize * sizeof(Word)) { perror("FreqInvList_open: Couldn't read index!"); exit(-1); } /* mmap data */ offs = headerSize + (s->header.indexSize * 4); s->dataSize = fileLength - offs; if ((s->data = mmap(NULL, s->dataSize, PROT_READ, 0, s->fd, offs)) == MAP_FAILED) { perror("FreqInvList_open: Failed to memory map data!"); exit(-1); } } /* * release the memory occupied by the index, unmap the data and close the file */ void FreqInvList_free(struct FreqInvList_Struct* s) { free(s->index); munmap(s->data, s->dataSize); close(s->fd); } /* * TODO: parameterize with hash-function */ void FreqInvList_query(struct FreqInvList_Struct* s, char *key) { Word ix; Word offs; Word numTokens; Word mask; void *ptr; Word termSize; Word numDocs; Word hash; mask = s->header.indexSize - 1; ix = sdbm_hash(key, strlen(key)); offs = s->index[ix & mask]; if (emptyBucket == offs) return; ptr = (s->data + offs); numTokens = *((Word*)ptr)++; while (numTokens-- > 0) { numDocs = *((Word*)ptr)++; termSize = *((unsigned char*)ptr)++; if (strncmp(ptr, key, termSize) == 0) { /* key found */ ptr += termSize; while (numDocs-- > 0) { printf("DocId: %u\t\tFrequency: %u\n", ((Word*)ptr)[0], ((Word*)ptr)[1]); ptr += 8; } } else { ptr += termSize + (numDocs*8); } } /* end while */ } int main(int argc, char** argv) { struct FreqInvList_Struct s; FreqInvList_open(&s, "/tmp/index"); FreqInvList_query(&s, argv[1]); FreqInvList_free(&s); return 0; } guugelhupf/src/gh/query/Makefile100644 1751 0 113 7515105046 16332 0ustar mneumannwheelranking.lex.sml: ranking.lex mllex ranking.lex clean: rm ranking.lex.sml guugelhupf/src/gh/query/ranking-parser.sml100644 1751 0 2374 7515105046 20365 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) structure RankingParser = struct structure Lex = struct open RankingLex open UserDeclarations end val default_pos_ranking = 1 (* + *) val default_neg_ranking = ~1 (* - *) val default_ranking = default_pos_ranking (* nothing *) datatype rt = RankedTerm of (string * int) fun parse (strm) : rt list = let val lexer = Lex.makeLexer (fn n => TextIO.inputN (strm, n)) val cur_ranking = ref default_ranking fun construct () = case lexer() of Lex.RANKING rank => (cur_ranking := rank; construct()) | Lex.RANKING_POS => (cur_ranking := default_pos_ranking; construct()) | Lex.RANKING_NEG => (cur_ranking := default_neg_ranking; construct()) | Lex.TERM term => RankedTerm (term, !cur_ranking) :: construct() | Lex.EOF => [] in construct () end fun output (l: rt list) (outputFn) = case l of [] => print "\n" | RankedTerm (term, rank) :: tl => (outputFn ("(" ^ term ^ ", " ^ Int.toString rank ^ ")"); output tl outputFn) end (* val rankedList = RankingParser.parse TextIO.stdIn; val _ = RankingParser.output rankedList; *) guugelhupf/src/gh/filter/ 40755 1751 0 0 7515105216 14741 5ustar mneumannwheelguugelhupf/src/gh/filter/stopWords.en100644 1751 0 14020 7515105046 17404 0ustar mneumannwheelthe of to a and in s that for is said it he on was with at as i by be his but from have are has an they who not will we t this had one or their were about you would more when year been two there all she out up which new 1 its if after no can first her than last other city time what also people years some 2 so into only three because 000 m state over county them just like him most do could san now million 3 los get 10 my angeles 5 before school don game high many 4 back may home 6 then our day even where made any such off u re officials against make through four way old long says those california down much team president going second work good me how week since well season say during did police world still being think another while company very go five american 7 play under take should these center 8 government here next public right both know today house court money 30 won national run want orange each your too group between left beach life d 15 night 20 man program see diego part never district council got ve p little business times best own department us told lot several until members office called six show west ago come children area points santa same big place however third states use board 9 used 12 around point united including set family didn john 11 few former country came days park end friday water valley coach might put federal league without month found director among tuesday community help according half local case major every women took soviet white law top york later monday plan games south real really news 0 early although political based months students wednesday better record saturday thursday college does open went something amp away lead must things free 50 great sunday market small asked least air past car report hit once bush far need give men black system less pay look line co service again 14 young street 25 ll party enough players 100 number 13 problems east southern began bill become job 16 others added lost drug whether music building defense 1988 late find win always art scored support making already general problem union hard along north seven war field health 18 keep recent university why expected call victory whose project held eight thing 17 committee hills cost washington oil start fire billion tax played development care trying doesn led sales though nation ever industry yards residents meeting l near decision final yet often force shot control manager power weeks 40 thought room interest series member head quarter large plans name conference almost wanted attorney issue story minutes kind death known hours special companies information senior working race campaign 500 firm club change behind believe earlier woman international chief move getting services food probably judge doing close military done taken within division full america price recently feel reported different playing film course cut live commission morning agency staff wife started fact running 1989 deal gave given hospital let schools foot fourth june having bank nearly 1987 football single player medical la land important executive television round makes action workers low outside history stock 21 road able parents side ball nothing anything david private 24 saying 19 runs official vote himself feet st average face spokesman increase front future programs drive robert hollywood mother together book budget coast looking received financial property seen turned economic became short return nine try summer el child tv meet administration 22 assn order n per foreign taking despite chairman o march 200 irvine rather instead johnson bad career rights prices mark yard policy sure baseball hand possible brown turn red study charges loss person age leaders mike r michael april include officers smith proposed teams contract strong coming wasn hour father j rate buy pacific love available current housing western groups security hall trade francisco hope town hotel decided involved announced worked agreed mayor scheduled ventura de basketball provide july talk c homes education question paid friends needed miles tell level james named areas across congress press officer total vice winning period george star offer corp share toward living effort moved continue position role space son kids class 23 view rose chance everything employees 60 japanese either fall trial calls 1986 using election insurance agreement works spent traffic legal construction seems tournament leader middle example issues stop central funds died built comes research term someone goal sold matter leave site sometimes style finished am van idea social saw church process richard 28 production beat personal soon isn killed movie pass proposal tried title themselves brought inc wants cases fund management higher largest japan davis station tom couple arts sell knew light sports lives begin leading result clear authorities growth sent costs cup organization owner cal allowed key takes student cities 27 whole anti income square bring guard fight bay act rock 35 difficult ground environmental section shows green europe approved likely list commercial annual hearing reason estate needs museum jim 26 base measure 1990 closed computer build cars everyone junior felt serious double else body human society especially store rest heart track performance paul americans filed longer planning plays perhaps century stage event arrested letter included weekend finally situation anyone ended bob gets stay similar released experience addition brief helped wall king heard inside reached guy assistant means sale safety records william considered further sense rates maybe training thomas husband ana senate e growing opening investigation hold b post changes owners voters ran theater goes mind capital main lower 300 mission couldn review worth quality offered break restaurant spring gone interview 45 ucla teachers itself form fell self non charge hot radio usually easy test throughout beginning simply opened 31 efforts canyon read allow tour met amount pro failed wilson shooting statement cannot block crime plant forward ask chicago starting seemed evidence blue pressure germany attention newport airport 1985 fine guugelhupf/src/gh/filter/stopword-filter.sml100644 1751 0 4276 7515105046 20731 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) functor StopwordFilterFn (structure TS: TOKEN_STREAM) :> sig include TOKEN_STREAM type table = (string, unit) HashTable.t val new : {tokenStream: TS.t, table: table} -> t val mkTable : string list -> table val mkTableFromStream : TextIO.instream -> table val mkTableFromFile : string -> table val inOutStats : t -> (word * word) (* * Return number of tokens that have gone through the filter, * and how many left it. The difference of both values is the * number of filtered tokens. *) end = struct open DS structure TS = TS type table = (string, unit) HashTable.t datatype t = T of {tokenStream: TS.t, hashTable: table, tokenIn: word ref, tokenOut: word ref} exception NotFound fun nextToken (t as T{tokenStream, hashTable, tokenIn, tokenOut}) = let in case TS.nextToken tokenStream of NONE => NONE (* end of stream *) | (tk as SOME(Token(tok))) => ( Word.inc tokenIn; if HashTable.hasKey hashTable (#term tok) then nextToken t else (Word.inc tokenOut; tk) ) end val hash = Hashing.sdbmHash fun new {tokenStream, table} = T {tokenStream = tokenStream, hashTable = table, tokenIn = ref 0w0, tokenOut = ref 0w0} fun inOutStats (T{tokenIn, tokenOut, ...}) = (!tokenIn, !tokenOut) fun mkTable (wordList: string list) = let val ht = HashTable.new {equals = (op =), hash = hash, size = List.length wordList, notFound = NotFound} fun ins w = HashTable.insertIfNew ht (w, ()) in List.app ins wordList; ht end fun mkTableFromStream (strm) = let val lines = TextIO.readLines strm in mkTable (List.map (fn line => String.chop line) lines) end fun mkTableFromFile (filename: string) = mkTableFromStream (TextIO.openIn filename) val map = TokenStream0.map nextToken val app = TokenStream0.app nextToken val toList = TokenStream0.toList nextToken end guugelhupf/src/gh/filter/filter.cm100644 1751 0 62 7515105006 16577 0ustar mneumannwheelGroup is ../token-stream0.sml stopword-filter.sml guugelhupf/src/gh/ds/ 40755 1751 0 0 7515105216 14062 5ustar mneumannwheelguugelhupf/src/gh/ds/inv-list.sml100644 1751 0 16561 7515105046 16473 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) structure FrequencyInvertedList = struct structure DocIdMap = SplayMapFn (struct type ord_key = doc_id val compare = Word.compare end) (* posting = occurences of a token in _one_ document *) type posting = {frequency: word ref} datatype inv_list = T of {hashTable: (string, posting DocIdMap.map ref) HashTable.t} exception NotFound fun new {hash, size} = T {hashTable = HashTable.new {equals = (op =), hash = hash, notFound = NotFound, size = size} } fun addToken (T{hashTable}) (docid: doc_id, Token{term, position, ...}) = let val map = HashTable.lookupOrInsert hashTable (term, ref DocIdMap.empty) val newMap = case DocIdMap.find (!map, docid) of NONE => DocIdMap.insert (!map, docid, {frequency = ref 0w1}) | SOME({frequency}) => (Word.inc frequency; !map) in map := newMap end fun lookupTerm (T{hashTable}) (term: string) = let val map = HashTable.find hashTable term in case map of NONE => [] | SOME(m) => DocIdMap.listItemsi (!m) end fun outputTerm (t as T{hashTable}) (term: string) = let val a = lookupTerm t term fun outp (docid, {frequency}) = let fun w2s w = Int.toString (Word.toInt w); in print " "; print (w2s docid); print " => "; print (w2s (!frequency)); print "\n" end in List.app outp a end fun output (t as T{hashTable}) = let val l = HashTable.listItemsi hashTable fun outp (k, v) = let in print k; print ": \n"; outputTerm t k end in List.app outp l end local open Posix.FileSys open Posix.IO open IO val emptyBucket = 0wxFFFFFFFF (* representation of an empty bucket in the file *) val headerSize = 28 val kennung = "FREQ_INV_LIST " val version = 0wx00010000 (* major = upper word (2-byte), minor = lower *) fun getPos fd = lseek (fd, 0, SEEK_CUR) fun setPos fd (pos: word) = lseek (fd, Word.toInt pos, SEEK_SET) fun readWordAtPos fd pos = (setPos fd pos; readWord fd) in (* Important: Use the same hash here as used for creating the index *) fun lookupTermsInFile {file, hash, terms: string list} = let val fd = openf (file, O_RDONLY, 0w0) val rKennung = readString fd (Word.fromInt (String.size kennung)) val rVersion = readWord fd val flags = readWord fd val indexSize = readWord fd val mask = indexSize - 0w1 val indexOffs = Word.fromInt headerSize val dataOffs = Word.fromInt headerSize + (indexSize * 0w4) fun lookupTerm (term: string) : (doc_id * word) list = let val hv = hash term val ix = Word.andb (hv, mask) val bucket = readWordAtPos fd (indexOffs + ix * 0w4) fun readDocs 0w0 = [] | readDocs n = let val docId = readWord fd val freq = readWord fd in (docId, freq) :: readDocs (n - 0w1) end fun readToken 0w0 = [] | readToken n = let val numDocs = readWord fd val termSize = Word.fromInt (Word8.toInt (readWord8 fd)) val termText = readString fd termSize in if termText = term (* found *) then readDocs numDocs else ( lseek (fd, 8 * Word.toInt numDocs, SEEK_CUR); readToken (n - 0w1) ) end fun readBucket () = let val numTokens = readWordAtPos fd (dataOffs + bucket) in readToken numTokens end in if bucket = emptyBucket then [] else readBucket () end (* lookupTerm *) in (* check header *) assert (rKennung = kennung); assert (rVersion = version); assert (getPos(fd) = headerSize); List.map (fn term => (term, lookupTerm term)) terms end fun writeToFile (T{hashTable}) (filename: string) = let val buckets = HashTable.getBuckets hashTable val numBuckets = HashTable.numBuckets hashTable val dataOffs = headerSize + (numBuckets*4) val fdi = creat (filename, S.irwxu) (* write the header and the index (hash) *) val fdc = openf (filename, O_WRONLY, 0w0) (* write the content of the inverted list *) val _ = setPos fdc (Word.fromInt dataOffs) (* position file pointer after index *) fun writeHeader () = let val flags = 0w0 val indexSize = Word.fromInt numBuckets in writeString fdi kennung; (* 16 bytes *) writeWord fdi version; (* 4 bytes *) writeWord fdi flags; (* 4 bytes *) writeWord fdi indexSize; (* 4 bytes *) assert (getPos(fdi) = headerSize) end (* * numDocs: word32 * termSize: word8 * termText: termSize bytes * * numDocs times { * docId: word32 * frequency: word32 * } *) fun writeToken (hash: word, termText: string, docs: posting DocIdMap.map ref) = let val numDocs = Word.fromInt (DocIdMap.numItems (!docs)) val termSize = Word8.fromInt (String.size termText) fun writeDoc (docId: doc_id, pst: posting) = let val frequency = !(#frequency pst) in writeWord fdc docId; writeWord fdc frequency end in writeWord fdc numDocs; writeWord8 fdc termSize; writeString fdc termText; List.app writeDoc (DocIdMap.listItemsi (!docs)) end (* * numTokens: word32 (if numTokens > 0) * * numTokens times { * see writeToken * } *) fun writeBucket (lst : (word * string * posting DocIdMap.map ref) list) = let val numTokens = Word.fromInt (List.length lst) val offs = Word.fromInt (getPos fdc - dataOffs) in if numTokens = 0w0 then writeWord fdi emptyBucket else ( writeWord fdi offs; writeWord fdc numTokens; List.app writeToken lst ) end in writeHeader (); Array.app writeBucket buckets end end (* local *) end guugelhupf/src/gh/ds/hash-table.sml100644 1751 0 13752 7515105046 16735 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann * * Many ideas from MLtons and John Reppys (SML/NJ) HashTable structure. *) structure HashTable = struct datatype ('a, 'b) t = HT of {buckets: (word * 'a * 'b) list array ref, equals: 'a * 'a -> bool, hash: 'a -> word, mask: word ref, notFound : exn, numItems: int ref} fun ('a, 'b) newWithBuckets {equals, hash, notFound, numBuckets}: ('a, 'b) t = let val mask: word = numBuckets - 0w1 in HT {buckets = ref (Array.new (Word.toInt numBuckets, [])), equals = equals, hash = hash, mask = ref mask, notFound = notFound, numItems = ref 0} end fun new {equals, hash, notFound, size} = newWithBuckets {equals = equals, hash = hash, notFound = notFound, numBuckets = Word.max (0w64, Word.roundUpToPowerOfTwo (Word.fromInt size))} (* Return number of key/value pairs in table *) fun size (HT {numItems, ...}) = !numItems fun numBuckets (HT {buckets, ...}) = Array.length (!buckets) fun getBuckets (HT {buckets, ...}) = (!buckets) fun index (w: word, mask: word): int = Word.toInt (Word.andb (w, mask)) (* Remove all keys for which function p returns true *) fun removeAll (HT {buckets, numItems, ...}, p) = let fun f (a, ac) = if p a then (Int.dec numItems; ac) else a :: ac fun g elts = List.foldr f [] elts in Array.modify g (!buckets) end (* Remove all elements from the table *) fun clear (HT {buckets, numItems, ...}) = let in numItems := 0; Array.modify (fn _ => []) (!buckets) end (* Insert an item. If the key already has an item associated with it, * then the old item is discarded. *) fun insert (HT{buckets, equals, hash, mask, numItems, ...}) (key, item) = let val hv = hash key val ix = index (hv, !mask) val bucket = Array.sub (!buckets, ix) fun ins [] = (Int.inc numItems; [(hv, key, item)]) (* new key *) | ins ((t as (h, k, i)) :: xs) = if hv = h andalso equals (key, k) (* key exists -> overwrite *) then (hv, key, item) :: xs else t :: (ins xs) in Array.update (!buckets, ix, ins bucket) end (* Insert an item if key not in table. *) fun insertIfNew (HT{buckets, equals, hash, mask, numItems, ...}) (key, item) = let val hv = hash key val ix = index (hv, !mask) val bucket = Array.sub (!buckets, ix) fun ins [] = (Int.inc numItems; [(hv, key, item)]) (* new key *) | ins (lst as ((t as (h, k, i)) :: xs)) = if hv = h andalso equals (key, k) (* key exists -> exit *) then lst else t :: (ins xs) in Array.update (!buckets, ix, ins bucket) end (* check whether key exits *) fun inDomain (HT{buckets, equals, hash, mask, ...}) key = let val hv = hash key val ix = index (hv, !mask) val bucket = Array.sub (!buckets, ix) fun find [] = false (* key does not exist *) | find ( (h, k, _) :: xs ) = if hv = h andalso equals (key, k) (* key exists *) then true else find xs in find bucket end (* synonym for inDomain *) val hasKey = inDomain (* If item does not exist, insert it into table. Otherwise just return the value. *) fun lookupOrInsert (HT{buckets, equals, hash, mask, numItems, ...}) (key, item) = let val hv = hash key val ix = index (hv, !mask) val bucket = Array.sub (!buckets, ix) fun upd b = Array.update (!buckets, ix, b) fun loi [] = (Int.inc numItems; upd [(hv, key, item)]; item) (* key not found -> insert *) | loi ((h, k, i) :: xs) = if hv = h andalso equals (key, k) (* key found -> return value *) then i else loi xs in loi bucket end (* look for an item, return NONE if the item doesn't exist *) fun find (HT{buckets, equals, hash, mask, ...}) key = let val hv = hash key val ix = index (hv, !mask) val bucket = Array.sub (!buckets, ix) fun find [] = NONE (* key does not exist *) | find ( (h, k, i) :: xs ) = if hv = h andalso equals (key, k) (* key exists *) then SOME(i) else find xs in find bucket end (* find an item, the table's exception is raised if the item doesn't exist *) fun lookup (tab as HT{buckets, equals, hash, mask, notFound, ...}) key = let in case find tab key of NONE => raise notFound | SOME(item) => item end (* Remove an item. The table's exception is raised if * the item doesn't exist. *) fun remove (HT{buckets, equals, hash, mask, notFound, ...}) key = let val hv = hash key val ix = index (hv, !mask) val bucket = Array.sub (!buckets, ix) fun rmv [] = raise notFound (* key does not exist *) | rmv ((t as (h, k, _)) :: xs) = if hv = h andalso equals (key, k) (* key found -> remove *) then xs else t :: (rmv xs) in Array.update (!buckets, ix, rmv bucket) end fun listItems (HT{buckets, ...}) = let fun extractItems (x, xs) = (List.map (fn (_,_,v) => v) x) @ xs in Array.foldr extractItems [] (!buckets) end fun listItemsi (HT{buckets, ...}) = let fun extractItems (x, xs) = (List.map (fn (_,k,v) => (k,v)) x) @ xs in Array.foldr extractItems [] (!buckets) end end (* fun hashString (s:string) = Word.fromInt(String.size s); (* TEST *) exception NotFound val ht = HashTable.new {equals = (op =), hash = hashString, size = 128, notFound = NotFound}; val _ = HashTable.insert ht ("hall", "na dann suepr"); val _ = HashTable.insert ht ("hallo", "geil"); val l = HashTable.lookup ht "hall"; val _ = print (l ^ "\n"); val i = HashTable.size ht ; val s = Int.toString i; val _ = print (s ^ "\n"); *) guugelhupf/src/gh/ds/ds.cm100644 1751 0 52 7515105046 15044 0ustar mneumannwheelPackage DS is hash-table.sml inv-list.sml guugelhupf/src/gh/ds/README100644 1751 0 20 7515105046 14770 0ustar mneumannwheelDatenstrukturen guugelhupf/src/gh/index/ 40755 1751 0 0 7515105216 14563 5ustar mneumannwheelguugelhupf/src/gh/index/index.sml100644 1751 0 4322 7515105046 16506 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) structure DefaultIndex = struct open DS open Tokenizer.CharTableTransform structure Tokenizer = MmapTokenizer structure TS = StopwordFilterFn(structure TS = Tokenizer) (* token stream *) structure InvList = FrequencyInvertedList val hash = Hashing.sdbmHash datatype t = T of {charTable: string, invList: InvList.inv_list, stopwordTable: TS.table, outputFn: string -> unit } fun new {size, stopwordFile, charTable, outputFn} = T {invList = InvList.new {hash = hash, size = size}, stopwordTable = TS.mkTableFromFile stopwordFile, charTable = charTable, outputFn = outputFn } fun indexDocument (T{charTable, stopwordTable, invList, outputFn}) (file: string, docid: doc_id) = let val _ = outputFn ("= processing file " ^ file ^ "\n") val timer = Timer.startCPUTimer () (* start timer *) val ms = Tokenizer.new (file, charTable) val ts = TS.new {tokenStream = ms, table = stopwordTable} fun loop() = case TS.nextToken ts of NONE => () | SOME(tok) => (InvList.addToken invList (docid, tok); loop ()) fun reportStats () = let val {usr, sys, gc} = Timer.checkCPUTimer timer (* stop timer *) val total = Time.+ (Time.+ (usr, sys), gc) val (tokenIn, tokenOut) = TS.inOutStats ts val tokenFiltered = tokenIn - tokenOut val wordToDecString = Int.toString o Word.toInt val timeToString = LargeInt.toString o Time.toMicroseconds in outputFn ("! token in/out/filtered: " ^ wordToDecString tokenIn ^ "/" ^ wordToDecString tokenOut ^ "/" ^ wordToDecString tokenFiltered ^ "\n"); outputFn ("! time (in us) usr/sys/gc/total: " ^ timeToString usr ^ "/" ^ timeToString sys ^ "/" ^ timeToString gc ^ "/" ^ timeToString total ^ "\n"); outputFn "\n" end in loop (); reportStats (); Tokenizer.free ms end fun writeIndex (T{invList, ...}) (file: string) = let in InvList.writeToFile invList file end end guugelhupf/src/gh/index/index.cm100644 1751 0 23 7515105046 16244 0ustar mneumannwheelGroup is index.sml guugelhupf/src/gh/gh-defs.sml100644 1751 0 1176 7515105046 15611 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) (* Global definitions for GH *) (* Document ID *) type doc_id = word (* Type that represents a token in a document. *) datatype token = Token of { term : string, (* Text of the token. *) position : word, (* Position of token in the document. *) kind : string option (* Kind of token (e.g. "title" or "emph"). *) } signature TOKEN_STREAM = sig type t val nextToken : t -> token option val toList : t -> token list val app : (token -> unit) -> t -> unit val map : (token -> 'a) -> t -> 'a list end guugelhupf/src/gh/io/ 40755 1751 0 0 7515105216 14063 5ustar mneumannwheelguugelhupf/src/gh/io/test/ 40755 1751 0 0 7515105216 15042 5ustar mneumannwheelguugelhupf/src/gh/io/test/test.sml100644 1751 0 714 7515105046 16616 0ustar mneumannwheelopen Posix.FileSys open Posix.IO val tWord = 0wx12345678; fun testWrite (filename: string) = let val fd = creat (filename, S.irwxu) in IO.writeWord fd tWord; close fd end fun testRead (filename: string) = let val fd = openf (filename, O_RDONLY, 0w0) in case IO.readWord fd of tWord => print "OK\n" | _ => print "FAILED\n" end val file = "hallo.txt"; val _ = testWrite file; val _ = testRead file; guugelhupf/src/gh/io/test/test.cm100644 1751 0 33 7515105046 16374 0ustar mneumannwheelGroup is ../io.cm test.sml guugelhupf/src/gh/io/io.sml100644 1751 0 5105 7515105046 15306 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) structure IO = struct structure Prim = struct type fd = word val write_char = _ffi "IO_write_char" : (fd * char) -> int; val write_word8 = _ffi "IO_write_word8" : (fd * Word8.word) -> int; val write_word = _ffi "IO_write_word" : (fd * Word.word) -> int; val write_int = _ffi "IO_write_int" : (fd * int) -> int; val write_string = _ffi "IO_write_string" : (fd * string) -> int; val write_wordArray = _ffi "IO_write_wordArray" : (fd * Word.word array) -> int; val write_wordVector = _ffi "IO_write_wordArray" : (fd * Word.word vector) -> int; val read_char = _ffi "IO_read_char" : fd -> char; val read_word8 = _ffi "IO_read_word8" : fd -> Word8.word; val read_word = _ffi "IO_read_word" : fd -> Word.word; val read_int = _ffi "IO_read_int" : fd -> int; val read_string = _ffi "IO_read_string" : (fd * CharArray.array) -> unit; val read_wordArray = _ffi "IO_read_wordArray" : (fd * Word.word array) -> unit; end; type fd = Posix.IO.file_desc fun failure () = let val err = Posix.Error.fromWord (Word.fromInt (MLton.errno ())) in raise OS.SysErr ("", SOME (err)) end fun check (i:int) : unit = if i = ~1 then failure () else () val fdToWord = Posix.FileSys.fdToWord fun writeChar (fd: fd) (v: char) = check (Prim.write_char (fdToWord fd, v)) fun writeWord8 (fd: fd) (v: Word8.word) = check (Prim.write_word8 (fdToWord fd, v)) fun writeWord (fd: fd) (v: Word.word) = check (Prim.write_word (fdToWord fd, v)) fun writeInt (fd: fd) (v: int) = check (Prim.write_int (fdToWord fd, v)) fun writeString (fd: fd) (v: string) = check (Prim.write_string (fdToWord fd, v)) fun writeWordArray (fd: fd) (v: Word.word array) = check (Prim.write_wordArray (fdToWord fd, v)) fun writeWordVector (fd: fd) (v: Word.word vector) = check (Prim.write_wordVector (fdToWord fd, v)) fun readChar (fd: fd) = Prim.read_char (fdToWord fd) fun readWord8 (fd: fd) = Prim.read_word8 (fdToWord fd) fun readWord (fd: fd) = Prim.read_word (fdToWord fd) fun readInt (fd: fd) = Prim.read_int (fdToWord fd) fun readString (fd: fd) (n: word) = let val buf = CharArray.array (Word.toInt n, #"\000") in Prim.read_string (fdToWord fd, buf); CharArray.extract (buf, 0, NONE) end fun readWordArray (fd: fd) (n: word) = let val buf = Array.array (Word.toInt n, 0w0) in Prim.read_wordArray (fdToWord fd, buf); buf end end guugelhupf/src/gh/io/io.cm100644 1751 0 34 7515105046 15046 0ustar mneumannwheelGroup is io.sml ! make io.o guugelhupf/src/gh/io/io.c100644 1751 0 3230 7515105046 14732 0ustar mneumannwheel/* * Copyright (c) 2002 Michael Neumann * * TODO: better error handling for reading functions */ #include #include #include #include "libmlton.h" /* WRITING */ Int IO_write_char(Fd handle, Char v) { return write(handle, (void*) &v, sizeof(Char)); } Int IO_write_word8(Fd handle, Word v) { return write(handle, (void*) &v, 1); } Int IO_write_word(Fd handle, Word v) { return write(handle, (void*) &v, sizeof(Word)); } Int IO_write_int(Fd handle, Int v) { return write(handle, (void*) &v, sizeof(Int)); } Int IO_write_string(Fd handle, Pointer v) { return write(handle, (void*) v, GC_arrayNumElements(v)); } Int IO_write_wordArray(Fd handle, Pointer v) { return write(handle, (void*) v, GC_arrayNumElements(v)*sizeof(Word)); } /* READING */ Char IO_read_char(Fd handle) { Char v; int c; c = read(handle, (void*) &v, sizeof(Char)); assert(c == sizeof(Char)); return v; } Word IO_read_word8(Fd handle) { Word v = 0L; int c; c = read(handle, (void*) &v, 1); assert(c == 1); return v; } Word IO_read_word(Fd handle) { Word v; int c; c = read(handle, (void*) &v, sizeof(Word)); assert(c == sizeof(Word)); return v; } Int IO_read_int(Fd handle) { Int v; int c; c = read(handle, (void*) &v, sizeof(Int)); assert(c == sizeof(Int)); return v; } void IO_read_string(Fd handle, Pointer v) { int c; Word len = GC_arrayNumElements(v); c = read(handle, (void*) v, len); assert(c == len); } void IO_read_wordArray(Fd handle, Pointer v) { int c; Word len = GC_arrayNumElements(v) * sizeof(Word); c = read(handle, (void*) v, len); assert(c == len); } guugelhupf/src/gh/io/Makefile100644 1751 0 117 7515105046 15600 0ustar mneumannwheelinclude ${GH_BASE}/mk/mlton.mk OBJS=io.o all: $(OBJS) clean: rm -f $(OBJS) guugelhupf/src/gh/tokenizer/ 40755 1751 0 0 7515105216 15466 5ustar mneumannwheelguugelhupf/src/gh/tokenizer/tokenizer.cm100644 1751 0 107 7515105046 20075 0ustar mneumannwheelPackage Tokenizer is ../token-stream0.sml char-table-transform/make.cm guugelhupf/src/gh/tokenizer/char-table-transform/ 40755 1751 0 0 7515105216 21501 5ustar mneumannwheelguugelhupf/src/gh/tokenizer/char-table-transform/tools/ 40755 1751 0 0 7515105216 22641 5ustar mneumannwheelguugelhupf/src/gh/tokenizer/char-table-transform/tools/german.cts100644 1751 0 143 7515105046 24701 0ustar mneumannwheelidentifier "German" add LOWERCASE add UPPERCASE, LOWERCASE add DIGITS add "ÄÖÜ", "äöü" add "äöüß" guugelhupf/src/gh/tokenizer/char-table-transform/tools/english.cts100644 1751 0 110 7515105046 25053 0ustar mneumannwheelidentifier "English" add LOWERCASE add UPPERCASE, LOWERCASE add DIGITS guugelhupf/src/gh/tokenizer/char-table-transform/tools/createCharTable.rb100644 1751 0 5453 7515105046 26304 0ustar mneumannwheel# # Copyright (c) 2002 Michael Neumann # LOWERCASE = ('a'..'z').to_a.join UPPERCASE = ('A'..'Z').to_a.join DIGITS = ('0'..'9').to_a.join class CharTable def initialize @charTable = Array.new(256).fill(0) end def addChar(c, c_transformed=nil) c_transformed ||= c if c.is_a? String c = c[0] c_transformed = c_transformed[0] end @charTable[c] = c_transformed end def addString(str, str_transformed=nil) str_transformed ||= str if str.size != str_transformed.size raise "Strings must be of same length!" end for i in 0...(str.size) do addChar(str[i], str_transformed[i]) end end def add(s, st=nil) case s when String addString(s, st) when Fixnum addChar(s, st) else raise "Wrong operand type" end end def [](ix) @charTable[ix] end def asString_SML(indent=nil, identifier=nil, inc_indent=nil) indent ||= 0 inc_indent ||= 2 res = "" if identifier res << (" "*indent) + "val #{ identifier } = \n" indent += inc_indent end for i in 0..15 res << (" " * indent) res << (i == 0 ? '"' : '\\') res << escapeArray(@charTable[16*i, 16]) res << (i != 15 ? "\\\n" : '"') end res << (identifier ? ";\n" : "\n") res end def asString_Bin(*a) @charTable.map {|c| c.chr}.join end def fromScript(script, lang=:SML) lang ||= :SML @identifier = nil @indent, @inc_indent = nil, nil instance_eval script send("asString_#{lang}", @indent, @identifier, @inc_indent) end private # escapes a char-array to \xxx format def escapeArray(arr) res = "" arr.each do |b| res << "\\" res << threeDigit(b) end res end def threeDigit(num) raise "Number too large!" if num < 0 or num > 255 hundreds = num / 100 tenth = (num-(hundreds*100)) / 10 ones = (num-(hundreds*100)-(tenth*10)) return "#{ hundreds }#{ tenth }#{ ones }" end # evtl. called from script def identifier(id) @identifier = id end # evtl. called from script def indent(indent, inc_indent=nil) @indent, @inc_indent = indent, inc_indent end end if __FILE__ == $0 # usage: $0 [format] # # where format is either SML or Bin # # takes script on stdin and writes output to stdout # example of script: # # identifier "hallo" # indent 3, 4 # add LOWERCASE # add UPPERCASE, LOWERCASE # ... # ct = CharTable.new STDOUT << ct.fromScript(STDIN.read, ARGV[0]) STDOUT.flush #ct = CharTable.new #ct.addString(LOWERCASE) #ct.addString(UPPERCASE, LOWERCASE) # transform upper -> lower #ct.addString(DIGITS) #ct.addString("ÄÖÜ", "äöü") # transform lowercase #ct.addString("äöüß") #print ct.asString_SML(0, "charTab1") end guugelhupf/src/gh/tokenizer/char-table-transform/german.sml100644 1751 0 2137 7515105046 23570 0ustar mneumannwheelval German = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\048\049\050\051\052\053\054\055\056\057\000\000\000\000\000\000\ \\000\097\098\099\100\101\102\103\104\105\106\107\108\109\110\111\ \\112\113\114\115\116\117\118\119\120\121\122\000\000\000\000\000\ \\000\097\098\099\100\101\102\103\104\105\106\107\108\109\110\111\ \\112\113\114\115\116\117\118\119\120\121\122\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\228\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\246\000\000\000\000\000\252\000\000\223\ \\000\000\000\000\228\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\246\000\000\000\000\000\252\000\000\000"; guugelhupf/src/gh/tokenizer/char-table-transform/english.sml100644 1751 0 2140 7515105046 23742 0ustar mneumannwheelval English = "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\048\049\050\051\052\053\054\055\056\057\000\000\000\000\000\000\ \\000\097\098\099\100\101\102\103\104\105\106\107\108\109\110\111\ \\112\113\114\115\116\117\118\119\120\121\122\000\000\000\000\000\ \\000\097\098\099\100\101\102\103\104\105\106\107\108\109\110\111\ \\112\113\114\115\116\117\118\119\120\121\122\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ \\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"; guugelhupf/src/gh/tokenizer/char-table-transform/string/ 40755 1751 0 0 7515105216 23007 5ustar mneumannwheelguugelhupf/src/gh/tokenizer/char-table-transform/string/string-tokenizer.sml100644 1751 0 5063 7515105047 27145 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) (* required, because MLton do not support TextIO.openString *) structure StringTokenizer :> sig include TOKEN_STREAM val new : (string * string) -> t val free : t -> unit end = struct val maxWordLen = 255 exception Error of string datatype t = T of {str: string, strPos: int ref, charTable: string, buf: CharArray.array, wordPos: word ref} fun new (str, ct) = let val _ = if (String.size ct) <> 256 then raise Error("charTable is of wrong size (should be 256)") else () in T {str = str, strPos = ref 0, charTable = ct, buf = CharArray.array (maxWordLen, #"\000"), wordPos = ref 0w0} end fun free t = () fun nextToken (T{str, strPos, charTable, buf, wordPos}) = let val buflen = ref 0 fun lookup c = String.sub (charTable, Char.ord c) fun readC () = if !strPos < String.size str then SOME (String.sub (str, !strPos)) else NONE fun skipws () = let val c = readC () in if isSome(c) andalso lookup (valOf c) = #"\000" then (strPos := !strPos + 1; skipws()) else () end fun consume () = let val c = readC () in if isSome(c) then (let val l = lookup (valOf c) in CharArray.update (buf, !buflen, l); strPos := !strPos + 1; if l = #"\000" then false (* whitespace *) else ( if Int.inc buflen >= maxWordLen then false else true ) end) else false end in skipws (); while consume() do (); if !buflen = 0 then NONE else SOME (Token {term = CharArray.extract (buf, 0, SOME (!buflen)), position = Word.inc wordPos, kind = NONE}) end val map = TokenStream0.map nextToken val app = TokenStream0.app nextToken val toList = TokenStream0.toList nextToken end guugelhupf/src/gh/tokenizer/char-table-transform/from-file.sml100644 1751 0 232 7515105047 24152 0ustar mneumannwheel(* read a char table from file *) fun mkCharTableFromFile file = let val strm = TextIO.openIn file in TextIO.inputN (strm, 256) end guugelhupf/src/gh/tokenizer/char-table-transform/mmap/ 40755 1751 0 0 7515105216 22433 5ustar mneumannwheelguugelhupf/src/gh/tokenizer/char-table-transform/mmap/mmap-tokenizer.c100644 1751 0 4476 7515105047 25653 0ustar mneumannwheel/* * Copyright (c) 2002 Michael Neumann */ #if (defined (__FreeBSD__)) #include #endif #include #include "libmlton.h" #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif struct MmapTokenizer { char *ptr; /* start address of memory */ char *cur_ptr; /* current position in memory */ char *end_ptr; /* last address of memeory frame */ unsigned long length; /* length of memory frame */ }; const int MmapTokenizer_structSize = sizeof(struct MmapTokenizer); const int MmapTokenizer_maxWordLen = 255; bool MmapTokenizer_init(Pointer p, Fd handle, Size length) { struct MmapTokenizer *s = (struct MmapTokenizer*) p; assert (GC_arrayNumElements(p) >= sizeof(struct MmapTokenizer)); s->ptr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, handle, 0); if (s->ptr == MAP_FAILED) { return FALSE; } s->length = length; s->cur_ptr = s->ptr; s->end_ptr = s->ptr + s->length; return TRUE; } void MmapTokenizer_free(Pointer p) { struct MmapTokenizer *s = (struct MmapTokenizer*) p; assert (GC_arrayNumElements(p) >= sizeof(struct MmapTokenizer)); munmap(s->ptr, s->length); } /* * returns size of token (EOS=0) and the token itself in buf. */ int MmapTokenizer_nextToken(Pointer p, Pointer charTable, Pointer buf) { struct MmapTokenizer *s = (struct MmapTokenizer*) p; char c; char *end_ptr; char *start_ptr; char *cur_ptr; assert (GC_arrayNumElements(p) >= sizeof(struct MmapTokenizer)); assert (GC_arrayNumElements(charTable) == 256); assert (GC_arrayNumElements(buf) >= MmapTokenizer_maxWordLen); /* load values from struct into local variables */ cur_ptr = s->cur_ptr; end_ptr = s->end_ptr; /* skip non-word characters (non-word characters are those for which * in the characterTable a \0 is stored.) */ while (cur_ptr != end_ptr && charTable[*((unsigned char*)cur_ptr)] == 0) { cur_ptr++; } /* cosume word characters until a non-word character occures */ start_ptr = cur_ptr; end_ptr = (char*) MIN(cur_ptr + MmapTokenizer_maxWordLen, end_ptr); while (cur_ptr != end_ptr && (c=charTable[*((unsigned char*)cur_ptr)]) != 0) { *buf++ = c; /* store character in SML buffer */ cur_ptr++; } /* store value back to struct */ s->cur_ptr = cur_ptr; return cur_ptr - start_ptr; } guugelhupf/src/gh/tokenizer/char-table-transform/mmap/mmap-tokenizer.sml100644 1751 0 4237 7515105047 26217 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) structure MmapTokenizer :> sig include TOKEN_STREAM val new : (string * string) -> t val new2 : (Posix.IO.file_desc * int * string) -> t val free : t -> unit end = struct exception Error of string structure Prim = struct type t = Misc.CStruct.t val structSize = _ffi "MmapTokenizer_structSize" : int; val maxWordLen = _ffi "MmapTokenizer_maxWordLen" : int; val init = _ffi "MmapTokenizer_init" : (t * word * int) -> bool; val free = _ffi "MmapTokenizer_free" : t -> unit; val nextToken = _ffi "MmapTokenizer_nextToken" : (t * string (* ct *) * CharArray.array (* buf *)) -> int; end (* Prim *) datatype t = T of {prim: Prim.t, fd : Posix.IO.file_desc, charTable: string, buf: CharArray.array, wordPos: word ref} fun new2 (fd : Posix.IO.file_desc, len : int, ct : string) : t = let val s = Misc.CStruct.new Prim.structSize val b = CharArray.array (Prim.maxWordLen, #"\000") in if (String.size ct) <> 256 then raise Error("charTable is of wrong size (should be 256)") else (); if Prim.init (s, Posix.FileSys.fdToWord fd, len) then T {prim = s, fd = fd, charTable = ct, buf = b, wordPos = ref 0w0} else raise Error("Failed to memory map file") end fun new (fileName : string, ct : string) : t = let open Posix.FileSys val fd = openf (fileName, O_RDONLY, 0w0) val sz = ST.size (stat fileName) in new2 (fd, sz, ct) end fun free (T {prim = s, fd, ...}) = (Prim.free s; Posix.IO.close fd) fun nextToken (T {prim = s, charTable = ct, buf = b, wordPos, ...}) = let val sz = Prim.nextToken (s, ct, b) in if sz = 0 then NONE else SOME (Token {term = CharArray.extract (b, 0, SOME (sz)), position = Word.inc wordPos, kind = NONE }) end val map = TokenStream0.map nextToken val app = TokenStream0.app nextToken val toList = TokenStream0.toList nextToken end guugelhupf/src/gh/tokenizer/char-table-transform/mmap/Makefile100644 1751 0 133 7515105047 24147 0ustar mneumannwheelinclude ${GH_BASE}/mk/mlton.mk OBJS=mmap-tokenizer.o all: $(OBJS) clean: rm -f $(OBJS) guugelhupf/src/gh/tokenizer/char-table-transform/stream/ 40755 1751 0 0 7515105216 22774 5ustar mneumannwheelguugelhupf/src/gh/tokenizer/char-table-transform/stream/stream-tokenizer.sml100644 1751 0 4710 7515105047 27115 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) structure StreamTokenizer :> sig include TOKEN_STREAM val new : (string * string) -> t val new2 : (TextIO.instream * string) -> t val free : t -> unit end = struct val maxWordLen = 255 exception Error of string datatype t = T of {stream: TextIO.instream, charTable: string, buf: CharArray.array, wordPos: word ref} fun new2 (strm, ct) = let val _ = if (String.size ct) <> 256 then raise Error("charTable is of wrong size (should be 256)") else () in T {stream = strm, charTable = ct, buf = CharArray.array (maxWordLen, #"\000"), wordPos = ref 0w0} end fun new (filename, ct) = new2 (TextIO.openIn filename, ct) fun free t = () fun nextToken (T{stream, charTable, buf, wordPos}) = let val buflen = ref 0 fun lookup c = String.sub (charTable, Char.ord c) fun skipws () = let val c = TextIO.lookahead stream in if isSome(c) andalso lookup (valOf c) = #"\000" then (TextIO.input1 stream; skipws()) else () end fun consume () = let val c = TextIO.lookahead stream in if isSome(c) then (let val l = lookup (valOf c) in CharArray.update (buf, !buflen, l); TextIO.input1 stream; if l = #"\000" then false (* whitespace *) else ( if Int.inc buflen >= maxWordLen then false else true ) end) else false end in skipws (); while consume() do (); if !buflen = 0 then NONE else SOME (Token {term = CharArray.extract (buf, 0, SOME (!buflen)), position = Word.inc wordPos, kind = NONE}) end val map = TokenStream0.map nextToken val app = TokenStream0.app nextToken val toList = TokenStream0.toList nextToken end guugelhupf/src/gh/tokenizer/char-table-transform/make.cm100644 1751 0 313 7515105047 23013 0ustar mneumannwheelPackage CharTableTransform is german.sml english.sml from-file.sml mmap/mmap-tokenizer.sml !cd mmap && make mmap-tokenizer.o mmap/mmap-tokenizer.o stream/stream-tokenizer.sml string/string-tokenizer.sml guugelhupf/src/gh/tokenizer/char-table-transform/Makefile100644 1751 0 456 7515105047 23225 0ustar mneumannwheelall: tools/german.cts tools/english.cts ruby tools/createCharTable.rb < tools/german.cts > german.sml ruby tools/createCharTable.rb Bin < tools/german.cts > german.ct ruby tools/createCharTable.rb < tools/english.cts > english.sml ruby tools/createCharTable.rb Bin < tools/english.cts > english.ct guugelhupf/src/gh/tokenizer/char-table-transform/german.ct100644 1751 0 400 7515105047 23353 0ustar mneumannwheel0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzäöüßäöüguugelhupf/src/gh/tokenizer/char-table-transform/english.ct100644 1751 0 400 7515105047 23533 0ustar mneumannwheel0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzguugelhupf/src/gh/tokenizer/char-table-transform/examples/ 40755 1751 0 0 7515105216 23317 5ustar mneumannwheelguugelhupf/src/gh/tokenizer/char-table-transform/examples/test.sml100644 1751 0 1134 7515105047 25111 0ustar mneumannwheel(*fun tf (n:int) = let in if (n >= Char.ord #"a" andalso n <= Char.ord #"z") then Char.chr n else if (n >= Char.ord #"A" andalso n <= Char.ord #"Z") then Char.chr (n - (Char.ord #"A") + (Char.ord #"a")) else Char.chr 0 end; val ct = CharArray.extract (CharArray.tabulate (256, tf), 0, NONE); *) open GH open GH.Tokenizer.CharTableTransform; val args = CommandLine.arguments (); val file = hd args; val s = MmapTokenizer.new (file, German); fun loop() = case MmapTokenizer.nextToken s of NONE => () | SOME (Token {term, ...}) => (print term; print "\n"; loop()) ; val _ = loop(); guugelhupf/src/gh/tokenizer/char-table-transform/examples/test.cm100644 1751 0 43 7515105047 24653 0ustar mneumannwheelGroup is ../../../make.cm test.sml guugelhupf/src/gh/tokenizer/char-table-transform/examples/Makefile100644 1751 0 257 7515105047 25042 0ustar mneumannwheelall: CM.rb test.cm /tmp/prg.sml ( cd ../mmap && $(MAKE) ) mlton /tmp/prg.sml ../mmap/mmap-tokenizer.o clean: rm -f /tmp/prg.sml /tmp/prg ( cd ../mmap && $(MAKE) clean ) guugelhupf/src/gh/misc/ 40755 1751 0 0 7515105216 14407 5ustar mneumannwheelguugelhupf/src/gh/misc/pack.c100644 1751 0 505 7515105047 15550 0ustar mneumannwheel/* * Copyright (c) 2002 Michael Neumann */ #include "libmlton.h" void Pack_word(Word val, Pointer str) { assert (GC_arrayNumElements(str) >= sizeof(Word)); *((Word*)str) = val; } Word Unpack_word(Pointer str) { assert (GC_arrayNumElements(str) >= sizeof(Word)); return *((Word*)str); } guugelhupf/src/gh/misc/cstruct.sml100644 1751 0 454 7515105047 16675 0ustar mneumannwheel(* * A structure that stores the content of a C-record in a SML * array. Used to iterface with functions written in C. *) structure CStruct :> sig type t val new : int -> t end = struct type t = Word8Array.array fun new (size:int) = Word8Array.array (size, 0w0) end; guugelhupf/src/gh/misc/misc.sml100644 1751 0 3261 7515105047 16160 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) structure Array = struct open Array val new = array end structure Int = struct open Int fun dec (n:int ref) = (n := !n - 1; !n) fun inc (n:int ref) = (n := !n + 1; !n) end structure Word = struct open Word fun roundUpToPowerOfTwo (n:word) = let fun f i = if (i >= n) then i else f(Word.* (i, 0w2)) in f 0w1 end fun dec (n:word ref) = (n := !n - 0w1; !n) fun inc (n:word ref) = (n := !n + 0w1; !n) end structure Error = struct fun unimplemented msg = raise Fail("unimplemented: " ^ msg) fun bug msg = raise (Fail msg) end fun assert (cond: bool) = if cond = false then raise (Fail "assertion failed") else () structure Char = struct open Char (* copied from MLton basic/char0.sml *) fun digitToInt (c: char): int option = if isDigit c then SOME (ord c - ord #"0") else NONE end structure String = struct open String fun chop str = substring (str, 0, size str - 1) val downcase = String.map Char.toLower val upcase = String.map Char.toUpper val length = size (* from MLton basic/string0.sml *) fun dropPrefix (s, n) = substring (s, n, (size s) - n) end structure TextIO = struct open TextIO fun appLines (f: string -> unit) (strm: instream) : unit = case TextIO.inputLine strm of "" => () | line => (f line; appLines f strm) fun readLines (strm: instream) : string list = let fun loop lst = case TextIO.inputLine strm of "" => lst | line => loop (lst @ [line]) in loop [] end end guugelhupf/src/gh/misc/all.cm100644 1751 0 32 7515105047 15532 0ustar mneumannwheelGroup is misc.sml misc.cm guugelhupf/src/gh/misc/pack.sml100644 1751 0 675 7515105047 16131 0ustar mneumannwheel(* * Copyright (c) 2002 Michael Neumann *) structure Pack = struct structure Prim = struct val pack_word = _ffi "Pack_word" : (word * string) -> unit; val unpack_word = _ffi "Unpack_word" : string -> word; end fun packWord (w: word) = let val str = "\000\000\000\000" val _ = Prim.pack_word (w, str) in str end val unpackWord = Prim.unpack_word end guugelhupf/src/gh/misc/misc.cm100644 1751 0 71 7515105047 15720 0ustar mneumannwheelPackage Misc is cstruct.sml pack.sml !make pack.o pack.o guugelhupf/src/gh/misc/Makefile100644 1751 0 121 7515105047 16120 0ustar mneumannwheelinclude ${GH_BASE}/mk/mlton.mk OBJS=pack.o all: $(OBJS) clean: rm -f $(OBJS)