00001 #ifndef DEF_GROUPINDEX 00002 #define DEF_GROUPINDEX 00003 #pragma warning( disable : 4786 ) 00004 #pragma message("Compiling the GroupIndex template library...") 00005 #include "util.h" 00006 #include <cstdio> 00007 #include <cstdlib> 00008 #include <vector> 00009 #include <map> 00010 #include <set> 00011 #include <string> 00012 #ifdef VISUALC_INCLUDES 00013 #include <direct.h> 00014 #else 00015 #include <unistd.h> 00016 #include <mingw/stdlib.h> 00017 #endif 00018 //#include "DefaultGListCompression.h" 00019 00020 00518 namespace groupindex 00519 { 00520 00563 // Wie schön, dass es Präprozessor gibt: 00564 // Damit können wir nämlich unter VisualC UND gcc (->ANSI) kompilieren! 00565 #ifdef VISUALC_INCLUDES 00566 #ifndef ANSI_BRACES 00567 #define ANSI_BRACES 00568 #endif 00569 #ifndef ANSI_TYPENAME 00570 #define ANSI_TYPENAME 00571 #endif 00572 #else 00573 #ifndef ANSI_BRACES 00574 #define ANSI_BRACES <> 00575 #endif 00576 #ifndef ANSI_TYPENAME 00577 #define ANSI_TYPENAME typename 00578 #endif 00579 #endif 00580 00581 #include <iostream> 00582 #include <fstream> 00583 #include "DefaultRepresentation.h" 00584 00585 00586 #ifndef TIME 00587 #include <time.h> 00588 #define TIME 00589 #endif 00590 00591 #ifdef set 00592 #undef set 00593 #endif 00594 #include <set> 00595 00596 //-------------------------------- 00597 #ifndef MISMATCHLIST 00598 #include "MismatchList.h" 00599 #endif 00600 //-------------------------------- 00601 00602 00603 typedef std::map<std::string, int> NameToIndexMap; 00604 typedef NameToIndexMap::value_type NameToIndexEntry; 00605 00606 typedef std::map<int, std::string> IndexToNameMap; 00607 typedef IndexToNameMap::value_type IndexToNameEntry; 00608 00609 //********************************************************************************** 00610 // Igitt - Makros! 00611 //********************************************************************************** 00612 00613 #define min(a, b) (((a) < (b)) ? (a) : (b)) 00614 00615 //********************************************************************************** 00616 00617 // Zunächst einige Worte zum Geleit für die doxygen-Doku: 00618 /* 00619 \mainpageOUT Die GroupIndex - Template-Library 00620 00621 \section introduction I. Einführung 00622 00623 Das Indexierungssystem \c GroupIndex ist eine generische Programmierbibliothek zum 00624 Erstellen von inhaltsbasierten Indexierungssystemen. 00625 \c GroupIndex nutzt an vielen Stellen das Konzept der Templates der Programmierprache C++. 00626 Um mit Hilfe von \c GroupIndex Indexierungsysteme zu 00627 erstellen, ist es unumgänglich, gewisse zugrundeliegende Ideen zu kennen, 00628 die in den folgenden Abschnitten erläutert werden. 00629 00630 \section group_and_set_classes II. Gruppen- und Mengenklassen 00631 00632 Eine zentrale Rolle im \c GroupIndex - System spielen die sogenannten 00633 \em Gruppenklassen und \em Mengenklassen. Wie die Benennungen dieser Klassen nahelegen, 00634 übernehmen die Instanzen dieser Klassen die Rolle von Elementen einer \em Gruppe resp. einer 00635 \em Menge - und zwar im mathematischen Sinne. Für jeden Index, der die \c GroupIndex - Template-Library 00636 verwendet, muss jeweils eine Gruppen- und eine Mengenklasse 00637 implementiert werden. Worin Rolle dieser Klassen genau besteht und wie sich dies 00638 in den jeweiligen Klassendeklarationen wiederspiegelt, soll im folgenden Abschnitt erläutert werden. 00639 00640 \subsection from_structures_to_classes II.1 Von mathematischen Strukturen zu C++-Klassen 00641 00642 Die Grundidee des \c GroupIndex - Systems besteht darin, dass mathematische Strukturen 00643 in Form von Klassen der Programmiersprache C++ implementiert werden. Die Implementierung 00644 einer mathematischen Struktur soll nun anhand einer relativ simplen Struktur, nämlich 00645 einer \em Gruppe demonstriert werden. Eine Gruppe ist eine Menge \em G mit einer Verknüpfung * 00646 : \em G x G -> G, wobei \em G und * gewisse \em Axiome erfüllen; so gilt für alle 00647 Elemente \em a, \em b und \em c aus \em G 00648 \li * ist assoziativ, d.h. (\em a * \em b) * \em c = \em a * (\em b * \em c) 00649 \li Es existiert ein neutrales Element 1, d.h. 1 * \em a = \em a 00650 \li Zu jedem \em a existiert ein Inverses \em d mit \em a * \em d = 1. 00651 00652 Diese Axiome lassen sich nun übertragen auf C++-Klassen. An die Stelle der Gruppe \em G 00653 rückt nun eine \em Gruppenklasse \c Group. Eine \em Instanz von \c Group entspricht 00654 dann einem \em Element von \em G . An Stelle der aus der Mathematik gewohnten 00655 Sprechweise "g sei Element von \em G " kann also in C++ geschreiben werden als 00656 00657 \code 00658 Group g; 00659 \endcode 00660 00661 An die Stelle der Verknüpfung * der Gruppe \em G rückt ein C++-Operator: 00662 00663 \code 00664 Group operator*(const Group& a, const Group& b); 00665 \endcode 00666 00667 Das geforderte inverse Element zu einem Gruppenelement \em g - das üblicherweise als 00668 \c g^(-1) geschrieben wird - lässt sich ebenfalls in einen Operator übertragen. So erhält 00669 man das Inverse zu einer Instanz der Gruppenklasse \c Group durch den unären Operator 00670 00671 \code 00672 Group& operator!(Group& g); 00673 \endcode 00674 00675 Das neutrale Element lässt sich implementieren durch einen Zuweisungsoperator der Gestalt 00676 \code 00677 Group& Group::operator=(int i) 00678 { 00679 if (i==1) 00680 ...; 00681 } 00682 \endcode 00683 Damit erhält man das neutrale Element der Gruppe (bzw. "die neutrale Instanz" der Gruppenklasse) durch 00684 die Zuweisung 00685 \code 00686 Group g; 00687 g = 1; 00688 \endcode 00689 00690 00691 Die übrigen Gruppenaxiome werden implizit erfüllt - durch eine korrekte Implementierung 00692 der Operatoren. 00693 00694 \subsection groupindex_group_class II.2 Gruppenklassen 00695 00696 Eine Gruppenklasse für die \c GroupIndex - Klassen wird in Anlehnung an das im vorigen Abschnitt 00697 beschriebene Konzept implementiert; in einigen Details weicht das Konzept der Gruppenklasse 00698 von der zuvor beschriebenen Idee ab: 00699 \li An Stelle des \c operator*() wird der Operator \c Group::operator*=(const \c Group& \c g) verwendet. 00700 \li Der Invertierungsoperator \c Group::operator!() invertiert das \c this - Objekt und liefert 00701 das \c this - Objekt als Ergebnis zurück. Es wird also \em kein neues Objekt erzeugt, 00702 welches das Inverse enthält. Beispielsweise haben die folgenden Codezeilen zur Folge, 00703 dass \c g und \c h dasselbe Gruppenelement beinhalten: 00704 \code 00705 Group g,h; 00706 ... 00707 h = !g; 00708 \endcode 00709 Um die Wirkung zu erzielen, dass \c g das Inverse von \c h enthält, kann der folgende 00710 Programmcode verwendet werden: 00711 \code 00712 Group g,h; 00713 ... 00714 h = g; 00715 !h; 00716 \endcode 00717 \li Die Operatoren 00718 00719 Da zur Anwendung der \c GroupIndex - Klassen immer eine Gruppe benötigt wird, die auf einer Menge operiert, 00720 muss auch die Struktur einer \em Gruppenoperation implementiert werden. Dazu benötigt die 00721 Gruppenklasse ein Überladen des *= - Operators: 00722 \code 00723 Set& Group::operator*=(Set& s) const; 00724 \endcode 00725 Die Codezeilen 00726 \code 00727 Group g; 00728 Set s_1, s_2; 00729 ... 00730 s_2 = s_1; 00731 g *= s_2; 00732 \endcode 00733 haben dann im mathematischen Sinne die Bedeutung \em s_2 = \em g*s_1 , d.h. das Gruppenelement \em g 00734 operiert auf dem Mengenelement \em s_1. 00735 00736 In Zusammenhang mit der Gruppenoperation benötigen die \c GroupIndex - Klassen auch eine 00737 Funktion zur \em Repräsentantenberechnung. Diese muss durch eine (im globalen namespace sichtbare) Funktion 00738 \code 00739 void getRepresentation(const Set& m, Group& g_m, Set& r_m); 00740 \endcode 00741 implementiert werden. Diese Methode liefert zur Eingabe \c m jeweils ein Gruppen- und ein 00742 Mengenelement \c g_m bzw. \c r_m, so dass 00743 \code 00744 m == (g_m *= r_m) 00745 \endcode 00746 gilt. \c r_m muss dabei aus Element eines Repräsentantensystems \em R - einer 00747 geeigneten Teilmenge der \em G - Menge \em M - sein. 00748 Algebraisch gesehen berechnet \c getRepresentation also ein Gruppenelement, welches 00749 ein beliebiges Mengenelement auf seinen Repräsentanten abbildet sowie den zugehörigen 00750 Repräsentanten: 00751 00752 \em m = \em g_m \em r_m 00753 00754 Für die algebraischen Einzelheiten sei hier auf die entsprechende Literatur verwiesen. 00755 00756 Zusätzlich zu den mathematisch relevanten Operatoren und Funktionen benötigt die Gruppenklasse noch einige 00757 programmiertechnisch notwendigen Funktionen: 00758 \li Konstruktoren und Destruktor 00759 \li Stream-Operatoren \c operator<<() und \c operator>>() zum Lesen und Schreiben in Dateien 00760 \li die Vergleichsoperatoren \c operator==() \c operator!=() (siehe dazu auch nachfolgende 00761 Bemerkung zu Vergleichsoperatoren) 00762 \li durch den Operator 00763 \code 00764 bool operator<(const Group& g, const Group& h) 00765 \endcode 00766 muss eine totale Ordnung auf den Instanzen der Gruppenklasse implementiert sein. 00767 \li die Methode 00768 \code 00769 int Group::size(); \endcode 00770 gibt an, weiviele Bytes vom \c operator<<() in einen \c ofstream geschriben werden. 00771 Falls alle Instanzen der Klasse gleich viel Platz belegen, kann diese Methode 00772 auch als \c static implementiert werden. 00773 00774 Eine vollständige Deklaration einer abstrakten Gruppenklasse folgt im übernächsten Abschnitt; 00775 zunächst werden die Funktionen der Mengenklasse näher beschrieben. 00776 00777 \em Bemerkungen \em zu \em Vergleichsoperatoren \c operator==() \em und \c operator!=() 00778 00779 Die Vergleichsoperatoren \c operator==() \c operator!=() müssen \em beide implementiert werden, 00780 da sie zu Vergleichen in unterschiedlichen Situationen verwendet werden: 00781 Der \c operator==() wird beim Indexaufbau verwendet und sollte die angegebenen Objekte auf exakte Gleichheit prüfen. Der 00782 \c operator!=() hingegen wird bei der Suche verwendet; bei Bedarf kann der Ungleich-Operator 00783 auch dann ein \c false zurückliefern, wenn die beiden zu vergleichenden Objekte 00784 ähnlich -- aber nicht exakt gleich -- sind. Dadurch wird eine einfache Form der Fehlertoleranz erlaubt; 00785 allerdings verliert die Suche u.U. wichtige Eigenschaften, so z.B. die Assoziativität 00786 der Schnittmengenbildung. Die naheliegenste Form, den \c operator!=() zu implementieren ist 00787 natürlich ein 00788 \code 00789 bool myGroup::operator!=(const myGroup& cmp) 00790 { 00791 return !((*this)==g2); 00792 } 00793 \endcode 00794 sofern nicht die oben erwähnte Form der Fehlertoleranz bei der Suche 00795 gewünscht ist. 00796 00797 \subsection groupindex_set_class II.3 Mengenklassen 00798 00799 Die Instanzen einer Menge benötigen in den \c GroupIndex - Klassen keine weiteren 00800 Funktion im mathematischen Sinne - die Gruppenoperation und die Repräsentanenberechnung 00801 werden bereits ausserhalb der Mengenklasse implementiert. 00802 00803 Es verbleiben die programmiertechnisch notwendigen Methoden, die in einer Mengenklasse zu 00804 implementieren sind. Dazu gehören - fast identisch mit den Funktionen der Gruppenklasse: 00805 \li Konstruktoren und Destruktor 00806 \li Stream-Operatoren \c operator<<() und \c operator>>() zum Lesen und Schreiben in Dateien 00807 \li ein Vergleichsoperator \c operator==() 00808 \li durch den Operator 00809 \code 00810 bool operator<(const Group& g, const Group& h) 00811 \endcode 00812 muss eine totale Ordnung auf den Instanzen der Gruppenklasse implementiert sein. Es sei angemerkt, dass 00813 es hinreichend ist, wenn die durch diesen Operator gegebene Ordnung auf den Repräsentanten, die von 00814 \c getRepresentation() berechnet werden, eine totale Ordnung ist. 00815 \li die Methode 00816 \code 00817 int Set::size(); \endcode 00818 gibt an, wieviele Bytes vom \c operator<<() in einen \c ofstream geschrieben werden. 00819 Falls alle Instanzen der Klasse gleich viel Platz belegen, kann diese Methode 00820 auch als \c static implementiert werden. 00821 00822 In den meisten Fällen wird es sinnvoll sein, die zur Mengenklasse gehörige Gruppenklasse als \c friend 00823 zu deklarieren. 00824 00825 \subsection abstract_goup_and_set_class II.4 Abstrakte Klassen für Gruppen- und Mengenklassen 00826 00827 Die folgenden Klassendeklarationen sind abstrakte Musterklassen für Gruppen- und 00828 Mengenklassen. Es ist theoretisch möglich, durch Ableiten von den Klassen \c AbstractGroup 00829 und \c AbstractSet und Implementieren der virtuellen Methoden konkrete Gruppen- und 00830 Mengenklassen zu erstellen. Es empfiehlt sich aber, \em nicht von diesen Klassen abzuleiten 00831 und von der Verwendung virtueller Methoden abzusehen, da deren Verwendung mit 00832 Effizienzeinbußen verbunden ist. 00833 00834 Statt dessen können Gruppen- und Mengenklasse als Basisklassen implementiert werden, 00835 und zwar so, 00836 dass alle hier beschriebenen Methoden und Operatoren in diesen Klassen 00837 (möglichst nicht-virtuell) implementiert sind. 00838 Unter Umständen lohnt es sich sogar, die Verknüpfungen der Gruppe und der Gruppenoperation 00839 \c inline zu deklarieren. 00840 00841 \code 00842 class AbstractGroup 00843 { 00844 public: 00845 00846 virtual bool operator==(Group&); 00847 virtual bool operator!=(Group&); 00848 virtual bool operator<(Group&); 00849 00850 friend std::ofstream& operator<<(std::ofstream&, const Group&); 00851 friend std::ifstream& operator>>(std::ifstream&, const Group&); 00852 00853 virtual Group(); 00854 virtual Group(const Group&); 00855 virtual ~Group(); 00856 00857 virtual int size(); 00858 00859 virtual Group& operator!(); 00860 virtual Group& operator*=(Group&); 00861 virtual Set& operator*=(Set&); 00862 00863 }; 00864 00865 \endcode 00866 00867 \code 00868 class AbstractSet 00869 { 00870 00871 friend class AbstractGroup; 00872 00873 public: 00874 00875 virtual bool operator==(Set&); 00876 virtual bool operator<(Set&); 00877 00878 friend std::ofstream& operator<<(std::ofstream&, const Set&); 00879 friend std::ifstream& operator>>(std::ifstream&, const Set&); 00880 00881 virtual Set(); 00882 virtual Set(const Set&); 00883 virtual ~Set(); 00884 00885 virtual int size(); 00886 00887 }; 00888 00889 \endcode 00890 00891 \section how_to_implement III. Vorgehensweise zum Implementieren eines Index 00892 00893 Um ein Indexierungssystem mit der \c GroupIndex - Template-Library zu erstellen, sind 00894 i.A. vier Schritte notwendig: 00895 <ol> 00896 <li> Identifizieren der Gruppenoperation, die dem zu erstellenden Index zugrunde liegt, 00897 <li> Implementieren der entsprechenden Gruppen- und Mengenklasse, 00898 <li> Auswahl geeigneter Container-Klassen 00899 <li> Auswahl geeigneter Klassen für Repräsentantensystem und für invertierte Listen. 00900 </ol> 00901 00902 \subsection index_impl_subsect1 III.1 Identifizieren der Gruppenoperation 00903 Eine Erläterung des erstgenannten Punktes liegt "beyond the scope" dieser Dokumentation 00904 (die sich auf programmiertechnische Aspekte beschränkt). 00905 00906 00907 \subsection index_impl_subsect2 III.2 Implementieren von Gruppen- und Mengenklasse 00908 Ist die Menge mit der darauf operierenden Gruppe identifiziert, so sind für den 00909 entsprechenden Index genau zwei Klassen 00910 zu implementieren: eine Gruppen- und eine Mengenklasse, die wie im vorigen Abschnitt 00911 erläutert implementiert werden müssen. Ist die der Mengenklasse zugrundeliegende Menge 00912 nicht diskret, so ist evtl. eine geeignete Diskretisierung zu wählen; bei nicht-diskreten 00913 Gruppen empfiehlt es sich u.U. unscharfe Schnittmengenbildung zu verwenden. Dies erfordert bei der 00914 Implementierung zusätzlich einen Index-Operator für die Gruppenklasse (mehr dazu inder 00915 Dokumentation der Klasse \c groupindex::IntervalIntersectionGList ). Bei der Implementierung 00916 von \c getRepresentation() gilt es zu beachten, dass ggf. Container mit den benötigten 00917 Eigenschaften zum Speichern von (Fuzzy-) Repräsentanten und Stabilisator verwendet werden; 00918 Eine nähere Beschreibung dieser Container folgt im nächsten Abschnitt. 00919 00920 \subsection index_impl_subsect3 III.3 Auswahl von Container-Klassen 00921 Die vom \c GroupIndex verwendeten Container werden in gesonderten Klassen deklariert: 00922 Alle Container, die Mengenelemente enthalten, werden in einer Mengen-Container-Klasse 00923 deklariert, und alle Container, die Gruppenelemente enthalten, werden in einer Gruppen-Container-Klasse 00924 deklariert. Diese beiden Klassen - Mengen-Container-Klasse und Gruppen-Container-Klasse - werden 00925 der Klasse \c groupindex::GroupIndex als Template-Parameter übergeben. 00926 Folgende Eigenschaften der zugrundeliegenden Gruppenoperation bzw. Anforderungen 00927 an den zu erstellenden Index erfordern spezielle Container-Klassen: 00928 00929 \li Bei nicht-regulären Gruppenoperationen besteht der Stabilisator aus mehreren Elementen. 00930 Deshalb muss ein Gruppen-Container verwendet werden, welcher mehrere Gruppenelemente verwaltet. Es empfiehlt sich, die 00931 Klasse 00932 \code 00933 groupindex::nonregular_group_container<Group, GList> 00934 \endcode 00935 zu verwenden.Die Methode \c getRepresentation muss dann folgende Parameter verwenden: 00936 \code 00937 void getRepresentation(const Set& m, std::list<Group>& g_m, Set& r_m); 00938 \endcode 00939 00940 \li Zur Fuzzy-Suche wird nicht \em ein Repräsentant berechnet, sondern eine Menge von Repräsentanten. 00941 Dies erfordert einen Mengen-Container, welcher einen geeigneter Container zur Verfügung stellt. Dazu 00942 gibt es die Template Klasse 00943 \code 00944 groupindex::fuzzy_set_container<Group, GList> 00945 \endcode 00946 Auch die Fuzzy-Suche erfordert eine Anpassung der Parameter von \c getRepresentation : 00947 \code 00948 void getRepresentation(const Set& m, Group& g_m, std::list<Set>& r_m); 00949 \endcode 00950 00951 \li Die Kombination aus Fuzzy-Suche und nicht-regulären Gruppenoperationen ist ebenfalls zulässig, indem 00952 sowohl \c fuzzy_set_container als auch \c nonregular_group_container verwendet werden. 00953 \c getRepresentation verwendet dann die Parameter 00954 \code 00955 void getRepresentation(const Set& m, std::list<Group>& g_m, std::list<Set>& r_m); 00956 \endcode 00957 00958 \subsection index_impl_subsect4 III.4 Auswahl von Klassen für Repräsentantensystem und für invertierte Listen 00959 Abhängig von den Eigenschaften der Gruppenoperation sollten unterschiedliche Klassen 00960 zum Verwalten invertierter Listen verwendet werden (zur Verwaltung von Repräsentanten 00961 existiert z.Z. nur die Klasse \c groupindex::DefaultRepresentation). 00962 Für invertierte Liste stehen folgende Klassen zur Verfügung: 00963 \li \c groupindex::GInvertedList<Group> für exakte Schnittmengenbildung, 00964 \li \c groupindex::IntervalIntersectionGList<Group, numeric, Dim> für unscharfe Schnittmengenbildung sowie 00965 \li \c groupindex::CompressedGInvertedList<Group> (für komprimierte Indexe bei Gruppen, deren Verknüpfung die zur Indexierung 00966 verwendete Ordnung der Gruppenelemente erhält. In der aktuellen Version ist dieser Listentyp nicht funktionsfähig.) 00967 00968 Alle Listentypen unterstützen Fuzzy- und Mismatch-Suche. Bei der \c groupindex::IntervalIntersectionGList müssen neben der 00969 Gruppenklasse zusätzlich noch die Template-Parameter \c numeric und \c Dim angegeben werden. Deren Bedeutung liegt 00970 darin, dass die Gruppenklasse den Index-Operator \c operator[] mit Rückgabetyp \c numeric überlädt, um die Gruppe in einen 00971 geeigneten (kontinuierlichen) Vektorraum einzubetten: 00972 \code 00973 numeric Group::operator[](int) 00974 \endcode 00975 Jedes Gruppenelement \c g liefert also für \c g[0],g[1],...g[Dim-1] einen Wert vom Typ numeric. 00976 Der Typ \c numeric muss volle Artihmetik unterstützen, d.h. Addition, Subtraktion, Multiplikation und Division. 00977 Oft genügt ein Typ vom Wert \c double oder \c float , denkbar ist aber auch, den Typ 00978 \c real aus der Bibliothek Leda zu verwenden. Weitere Details und Bedingnungen an die Gruppenklasse finden sich in der 00979 Dokumentation der \c IntervalIntersectionGList . 00980 00981 \section intro_examples IV. Beispiele 00982 \warning Beispiele z.T. noch nicht getestet! -am 17.12.2001 00983 00984 In diesem Abschnitt werden einige Beispiele für die Auswahl von Containern und Listenklassen zur Umsetzung 00985 eines Indexierungsystems gegeben. Es werden lediglich die C++-Typdeklarationen angegeben, keine detaillierten 00986 Implementierungen der Gruppen- und Mengenklassen. Gruppen- bzw. Mengenklasse werden stets als \c myGroup bzw. 00987 \c mySet angegeben. 00988 00989 Alle \c typedefs bis auf die Deklaration des Typs \c myIndex sind lediglich Hilfsdeklarationen, um 00990 die Definitionen übersichtlicher zu gestalten. Zum weiteren Gebrauch im Indexierungssystem 00991 werden ausschließlich Instanzen der Klasse \c myIndex benötigt. 00992 00993 \subsection example1 IV.1 Beispiel 1: Reguläre Gruppenoperation ohne Fuzzy-Suche mit exakter Schnittmengenberechnung 00994 00995 \code 00996 typedef GInvertedList < myGroup > myGList; 00997 typedef DefaultRepresentation < 00998 mySet, 00999 myGroup, 01000 myGList 01001 > myRepresentation; 01002 typedef GroupIndex < 01003 mySet, 01004 myGroup, 01005 myRepresentation, 01006 myGList 01007 > myIndex; 01008 \endcode 01009 01010 \subsection example2 IV.2 Beispiel 2: Reguläre Gruppenoperation ohne Fuzzy-Suche mit unscharfer Schnittmengenberechnung 01011 01012 \code 01013 typedef IntervalIntersectionGList < myGroup , 7 , double > myGList; 01014 typedef DefaultRepresentation < 01015 mySet, 01016 myGroup, 01017 myGList 01018 > myRepresentation; 01019 typedef GroupIndex < 01020 mySet, 01021 myGroup, 01022 myRepresentation, 01023 myGList 01024 > myIndex; 01025 \endcode 01026 01027 \subsection example3 IV.3 Beispiel 3: Nicht-reguläre Gruppenoperation ohne Fuzzy-Suche mit unscharfer Schnittmengenberechnung 01028 01029 \code 01030 typedef nonregular_group_container < myGroup > my_group_container; 01031 typedef default_set_container < mySet > my_set_container; 01032 typedef IntervalIntersectionGList < myGroup , 7 , double > myGList; 01033 typedef DefaultRepresentation < 01034 mySet, 01035 myGroup, 01036 myGList, 01037 my_group_container, 01038 my_set_container 01039 > myRepresentation; 01040 typedef GroupIndex < 01041 mySet, 01042 myGroup, 01043 myRepresentation, 01044 myGList, 01045 my_group_container, 01046 my_set_container 01047 > myIndex; 01048 \endcode 01049 01050 \subsection example4 IV.4 Beispiel 4: Nicht-reguläre Gruppenoperation mit Fuzzy-Suche mit unscharfer Schnittmengenberechnung 01051 01052 \code 01053 typedef nonregular_group_container < myGroup > my_group_container; 01054 typedef fuzzy_set_container < mySet > my_set_container; 01055 typedef IntervalIntersectionGList < myGroup , 7 , double > myGList; 01056 typedef DefaultRepresentation < 01057 mySet, 01058 myGroup, 01059 myGList, 01060 my_group_container, 01061 my_set_container 01062 > myRepresentation; 01063 typedef GroupIndex < 01064 mySet, 01065 myGroup, 01066 myRepresentation, 01067 myGList, 01068 my_group_container, 01069 my_set_container 01070 > myIndex; 01071 \endcode 01072 01073 \section intro_commandline V. Erzeugen simpler Benutzer-Schnittstellen mit Hilfe der Template-Klasse IndexCommandLine 01074 01075 Die Template-Klassen für Repräasentantensystem, invertierte Listen und Indexe stellen 01076 lediglich ein Index-Objekt zur Verfügung, mit dem durch Aufruf der Methoden eines 01077 Index-Objektes die Funktionen eines Indexierungssystems (Einfügen, Suchen und Löschen) 01078 ausgeführt werden können. Um diese Funktionen auch einem Benutzer zur Verfügung zu stellen, 01079 ohne die C++-Schnittstellen verwenden zu müssen, gibt es die Template-Klasse \c IndexCommandLine. 01080 Diese erzeugt für eine fertige Indexklasse aus einem anzugebenen Interface zum Einlesen 01081 von Daten ein kleines Kommandozeilen-Tool, mit dem die Funktionen eines Index per 01082 Tastatureingabe einiger primitiver Befehle (z.B. ADD, REMOVE oder SEARCH) gesteuert 01083 werden können. 01084 01085 Eine genaue Beschreibung liegt in der Dokumentation zur Klasse \c groupindex::IndexCommandLine vor. 01086 01087 */ 01088 01092 namespace groupindex 01093 { 01094 01134 template 01135 <class Set, // Klasse für Elemente der Menge M' 01136 class Group, // Klasse für Elemente der Gruppe G 01137 class Representation = DefaultRepresentation<Set, Group>, // Klasse fuer Elemente aus dem Repraesentantensystem R' 01138 class GList = GInvertedList<Group>, 01139 class GroupContainers = default_group_container<Group, GList>, // Diese Klasse enthält typedefs für Iteratoren, die für Gruppenelemente bzw. G-inverteirte Listen zu verwenden sind 01140 class SetContainers = default_set_container<Set> // Diese Klasse enthält typedefs für Iteratoren, die für Mengenelemente zu verwenden sind 01141 > 01142 01143 class GroupIndex 01144 { 01145 01146 private: 01147 01149 NameToIndexMap nameToIndexMap; 01151 IndexToNameMap indexToNameMap; 01152 01156 int maxFileIndex; 01157 01159 clock_t insertion_time; 01160 01161 public: 01162 01163 typedef Group IndexGroup; 01164 typedef Set IndexSet; 01165 typedef Representation IndexRepresentation; 01166 01168 Representation representation; 01169 01171 time_t start; 01172 01174 time_t finish; 01175 01184 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01185 std::string getFileName(int fileindex) 01186 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01187 { 01188 IndexToNameMap::iterator it; 01189 it = indexToNameMap.find(fileindex); 01190 if (it != indexToNameMap.end()) 01191 return indexToNameMap[fileindex]; 01192 else 01193 { 01194 throw IndexingException(IndexingException::UNKNOWN_FILEINDEX); 01195 return std::string(); // leeren String zurückliefern. 01196 } 01197 } 01198 01203 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01204 std::string getUniqueFileName(std::string& filename) 01205 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01206 { 01207 if (!hasFileIndex(filename)) 01208 return filename; 01209 01210 int i=0; 01211 char buffer[20]; 01212 01213 while (true) 01214 { 01215 01216 std::string ufilename = filename; 01217 my_itoa(i,buffer,10); 01218 ufilename.append("_"); 01219 ufilename.append(buffer); 01220 if (!hasFileIndex(ufilename)) 01221 return ufilename; 01222 01223 } 01224 } 01225 01227 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01228 bool hasFileIndex(const std::string& filename) 01229 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01230 { 01231 NameToIndexMap::iterator it; 01232 it = nameToIndexMap.find(filename); 01233 if (it == nameToIndexMap.end()) 01234 return false; 01235 else 01236 return true; 01237 } 01238 01239 01248 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01249 int getFileIndex(std::string filename) 01250 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01251 // liefert den zu filename gemappten index zurück. 01252 // bestizt filename keinen index, so wird ein neuer angelegt. 01253 { 01254 NameToIndexMap::iterator it; 01255 it = nameToIndexMap.find(filename); 01256 if (it != nameToIndexMap.end()) 01257 return nameToIndexMap[filename]; 01258 else 01259 return createNewFileMapping(filename); 01260 } 01261 01262 private: 01275 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01276 int createNewFileMapping(std::string filename) 01277 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01278 { 01279 01280 if (filename.empty()) 01281 return -1; 01282 01283 int fileindex = maxFileIndex; 01284 maxFileIndex++; 01285 01286 nameToIndexMap.insert(NameToIndexEntry(filename, fileindex)); 01287 indexToNameMap.insert(IndexToNameEntry(fileindex, filename)); 01288 01289 return fileindex; 01290 } 01291 01292 public: 01297 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01298 GroupIndex(Representation sr) 01299 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01300 { 01301 representation = sr; 01302 init(); 01303 }; 01304 01306 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01307 GroupIndex() 01308 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01309 { 01310 representation = Representation(); 01311 init(); 01312 }; 01313 01315 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01316 ~GroupIndex() 01317 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01318 { 01319 01320 clear(); 01321 01322 } 01323 01328 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01329 void clear() 01330 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01331 { 01332 indexToNameMap.clear(); 01333 nameToIndexMap.clear(); 01334 representation.clear(); 01335 } 01336 01337 private: 01338 01345 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01346 virtual void initFileRead(char* filename) 01347 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01348 { 01349 std::cout<<"initFileRead\n"; 01350 std::/*i*/fstream ifs; 01351 ifs.open(filename,std::ios::binary); 01352 01353 if (!ifs) 01354 { 01355 throw IndexingException(IndexingException::FILE_ERROR_OPEN); 01356 } 01357 01358 initFileRead(ifs); 01359 01360 } 01361 01362 public: 01363 01370 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01371 GroupIndex(Representation& rep, const char* filename) 01372 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01373 { 01374 std::/*i*/fstream ifs; 01375 ifs.open(filename,std::ios::in | std::ios::binary); 01376 representation = rep; 01377 initFileRead(ifs); 01378 } 01379 01387 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01388 GroupIndex(Representation rep, std::/*i*/fstream& ifs) 01389 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01390 { 01391 representation = rep; 01392 initFileRead(ifs); 01393 } 01394 01402 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01403 //void initFileRead(ifstream ifs) 01404 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01405 //{ 01406 // initFileRead(fdopen(ifs.fd(),"rb")); 01407 //} 01408 01409 private: 01417 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01418 void initFileRead(std::/*i*/fstream& ifs) 01419 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01420 { 01421 01422 ifs.seekg(0,std::ios::beg); 01423 ifs.seekp(0,std::ios::beg); 01424 01425 01426 // BEGIN DEBUG 01427 long ffpos = ifs.tellg(); 01428 // END DEBUG 01429 01430 if (!ifs || ifs.eof()) 01431 { 01432 nameToIndexMap.clear(); 01433 indexToNameMap.clear(); 01434 return; 01435 } 01436 01437 01438 char c_null = '\0'; 01439 char buffer[2048]; 01440 maxFileIndex = 0; 01441 01442 // Anzahl der Files lesen 01443 int filecount; 01444 // BEGIN DEBUG 01445 ffpos = ifs.tellg(); 01446 // END DEBUG 01447 ifs.read((char*)&filecount, sizeof(int)); 01448 // BEGIN DEBUG 01449 ffpos = ifs.tellg(); 01450 // END DEBUG 01451 if (!ifs || ifs.eof()) 01452 { 01453 nameToIndexMap.clear(); 01454 indexToNameMap.clear(); 01455 return; 01456 } 01457 01458 for (int i=0; i<filecount; i++) 01459 { 01460 // fileindex lesen 01461 int fileindex; 01462 // BEGIN DEBUG 01463 ffpos = ifs.tellg(); 01464 // END DEBUG 01465 ifs.read((char*)&fileindex, sizeof(int)); 01466 // BEGIN DEBUG 01467 ffpos = ifs.tellg(); 01468 // END DEBUG 01469 int i=0; 01470 do ifs.read(&buffer[i],1); while (buffer[i++]!=0); 01471 01472 std::string filename = std::string(buffer); 01473 01474 // (fileindex, filename)-Tupel anlegen 01475 nameToIndexMap.insert(NameToIndexEntry(filename, fileindex)); 01476 indexToNameMap.insert(IndexToNameEntry(fileindex, filename)); 01477 01478 if (fileindex>maxFileIndex) 01479 maxFileIndex = fileindex; 01480 } 01481 01482 // BEGIN DEBUG 01483 ffpos = ifs.tellg(); 01484 // END DEBUG 01485 01486 // G-Listen zum Lesen vorbereiten 01487 if (filecount>0) 01488 { 01489 ifs>>representation; 01490 maxFileIndex++; 01491 } 01492 01493 01494 } 01495 01496 public: 01504 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01505 GroupIndex(std::/*i*/fstream& ifs) 01506 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01507 { 01508 initFileRead(ifs); 01509 }; 01510 01512 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01513 void init() 01514 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01515 { 01516 01517 insertion_time = 0; 01518 maxFileIndex = 0; 01519 representation.init(); 01520 01521 }; 01522 01523 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01524 friend std::ofstream& operator<< (std::ofstream& o, GroupIndex& idx) 01525 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01526 { 01527 01528 idx.writeToFile(o); 01529 return o; 01530 } 01531 01532 private: 01542 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01543 void writeToFile(std::ofstream& o) 01544 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01545 { 01546 01547 char c_null = '\0'; 01548 // fileindex<->filename - Map in Datei schreiben 01549 01550 // Anzahl der Dateien schreiben 01551 int filecount = nameToIndexMap.size(); 01552 01553 o.write((char*)&filecount, sizeof(int)); 01554 01555 NameToIndexMap::iterator it = nameToIndexMap.begin(); 01556 01557 while (it!=nameToIndexMap.end()) 01558 { 01559 std::string fname = (*it).first; 01560 int findex = (*it).second; 01561 01562 // fname und findex in stream schreiben 01563 o.write((char*)&findex, sizeof(int)); 01564 int flen = fname.size(); 01565 o.write((char*)fname.c_str(), flen+1); 01566 01567 ++it; 01568 } 01569 01570 // G-Listen in Datei schreiben 01571 o<<representation; 01572 01573 finish = clock(); 01574 insertion_time += (finish-start); 01575 01576 #ifndef CHURCHILL // wenn CHURCHILL definiert ist, wird keine Statistik erzeugt 01577 FILE* stat_stream; 01578 stat_stream = fopen("groupindex.log","w"); 01579 if (stat_stream!=NULL) 01580 { 01581 writeStatistics(stat_stream); 01582 fclose(stat_stream); 01583 } 01584 01585 #endif 01586 01587 }; 01588 01589 public: 01593 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01594 void writeStatistics(FILE* stream) 01595 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01596 { 01597 double total_time = ((double)(1000.0*insertion_time)) / CLOCKS_PER_SEC; 01598 fprintf(stream,"Gesamtzeit zum Indexieren ohne Einlesen der Daten: %f secs\n",total_time/1000.0); 01599 representation.writeStatistics(stream); 01600 } 01601 01602 01615 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01616 void renameFile(std::string old_filename, std::string new_filename) 01617 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01618 { 01619 01620 if (!hasFileIndex(old_filename)) 01621 { 01622 throw IndexingException(IndexingException::NO_SUCH_FILE); 01623 } 01624 01625 if (hasFileIndex(new_filename)) 01626 { 01627 throw IndexingException(IndexingException::FILE_ALREADY_EXISTS); 01628 } 01629 01630 int findex = getFileIndex(old_filename); 01631 01632 indexToNameMap.erase(findex); 01633 nameToIndexMap.erase(old_filename); 01634 01635 nameToIndexMap[new_filename] = findex; 01636 indexToNameMap[findex] = new_filename; 01637 01638 } 01639 01645 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01646 void remove(std::string& filename) 01647 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01648 { 01649 // filename aus der Zuordnung filename <-> fileindex herausnehmen 01650 01651 int fileindex = getFileIndex(filename); 01652 01653 IndexToNameMap::iterator iit; 01654 iit = indexToNameMap.find(fileindex); 01655 if (iit != indexToNameMap.end()) 01656 indexToNameMap.erase(iit); 01657 01658 NameToIndexMap::iterator nit; 01659 nit = nameToIndexMap.find(filename); 01660 if (nit != nameToIndexMap.end()) 01661 nameToIndexMap.erase(nit); 01662 01663 // fileindex aus allen G-invertierten Listen herausnehmen 01664 01665 representation.remove(fileindex); 01666 01667 } 01668 01674 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01675 void remove(char* filename) 01676 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01677 { 01678 std::string str_filename(filename); 01679 remove(str_filename); 01680 } 01681 01686 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01687 void insert(Set& m, const std::string& filename) 01688 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01689 { 01690 clock_t start = clock(); 01691 01692 int fileindex = getFileIndex(filename); 01693 representation.insert(m,fileindex); 01694 01695 clock_t finish = clock(); 01696 insertion_time += (finish-start); 01697 01698 } 01699 01704 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01705 void insert(Set& m, char* filename) 01706 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01707 { 01708 insert(m, std::string(filename)); 01709 } 01710 01737 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01738 int merge(char* indexfile2, int mode = MODE_MERGE_DELETE) 01739 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01740 { 01741 01742 std::/*i*/fstream idxstream2; 01743 idxstream2.open(indexfile2, std::ios::in | std::ios::binary); 01744 01745 // geändert für MultiDimGList (r.b. 31.01.2002) 01746 // if (idxstream2 == NULL) 01747 if (idxstream2.bad()) 01748 return -1; 01749 01750 int succ = merge(idxstream2,mode); 01751 01752 idxstream2.close(); 01753 01754 return succ; 01755 01756 } 01757 01784 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01785 int merge(std::/*i*/fstream& idxstream2, int mode = MODE_MERGE_DELETE) 01786 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01787 { 01788 01789 GroupIndex<Set,Group,Representation>* mergeIndex = new GroupIndex<Set,Group,Representation>(idxstream2); 01790 01791 std::set<std::string> deleteList; 01792 01793 // I. Schritt: Zuordnung zwischen Dateinummern und Dateinamen mergen 01794 01795 NameToIndexMap::iterator it; 01796 01797 std::pair<std::string,int> val; 01798 01799 for (it = nameToIndexMap.begin(); it != nameToIndexMap.end(); it++) 01800 { 01801 01802 std::string fname = (*it).first; 01803 if (mergeIndex->hasFileIndex(fname)) 01804 { 01805 // Da ham'mer den Salat: Namenskonflikt! 01806 if (mode==MODE_MERGE_MAKE_UNIQUE) 01807 { 01808 // eindeutigen Dateinamen besorgen 01809 std::string ufname = getUniqueFileName(fname); 01810 // und umbenennen 01811 mergeIndex->renameFile(fname,ufname); 01812 } else if (mode==MODE_MERGE_DELETE) { 01813 // MODE_MERGE_DELETE 01814 // Datei in die deleteList aufnehmen 01815 deleteList.insert(fname); 01816 } // else : mode==MODE_MERGE_IGNORE -> nix tun 01817 } 01818 } 01819 01820 // II. Schritt: ListTupel aus idx2 nachschlagen und in idx1 einfügen 01821 int i; 01822 for (i=0; i<mergeIndex->representation.size(); i++) 01823 { 01824 GList* glist = mergeIndex->representation[i]; 01825 01826 // wer das folgende typename verstehen will, lese bitte im Stroustrup nach ;) 01827 typename GList::iterator it = glist->begin(); 01828 typename GList::iterator last = glist->end(); 01829 01830 for (; it!=last; it++) 01831 { 01832 ListTupel<IndexGroup> lt = *it; 01833 std::string fname = mergeIndex->getFileName(lt.i); 01834 Set s = mergeIndex->representation.getRepresentative(i); 01835 01836 // durch die folgenden beiden Zeilen ist sichergestellt, 01837 // dass die G-invertierte Liste zu s ggf. neu angelegt wird. 01838 GList* insertList = representation[s]; 01839 01840 std::set<std::string>::iterator ci = deleteList.find(fname); 01841 if (ci==deleteList.end() || mode==MODE_MERGE_MAKE_UNIQUE || mode==MODE_MERGE_IGNORE) 01842 { 01843 int findex = getFileIndex(fname); 01844 insertList->insert(lt.g,findex); 01845 } else { 01846 // Namenskonflikt, mode==MODE_MERGE_DELETE 01847 // -> nix tun 01848 } 01849 } 01850 } 01851 01852 idxstream2.close(); 01853 01854 delete mergeIndex; 01855 01856 return 0; 01857 01858 } 01859 01873 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01874 int getIndexNameConflicts(char* indexfile2, std::set<std::string>& conflicting_names) 01875 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01876 { 01877 01878 int conflict_count = 0; 01879 01880 FILE* idxstream2 = fopen(indexfile2, "rb"); 01881 if (idxstream2 == NULL) 01882 return -1; 01883 01884 GroupIndex<Set,Group,Representation> mergeIndex = GroupIndex<Set,Group,Representation>(); 01885 mergeIndex.initFileRead(std::/*i*/fstream(fileno(idxstream2))); 01886 01887 01888 // Dateinamen aus idx1 in idx2 nachschlagen 01889 01890 for (int i=0; i<maxFileIndex; i++) 01891 { 01892 std::string fname = getFileName(i); 01893 if (mergeIndex.hasFileIndex(fname)) 01894 { 01895 01896 // Da ham'mer den Salat: Namenskonflikt! 01897 conflict_count++; 01898 conflicting_names.insert(conflicting_names.end(),fname); 01899 01900 } 01901 } 01902 01903 fclose(idxstream2); 01904 01905 return conflict_count; 01906 01907 } 01908 01921 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01922 int getFilenames(std::set<std::string>& filenames) 01923 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01924 { 01925 01926 NameToIndexMap::iterator it; 01927 01928 std::pair<std::string,int> val; 01929 01930 for (it = nameToIndexMap.begin(); it != nameToIndexMap.end(); it++) 01931 filenames.insert((*it).first); 01932 01933 return filenames.size(); 01934 01935 } 01936 01943 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01944 long search(Set* query, int query_size, ListTupel<IndexGroup>*& result) 01945 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01946 { 01947 return search(query, query_size, result, 0); 01948 } 01949 01957 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01958 long search(Set* query, int query_size, ListTupel<IndexGroup>*& result, int mismatches) 01959 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// 01960 { 01961 typename SetContainers::query_container c_query; 01962 01963 // query-Array in query_container übertragen 01964 for (int i=0; i<query_size; i++) 01965 c_query.insert(c_query.begin(), query[i]); 01966 01967 typename GroupContainers::result_container c_result; 01968 long rsize; 01969 01970 rsize = search(c_query, c_result, mismatches); 01971 01972 result = new ListTupel<IndexGroup>[rsize]; 01973 typename GroupContainers::result_container::iterator r_it = c_result.begin(); 01974 typename GroupContainers::result_container::iterator r_end = c_result.end(); 01975 01976 int j=0; 01977 rsize = 0; // 3.7.02 r.b. 01978 for (; r_it!=r_end; r_it++) 01979 { 01980 result[j] = *r_it; 01981 j++; 01982 rsize++; // 3.7.02 r.b. 01983 } 01984 return rsize; 01985 01986 } 01987 01997 long search(const typename SetContainers::query_container& query, typename GroupContainers::result_container& result, int mismatches=0) 01998 { 01999 02000 if (mismatches>=query.size()) 02001 throw IndexingException(IndexingException::INVALID_QUERY); 02002 02003 // Ermitteln der Ergebnislisten 02004 02005 result.clear(); 02006 if (query.size()<=0) 02007 { 02008 return 0; 02009 } 02010 02011 // result_lists und operator_list sind Arrays von Pointern, 02012 // damit beim Sortieren niemand auf die Idee kommt, copy-Konstruktoren aufzurufen... 02013 GList** result_lists = new GList*[query.size()]; 02014 02015 typename SetContainers::query_container::const_iterator it = query.begin(); 02016 typename SetContainers::query_container::const_iterator last = query.end(); 02017 02018 int rl_count = 0; 02019 for (;it!=last; it++) 02020 { 02021 02022 // r_m besteht aus einer Menge von Fuzzy-Repräsentanten 02023 typename SetContainers::representation_container r_m; 02024 02025 // g_m muss nicht den gesamten Stabilisator beinhalten, sondern nur ein Element aus diesem, 02026 // weil der Stabilisator bereits komplett in den invertierten Listen gespeichert ist. 02027 // C++-syntaktisch funktioniert das, weil der Stabilisator-Container 02028 // zuweisungskompatibel mit Group sein muss. 02029 Group g_m; 02030 typename GroupContainers::stabilizer_container stab; 02031 getRepresentation(*it, stab, r_m); 02032 g_m = stab; 02033 02034 02035 typename SetContainers::representation_container::iterator r_it = r_m.begin(); 02036 typename SetContainers::representation_container::iterator r_last = r_m.end(); 02037 02038 // ermittle nun base_list als die erste NICHT LEERE (!) invertierte Liste der Fuzzy-Repräsentanten 02039 GList* base_list = representation[*(r_it)]; 02040 02041 r_it++; 02042 while (r_it != r_last && base_list==NULL) 02043 { 02044 base_list = representation[*(r_it)]; 02045 r_it++; 02046 } 02047 02048 // merge base_list mit allen weiteren nicht-leeren Listen der Fuzzy-Repräsentanten 02049 if (base_list!=NULL) 02050 { 02051 // Kopie der Basisliste anlegen! 02052 base_list = new GList(*base_list); 02053 #ifndef DONT_MERGE_WHILE_SEARCHING 02054 while (r_it!=r_last) 02055 { 02056 GList* merge_with_list; 02057 merge_with_list = representation[*(r_it++)]; 02058 if (merge_with_list != NULL) 02059 base_list->merge(*merge_with_list); 02060 } 02061 #else 02062 #endif 02063 02064 *base_list *= (!g_m); 02065 02066 result_lists[rl_count++] = base_list; 02067 } else { 02068 // Wenn alle Listen der Fuzzy-Repräsentanten leer waren, wird dafür für alle 02069 // Treffer-Kandidaten ein Mismatch benötigt. 02070 mismatches--; 02071 } 02072 02073 } 02074 02075 if (rl_count==0) 02076 { 02077 delete[] result_lists; 02078 return 0; 02079 } 02080 02081 // mache das Array result_lists zu einem heap 02082 // (z.B. bezüglich Größe der Listen; nach welchem Kriterium der heap 02083 // verwaltet wird, entscheidet GList::operator>() 02084 heapify< GList >(result_lists, rl_count); 02085 02086 // ... und jetzt *effizient* die Schnittmenge aller Listen ermitteln 02087 02088 int count = rl_count; 02089 02090 GList* current_list = removeTopFromHeap< GList >(result_lists, count); 02091 int sect_list_size = current_list->size(); 02092 02093 // die Ausgangsmenge dient auch als Ergebnismenge, deshalb arbeiten wir auf einer Kopie dieser Liste 02094 // (-> Aufruf des copy-Konstrukturs!) 02095 02096 // Bei der Suche muss der nicht-reguläre Fall nicht weiter beachtet werden, wenn 02097 // die invertierten Listen bereits beim Einfügen mit dem gesamten Stabilisator 02098 // gefüllt werden. 02099 // 02100 // Wie sich leicht zeigen lässt, genügt es unter dieser Voraussetzung, bei der 02101 // Suche mit einem beliebigen Element aus dem Stabilisator zu arbeiten. 02102 // Dass dieses Vorgehen i.A. auch effizienter ist, zeigt die folgende Überlegung: 02103 // 02104 // Es bezeichne t \in \N die Grösse des (endlichen) Stabilisators und N die 02105 // Anzahl der Einträge in der invertierten Liste L \em modulo \em Stabilisator. 02106 // Dann hat nach oben vorgeschlagenem Vorgehen (Einfügen des gesamten Stabilisators 02107 // in die invertierten Listen) die invertierte Liste L t*N Einträge. Durchsuchen 02108 // dieser Liste kostet 02109 // (*) O(log (t*N)) = O(log t + log N) 02110 // Zeit. 02111 // 02112 // Nach dem alternativen Vorgehen werden die invertierten Listen modulo dem Stabilisator 02113 // gespeichert, enthalten also nur N Einträge. Zum Nachschlagen in dieser Liste 02114 // muss allerdings t mal in dieser Liste mit einem Zeitbedarf von je O(log N) gesucht 02115 // werden, was zu einem Gesamtaufwand von 02116 // (**) O(t*log N) 02117 // führt, was weniger effizient als (*) ist. 02118 02119 GList* merge_list = NULL; 02120 02121 current_list->setCredit(mismatches); 02122 02123 // Eingefügt für MultiDimGList: 02124 current_list->setHeapStream(representation.getHeapStream()); 02125 02126 while (count>0 && current_list->size()>0) 02127 { 02128 // Beachte: count wird durch removeTopFromHeap um eins verringert! 02129 merge_list = removeTopFromHeap< GList >(result_lists, count); 02130 02131 // Eingefügt für MultiDimGList: 02132 merge_list->setHeapStream(representation.getHeapStream()); 02133 02134 // Schnittmenge der Listen bilden 02135 current_list->intersect(*merge_list, mismatches); 02136 02137 } 02138 02139 int rsize = current_list->size(); 02140 02141 // Ergebnisse der Schnittmengenbildungen nach result übertragen 02142 // kleine Änderung zwecks Test mit MultDimGList: (true) 02143 typename GList::iterator res_it = current_list->begin(true); 02144 typename GList::iterator res_last = current_list->end(true); 02145 02146 while (res_it != res_last) 02147 { 02148 result.insert(result.begin(),*res_it); 02149 res_it++; 02150 } 02151 02152 // Speicher freigeben 02153 for (int i=0; i<rl_count; i++) 02154 delete result_lists[i]; 02155 delete[] result_lists; 02156 02157 return rsize; 02158 02159 } 02160 02161 02162 02163 }; 02164 02165 02166 02167 02168 } 02169 02170 #endif