/* ---- This file is part of SECONDO. Copyright (C) 2004, University in Hagen, Department of Computer Science, Database Systems for New Applications. SECONDO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SECONDO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- //paragraph [1] title: [{\Large \bf ] [}] //[ae] [\"{a}] //[ue] [\"{u}] [1] ArrayAlgebra December 2003 Oliver L[ue]ck This algebra provides a type constructor ~array~, which defines a ["]generic["] array. The elements of the array must have an internal list representation. The definition of arrays has been tested for arrays of relations (type constructor ~rel~, only in the main memory version!), b-trees (~btree~) and the standard types ~int~, ~real~, ~bool~ and ~string~. The four basic operators ~size~, ~get~, ~put~ and ~makearray~ are used to get the size of an array, to retrieve an element with a given index, to set a value to an element with a given index or to construct an array from a given list of elements. Note that the first element has the index 0. A precondition for the operators ~get~ and ~put~ is a valid index between 0 and the size of the array minus 1. The operator ~sortarray~ arranges the elements of an array according to their integer values of a given functon. The operator ~tie~ concatenates all elements of an array with a given function, e.g. it sums up all elements of an integer array. The operator ~cumulate~ calculates the ["]cumulative["] values for each position of the array (see detailed operator specification). The operator ~distribute~ creates an array of relations from a tuple stream (by ["]distributing["] the tuples into the relations of the array). The operator ~summarize~ provides a tuple stream containing all tuples stored in an array of relations (similar to the operator ~feed~ of the relational algebra for a single relation). The operator ~loop~ evaluates each element of an array with a given function and returns an array which contains the resulting values. Based upon this elementary operator a ["]family["] of more complex ~loop~ operators is defined, which are capable to work with two arrays or with more than one function. The aim is to achieve optimization through switching between these functions or the selection of a function (at best of the most efficient function). Therefore, as a precondition, all functions have to be equivalent (with regard to the calculated values). The operator ~partjoin~ (and its extensions ~partjoinswitch~ and ~partjoinselect~) realizes a special way to calculate ["]joins["] between two arrays of relations. Nevertheless, the result of this operator is also an ~array~ (see detailed operator specification). Two additional type operators called ~ELEMENT~ and ~ELEMENT2~ support the technique of implicit parameter types (for the declaration of parameter functions). These operators are used by the parser and are not for use with sos-syntax. August 2004, M. Spiekermann. Function ~distributeFun~ has been modified. Due to the fact that a relation creates two files the size of the array is limited by the operatings systems capability of open files per process. Currently it is possible to create approximately 400 relations. August 24, 2004. M. Spiekermann removed a memory leak in the function ~distributeFun~ December 2005, Victor Almeida deleted the deprecated algebra levels (~executable~, ~descriptive~, and ~hibrid~). Only the executable level remains. Models are also removed from type constructors. February 2006, M. Spiekermann. Bug fixes in type mapping of the ~distribute~ and in the value mapping of the ~summarize~ operator. June 2006, V. Almeida moved some code to the FunVector.h and FunVector.cpp files Januar 2007, Some bug fixes. However, loopswitcha and loopselecta still do not work for arrays of btree. 1 Preliminaries 1.1 Includes */ #include #include "Algebra.h" #include "NestedList.h" #include "NList.h" #include "QueryProcessor.h" #include "AlgebraManager.h" #include "StandardTypes.h" #include "Algebras/Relation-C++/RelationAlgebra.h" #include "time.h" #include "FunVector.h" #include "ArrayAlgebra.h" #include "ListUtils.h" using namespace std; extern NestedList* nl; extern QueryProcessor* qp; extern AlgebraManager* am; namespace arrayalgebra{ /* 1.2 Dummy Functions */ static void* DummyCast( void* addr ) { return (0); } /* 1.3 Auxiliary Functions The function ~toString~ just converts an integer value to a string. The function ~extractIds~ ["]extracts["] the id-numbers of the algebra and the type from a given type expression (nested list). This type expression must already be in the numeric format. */ string toString( int number ) { ostringstream o; o << number << char(0); return o.str(); } void extractIds( const ListExpr numType, int& algebraId, int& typeId ) { ListExpr pair; if (nl->IsAtom(nl->First(numType))) { pair = numType; } else { pair = nl->First(numType); } algebraId = nl->IntValue(nl->First(pair)); typeId = nl->IntValue(nl->Second(pair)); } /* The following ["]generic["] clone function is used by several operators in order to clone objects. Some types may provide just a dummy clone function. In this case the list representation for input and output of objects (if defined) may be used for cloning. */ Word Array::genericClone( int algebraId, int typeId, ListExpr typeInfo, Word object ) { Word clone; // Try cloning with the clone function of the appropriate type clone = (am->CloneObj(algebraId, typeId))(typeInfo, object); if (clone.addr == 0) { // Try cloning via the object's list representation ListExpr objectLE; int errorPos=0; ListExpr errorInfo; bool correct; objectLE = (am->OutObj(algebraId, typeId))(typeInfo, object); clone = (am->InObj(algebraId, typeId)) (typeInfo, objectLE, errorPos, errorInfo, correct); assert (correct); } return clone; } /* 2 Type Constructor ~array~ 2.1 Data Structure - Class ~Array~ At first a data structure for storing an ~array~ in the main memory is defined. Since an object is represented as a storage Word (which is often a pointer to the actual object), the data structure of an ~array~ is an array of Word. */ Array::Array( int algebraId, int typeId, int n, Word* elements ) // Constructor with complete initialization of the array { defined = true; elemAlgId = algebraId; elemTypeId = typeId; size = n; array = new Word[size]; for (int i=0; iOneElemList( nl->SymbolAtom( "ERRORS" ) ); bool correct; valueRecord.Read( &valueLength, sizeof(valueLength), offset ); offset += sizeof(valueLength); char* buffer = new char[valueLength]; valueRecord.Read( buffer, valueLength, offset ); offset += valueLength; valueString.assign( buffer, valueLength ); delete []buffer; nl->ReadFromString( valueString, valueList ); value = RestoreFromListArray( typeInfo, nl->First(valueList), 1, errorInfo, correct ); if (errorInfo != 0) { nl->Destroy( errorInfo ); } nl->Destroy( valueList ); return (true); } bool SaveArray( SmiRecord& valueRecord, size_t& offset, const ListExpr typeInfo, Word& value) { //cerr << "*SaveArray" << endl; ListExpr valueList; string valueString; int valueLength; valueList = SaveToListArray( typeInfo, value ); valueList = nl->OneElemList( valueList ); nl->WriteToString( valueString, valueList ); valueLength = valueString.length(); valueRecord.Write( &valueLength, sizeof(valueLength), offset ); offset += sizeof(valueLength); valueRecord.Write( valueString.data(), valueString.length(), offset ); offset += valueString.length(); nl->Destroy( valueList ); return (true); } /* 2.6 Object ~Creation~, ~Deletion~, ~Close~, ~Clone~ and ~SizeOf~ Functions The ~Clone~ and the ~Close~ functions use the appropriate functions of the elements of the array. Additional details are explained within these function. */ Word CreateArray( const ListExpr typeInfo ) { //cerr << "*CreateArray" << endl; return SetWord(new Array()); } void DeleteArray( const ListExpr typeInfo, Word& w ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* array = (Array*)w.addr; //cerr << "*DeleteArray, defined =" << array->isDefined() << endl; if (array->isDefined()) { string typeName = sc->GetTypeName(array->getElemAlgId(), array->getElemTypeId()); for (int i=0; igetSize(); i++) { Word element = array->getElement(i); (am->DeleteObj(array->getElemAlgId(), array->getElemTypeId()))(nl->TheEmptyList(), element); } } delete array; w.addr = 0; } void CloseArray( const ListExpr typeInfo, Word& w ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* array = (Array*)w.addr; //cerr << "*CloseArray, defined =" << array->isDefined() << endl; if (array->isDefined()) { string typeName = sc->GetTypeName(array->getElemAlgId(), array->getElemTypeId()); for (int i=0; igetSize(); i++) { Word element = array->getElement(i); (am->CloseObj(array->getElemAlgId(), array->getElemTypeId()))(nl->TheEmptyList(), element); } } delete array; w.addr = 0; } Word CloneArray( const ListExpr typeInfo, const Word& w ) { //cerr << "*CloneArray" << endl; Array* array = (Array*)w.addr; Array* newarray; bool ok = true; int n = array->getSize(); int algebraId = array->getElemAlgId(); int typeId = array->getElemTypeId(); Word* a = new Word[array->getSize()]; for (int i=0; i < n; i++) { a[i] = (am->CloneObj(algebraId, typeId))( nl->TheEmptyList(), array->getElement(i) ); // Check whether cloning was successful ok = ok && (a[i].addr != 0); } if (ok) { newarray = new Array(algebraId, typeId, n, a); } else { // If the element's type just provides a dummy clone function, the clone // function of the array is also a dummy function. newarray = 0; } delete[] a; return SetWord(newarray); } int SizeOfArray() { return sizeof(Array); } /* 2.7 Function Describing the Signature of the Type Constructor The type of the elements of the array may be described by any valid type constructor, but the elements must have an internal list representation. */ static ListExpr ArrayProperty() { ListExpr remarkslist = nl->TextAtom(); nl->AppendText(remarkslist, "The elements of the array must have an " "internal list representation."); return (nl->TwoElemList( nl->FiveElemList( nl->StringAtom("Signature"), nl->StringAtom("Example Type List"), nl->StringAtom("List Rep"), nl->StringAtom("Example List"), nl->StringAtom("Remarks")), nl->FiveElemList( nl->StringAtom("typeconstructor -> ARRAY"), nl->StringAtom("(array int)"), nl->StringAtom("(a1 a2 ... an)"), nl->StringAtom("(0 1 2 3)"), remarkslist))); } /* 2.8 Kind Checking Function The type constructor of an array is a list (array type). The first element of that list is the symbol atom Array::BasicType() and the second element has to be a valid type constructor for the elements of the array. So the second element can be an atom (e.g. int) or - in case of a more complex type - a nested list itself. In order to achieve great flexibility, the element's type is not restricted to the tested types (see introduction). The user of an array has to make sure that the elements have an internal list representation, because this is not checked here. */ static bool CheckArray( ListExpr type, ListExpr& errorInfo ) { if (nl->ListLength(type) == 2) { ListExpr First = nl->First(type); ListExpr Second = nl->Second(type); if (nl->IsEqual(First, Array::BasicType())) { // Check whether Second is a valid type constructor SecondoCatalog* sc = SecondoSystem::GetCatalog(); if (sc->KindCorrect(Second, errorInfo)) { return true; } } } return false; } /* 2.9 Creation of the Type Constructor Instance Here an object of the class TypeConstructor is created. The constructor for an instance of the class TypeConstructor is called with the properties and functions for the array as parameters. The name of the type constructor is ~array~. */ TypeConstructor array ( Array::BasicType(), ArrayProperty, OutArray, InArray, SaveToListArray, RestoreFromListArray, CreateArray, DeleteArray, OpenArray, SaveArray, CloseArray, CloneArray, DummyCast, SizeOfArray, CheckArray ); /* 4 Operators 4.1 Operator ~size~ The operator ~size~ returns the number of elements of an array. The type mapping is: ---- ((array t)) -> int ---- */ static ListExpr sizeTypeMap( ListExpr args ) { if (nl->ListLength(args) == 1) { ListExpr arg1 = nl->First(args); if (!nl->IsAtom(arg1) && nl->IsEqual(nl->First(arg1), Array::BasicType())) { return nl->SymbolAtom(CcInt::BasicType()); } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int sizeFun ( Word* args, Word& result, int message, Word& local, Supplier s ) { Array* array = ((Array*)args[0].addr); result = qp->ResultStorage(s); ((CcInt*)result.addr)->Set(true, array->getSize()); return 0; } const string sizeSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t)) -> int" "size ( _ )" "Returns the size of an array." "query size(ai) ))"; Operator size ( "size", sizeSpec, sizeFun, Operator::SimpleSelect, sizeTypeMap ); /* 4.2 Operator ~get~ The operator ~get~ returns an element at a given index. So the result type of the operator is the type of the array's elements. The type mapping is: ---- ((array t) int) -> t ---- Precondition of the value mapping function is a valid index. This means an index between 0 and the size of the array minus 1. An element is cloned before returning. */ static ListExpr getTypeMap( ListExpr args ) { if (nl->ListLength(args) == 2) { ListExpr arg1 = nl->First(args); ListExpr arg2 = nl->Second(args); if (!nl->IsAtom(arg1) && nl->IsEqual(nl->First(arg1), Array::BasicType()) && nl->IsEqual(arg2, CcInt::BasicType())) { // The second item of arg1 is the type of the array's elements. ListExpr resultType = nl->Second(arg1); return resultType; } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int getFun ( Word* args, Word& result, int message, Word& local, Supplier s ) { Array* array = ((Array*)args[0].addr); CcInt* index = ((CcInt*)args[1].addr); int i = index->GetIntval(); if (i<0 || i >= array->getSize()) { // error handling cout << "*** Error in Operator get: " << endl; cout << "Index " << i << " out of range [0;" << array->getSize() - 1 << "], "; cout << "first element will be returned." << endl; i = 0; } if (i>=0 && i < array->getSize()) { // should always be true SecondoCatalog* sc = SecondoSystem::GetCatalog(); Word element = array->getElement(i); Word clonedElement; ListExpr resultType = qp->GetType(s); if (nl->ListLength(resultType) > 1) { if (nl->IsEqual(nl->First(resultType), Symbol::MAP())) { // In case of a mapping only the type of the resulting object of // the mapping is relevant. while (nl->ListLength(resultType) > 1) { resultType = nl->Rest(resultType); } resultType = nl->First(resultType); } } resultType = sc->NumericType(resultType); /* // test to avoid clone the argument clonedElement = Array::genericClone(algebraId, typeId, resultType, element); // // In the next statement the (by the Query Processor) provided place for // the result is not used in order to be flexible with regard to the // result type. result.addr = clonedElement.addr; */ result.addr = element.addr; return 0; } else { return 1; } } const string getSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) int) -> t" "get ( _, _ )" "Returns an element with a given index." "query get(ai,3) ))"; Operator get ( "get", getSpec, getFun, Operator::SimpleSelect, getTypeMap ); /* 4.3 Operator ~put~ The operator ~put~ assigns a value to an element at a given index of an array. The type mapping is: ---- ((array t) t int) -> (array t) ---- Precondition of the value mapping function is a valid index. This means an index between 0 and the size of the array minus 1. The function returns a new array. */ static ListExpr putTypeMap( ListExpr args ) { if (nl->ListLength(args) == 3) { ListExpr arg1 = nl->First(args); ListExpr arg2 = nl->Second(args); ListExpr arg3 = nl->Third(args); if (!nl->IsAtom(arg1) && nl->IsEqual(nl->First(arg1), Array::BasicType()) && nl->Equal(nl->Second(arg1), arg2) && nl->IsEqual(arg3, CcInt::BasicType())) { return arg1; } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int putFun ( Word* args, Word& result, int message, Word& local, Supplier s ) { Array* array = ((Array*)args[0].addr); Word newelement = args[1]; int i = ((CcInt*)args[2].addr)->GetIntval(); if (i<0 || i >= array->getSize()) { // error handling cout << "*** Error in Operator put: " << endl; cout << "Index " << i << " out of range [0;" << array->getSize() - 1 << "], "; cout << "first element will be replaced." << endl; i = 0; } if (i>=0 && i < array->getSize()) { // should always be true SecondoCatalog* sc = SecondoSystem::GetCatalog(); int n = array->getSize(); int algebraId = array->getElemAlgId(); int typeId = array->getElemTypeId(); Word* a = new Word[array->getSize()]; Word element; ListExpr resultType = qp->GetType(s); if (nl->ListLength(resultType) > 1) { if (nl->IsEqual(nl->First(resultType), Symbol::MAP())) { // In case of a mapping only the type of the resulting object of // the mapping is relevant. while (nl->ListLength(resultType) > 1) { resultType = nl->Rest(resultType); } resultType = nl->First(resultType); } } resultType = sc->NumericType(resultType); ListExpr typeOfElement = sc->NumericType(nl->Second(resultType)); for (int l=0; l < n; l++) { element = (l!=i) ? array->getElement(l) : newelement; a[l] = Array::genericClone(algebraId, typeId, typeOfElement, element); } result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } else { return 1; } } const string putSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) t int) -> (array t)" "put ( _, _, _ )" "Replaces an element at a given index." "query put(ai,9,3) ))"; Operator put ( "put", putSpec, putFun, Operator::SimpleSelect, putTypeMap ); /* 4.4 Operator ~makearray~ This operator creates an array containing the elements of a given list. Note that all elements must have the same type. The elements are cloned before creating the array. The type mapping is: ---- (t t ... t) -> (array t) ---- */ static ListExpr makearrayTypeMap( ListExpr args ) { bool sameType = true; if (nl->ListLength(args) > 0) { ListExpr typeOfElement = nl->First(args); args = nl->Rest(args); while (!nl->IsEmpty(args) && sameType) { sameType = nl->Equal(nl->First(args), typeOfElement); args = nl->Rest(args); } if (sameType) { return nl->TwoElemList(nl->SymbolAtom(Array::BasicType()), typeOfElement); } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int makearrayFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = qp->GetNoSons(s); Word* a = new Word[n]; for (int i=0; iResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string makearraySpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( (t t ... t) -> (array t)" "makearray ( list )" "Creates an array containing the elements of a given list. " "The elements are cloned before creating the array." "let ai = makearray (0,1,2,3) ))"; Operator makearray( "makearray", makearraySpec, makearrayFun, Operator::SimpleSelect, makearrayTypeMap ); /* 4.5 Operator ~sortarray~ The operator ~sortarray~ arranges the elements of an array according to their integer values of a given function. The formal specification of type mapping is: ---- ((array t) (map t int)) -> (array t) ---- First an auxiliary class ~IntPair~ is defined. The aim of this class is to use the standard sort algorithm in the value mapping function. */ class IntPair { friend bool operator<(const IntPair&, const IntPair&); public : int first, second; }; bool operator<(const IntPair& p1, const IntPair& p2) { return (p1.first < p2.first) || (p1.first == p2.first && p1.second < p2.second); } /* Implementation of operator ~sortarray~. */ static ListExpr sortarrayTypeMap( ListExpr args ) { if (nl->ListLength(args) == 2) { ListExpr arrayDesc = nl->First(args); ListExpr mapDesc = nl->Second(args); if ((nl->ListLength(arrayDesc) == 2) && (nl->ListLength(mapDesc) == 3)) { if (nl->IsEqual(nl->First(arrayDesc), Array::BasicType()) && nl->IsEqual(nl->First(mapDesc), Symbol::MAP())) { if (nl->Equal(nl->Second(arrayDesc), nl->Second(mapDesc)) && nl->IsEqual(nl->Third(mapDesc), CcInt::BasicType())) { return arrayDesc; } } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int sortarrayFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* array = ((Array*)args[0].addr); ArgVectorPointer funargs = qp->Argument(args[1].addr); Word funresult; ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = array->getSize(); vector index(n); Word* a = new Word[n]; // Calculate and assing function values for (int i=0; igetElement(i); qp->Request(args[1].addr, funresult); index[i].first = ((CcInt*)funresult.addr)->GetIntval(); index[i].second = i; } // Sort index-vector according to function values sort(index.begin(), index.end()); // Create resulting array for (int i=0; igetElement(index[i].second)); } result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string sortarraySpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (map t int)) -> (array t)" "_ sortarray [ fun ]" "Sorts an array in order of the function values of the " "elements." "query ai sortarray[fun(i:int)i] ))"; Operator sortarray( "sortarray", sortarraySpec, sortarrayFun, Operator::SimpleSelect, sortarrayTypeMap ); /* 4.6 Operator ~tie~ The operator calculates a single "value" of an array by evaluating the elements of an array with a given function from left to right, e.g. tie ( (a1, a2, ... , an), + ) = a1 + a2 + ... + an The formal specification of type mapping is: ---- ((array t) (map t t t)) -> t ---- */ static ListExpr tieTypeMap( ListExpr args ) { if (nl->ListLength(args) == 2) { ListExpr arrayDesc = nl->First(args); ListExpr mapDesc = nl->Second(args); if ((nl->ListLength(arrayDesc) == 2) && (nl->ListLength(mapDesc) == 4)) { if (nl->IsEqual(nl->First(arrayDesc), Array::BasicType()) && nl->IsEqual(nl->First(mapDesc), Symbol::MAP())) { ListExpr elementDesc = nl->Second(arrayDesc); if (nl->Equal(elementDesc, nl->Second(mapDesc)) && nl->Equal(elementDesc, nl->Third(mapDesc)) && nl->Equal(elementDesc, nl->Fourth(mapDesc))) { return elementDesc; } } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int tieFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* array = ((Array*)args[0].addr); ArgVectorPointer funargs = qp->Argument(args[1].addr); Word funresult; ListExpr typeOfElement = sc->NumericType(qp->GetType(s)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = array->getSize(); Word partResult; partResult = array->getElement(0); for (int i=1; igetElement(i); qp->Request(args[1].addr, funresult); if (funresult.addr != partResult.addr) { if (i>1) { (am->DeleteObj(algebraId, typeId))(typeOfElement, partResult); } partResult = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } } // In the next statement the (by the Query Processor) provided place for // the result is not used in order to be flexible with regard to the // result type. result.addr = partResult.addr; return 0; } const string tieSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (map t t t)) -> t" "_ tie [ fun ]" "Calculates the \"value\" of an array evaluating the elements of " "the array with a given function from left to right." "query ai tie[fun(i:int,l:int)(i+l)] ))"; Operator tie( "tie", tieSpec, tieFun, Operator::SimpleSelect, tieTypeMap ); /* 4.7 Operator ~cumulate~ This operator ["]cumulates["] the values of the array under a given function. The i-th element of the resulting array is the concatenation from the first to the i-th element of the input array under a given function evaluated from left to right (compare operator ~tie~), e.g. cumulate ( (a1, a2, ... , an), + ) = (a1, a1 + a2, ... , a1 + a2 + ... + an) The formal specification of type mapping is: ---- ((array t) (map t t t)) -> (array t) ---- */ static ListExpr cumulateTypeMap( ListExpr args ) { if (nl->ListLength(args) == 2) { ListExpr arrayDesc = nl->First(args); ListExpr mapDesc = nl->Second(args); if ((nl->ListLength(arrayDesc) == 2) && (nl->ListLength(mapDesc) == 4)) { if (nl->IsEqual(nl->First(arrayDesc), Array::BasicType()) && nl->IsEqual(nl->First(mapDesc), Symbol::MAP())) { ListExpr elementDesc = nl->Second(arrayDesc); if (nl->Equal(elementDesc, nl->Second(mapDesc)) && nl->Equal(elementDesc, nl->Third(mapDesc)) && nl->Equal(elementDesc, nl->Fourth(mapDesc))) { return arrayDesc; } } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int cumulateFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* array = ((Array*)args[0].addr); ArgVectorPointer funargs = qp->Argument(args[1].addr); Word funresult; ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = array->getSize(); Word* a = new Word[n]; Word cumResult; cumResult = array->getElement(0); for (int i=0; i=1) { (*funargs)[0] = cumResult; (*funargs)[1] = array->getElement(i); qp->Request(args[1].addr, funresult); if (funresult.addr != cumResult.addr) { //(am->DeleteObj(algebraId, typeId))(typeOfElement, cumResult); // SPM: will be deleted by the QP's destroy function. cumResult = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } } a[i] = Array::genericClone(algebraId, typeId, typeOfElement, cumResult); } result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string cumulateSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (map t t t)) -> (array t)" "_ cumulate [ fun ]" "Cumulates the values of an array under a given " "function." "query ai cumulate[fun(i:int,l:int)(i+l)] ))"; Operator cumulate ( "cumulate", cumulateSpec, cumulateFun, Operator::SimpleSelect, cumulateTypeMap ); /* 4.8 Operator ~distribute~ The operator ~distribute~ creates an array of relations from a stream of tuples. The index of the appropriate relation has to be given by an integer attribute of the tuple. This integer attribute is removed from the tuples in the resulting relations. So the incoming tuples have to consist of at least two attributes. The formal specification of type mapping is: ---- ((stream (tuple ((x1 t1) ... (xn tn)))) xi) -> (array (rel (tuple ((x1 t1) ... (xi-1 ti-1) (xi+1 ti+1) ... (xn tn))))) at which n>=2, 1<=i<=n and ti (the type of xi) = int ---- The index of the attribute ai is appended to the result type by the type mapping function, because this information is needed by the value mapping function. Within the value mapping function, an integer constant defines the maximum number of relations in the resulting array. Tuples with an index smaller than 0 or an index greater than the maximum number of relations are distributed into the first respectively the last relation. */ static ListExpr distributeTypeMap( ListExpr inArgs ) { NList args(inArgs); if ( args.length() == 2 ) { NList streamDesc = args.first(); ListExpr attrNameLE = args.second().listExpr(); if ( streamDesc.isList() && streamDesc.first().isSymbol(Symbol::STREAM()) && (streamDesc.length() == 2) && (nl->AtomType(attrNameLE) == SymbolType) ) { ListExpr tupleDesc = streamDesc.second().listExpr(); string attrName = nl->SymbolValue(attrNameLE); if(!Tuple::checkType(tupleDesc)){ return nl->TypeError(); } if (nl->IsEqual(nl->First(tupleDesc), Tuple::BasicType()) && (nl->ListLength(tupleDesc) == 2)) { ListExpr attrList = nl->Second(tupleDesc); if (IsTupleDescription(attrList)) { int attrIndex; ListExpr attrType; attrIndex = FindAttribute(attrList, attrName, attrType); if (nl->ListLength(attrList) > 1 && attrIndex > 0 && nl->IsEqual(attrType, CcInt::BasicType())) { ListExpr attrList2 = nl->TheEmptyList(); ListExpr last = nl->TheEmptyList(); while (!nl->IsEmpty(attrList)) { ListExpr attr = nl->First(attrList); if (nl->SymbolValue(nl->First(attr)) != attrName) { if (nl->IsEmpty(attrList2)) { attrList2 = nl->OneElemList(attr); last = attrList2; } else { last = nl->Append(last, attr); } } attrList = nl->Rest(attrList); } return nl->ThreeElemList( nl->SymbolAtom(Symbol::APPEND()), nl->OneElemList(nl->IntAtom(attrIndex)), nl->TwoElemList( nl->SymbolAtom(Array::BasicType()), nl->TwoElemList( nl->SymbolAtom(Relation::BasicType()), nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()), attrList2)))); } } } } } return args.typeError("input is not (stream(tuple(y))) x ..."); } /* 17.08.04 M. Spiekermann The operating system has an limitation of simultaneously opend files per process (Linux 1024). Currently each created relation will open two SMI-Files. Creating an array containing 512 relations will break these limits. However, this limitation can only be removed with a redesign of the relation class and/or SmiFile. It should be changed to allow multiple relations stored in one file. */ static int distributeFun (Word* args, Word& result, int message, Word& local, Supplier s) { const int MAX_OPEN_RELATIONS = 400; // not the absolute possible maximum SecondoCatalog* sc = SecondoSystem::GetCatalog(); CcInt* indexAttrCcInt = (CcInt*)args[2].addr; int pkgAttr = (indexAttrCcInt->GetIntval()) - 1; vector relPkg; ListExpr relType = nl->Second(qp->GetType(s)); relType = sc->NumericType(relType); ListExpr tupleType = nl->Second(relType); int n = 0; relPkg.push_back( new Relation(relType) ); CcInt* pkgNrCcInt = 0; int pkgNr = 0; int outOfRangePkgNr = 0; Word actual = SetWord( Address(0) ); qp->Open(args[0].addr); qp->Request(args[0].addr, actual); bool msgPrinted = false; while(qp->Received(args[0].addr)) { Tuple* tuple = (Tuple*)actual.addr; Tuple* tuple2 = new Tuple(tupleType); // Copy all attributes except the package number from tuple to tuple2. int j = 0; for (int i=0; iGetNoAttributes(); i++) { if (i!=pkgAttr) tuple2->CopyAttribute(i, tuple, j++); } // Determine package and distribute tuple2 into that package. pkgNrCcInt = (CcInt*)(tuple->GetAttribute(pkgAttr)); pkgNr = pkgNrCcInt->GetIntval(); tuple->DeleteIfAllowed(); if ( pkgNr > (MAX_OPEN_RELATIONS - 1) ) { // check if pckNr is valid if ( !msgPrinted ) { cerr << "Warning: Package number out of Range. " << "Open files per process are limited! " << "Since every open relation needs to open files " << "it is only possible to create at most an array " << "with " << MAX_OPEN_RELATIONS << " relations." << endl; msgPrinted = true; } pkgNr = outOfRangePkgNr % MAX_OPEN_RELATIONS; outOfRangePkgNr++; } while ( n < pkgNr ) { // enlarge the array if necessary relPkg.push_back( new Relation(relType) ); n++; } relPkg[pkgNr]->AppendTuple(tuple2); tuple2->DeleteIfAllowed(); // free memory qp->Request(args[0].addr, actual); } result = qp->ResultStorage(s); Word* a = new Word[++n]; for (int i=0; iGetTypeId(Relation::BasicType(), algebraId, typeId)) { ((Array*)result.addr)->initialize(algebraId, typeId, n, a); qp->Close(args[0].addr); delete[] a; return 0; } else { // should never happen assert(false); delete[] a; return 1; } } const string distributeSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((stream (tuple ((x1 t1) ... (xn tn)))) xi) -> " "(array (rel (tuple ((x1 t1) ... (xi-1 ti-1) (xi+1 ti+1) ... " "(xn tn)))))" "_ distribute [ _ ]" "Distributes a stream of tuples into an array or relations. The " "attribute xi determines the index of the relation, therefore ti must " "be int." "let prel = plz feed distribute [pkg] ))"; Operator distribute ( "distribute", distributeSpec, distributeFun, Operator::SimpleSelect, distributeTypeMap ); /* 4.9 Operator ~summarize~ The operator ~summarize~ provides a stream of tuples from an array of relations. For this purpose, the operator scans all relations beginning with the first relation of the array. The formal specification of type mapping is: ---- ((array (rel t))) -> (stream t) at which t is of the type tuple ---- Note that the operator ~summarize~ is not exactly inverse to the operator ~distribute~ because the index of the relation is not appended to the attributes of the outgoing tuples. If the array has been constructed by the operator ~distribute~ the order of the resulting stream in most cases does not correspond to the order of the input stream of the operator ~distribute~. */ static ListExpr summarizeTypeMap( ListExpr args ) { if (nl->ListLength(args) == 1) { ListExpr arrayDesc = nl->First(args); if (nl->ListLength(arrayDesc) == 2 && nl->IsEqual(nl->First(arrayDesc), Array::BasicType())) { ListExpr relDesc = nl->Second(arrayDesc); if (nl->ListLength(relDesc) == 2 && nl->IsEqual(nl->First(relDesc), Relation::BasicType())) { ListExpr tupleDesc = nl->Second(relDesc); if (nl->IsEqual(nl->First(tupleDesc), Tuple::BasicType())) { return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()), nl->Second(relDesc)); } } } } ErrorReporter::ReportError( "summarize: Input type array( rel( tuple(...))) expected!"); return nl->SymbolAtom(Symbol::TYPEERROR()); } static int summarizeFun( Word* args, Word& result, int message, Word& local, Supplier s ) { struct ArrayIterator { private: int current; Array* array; GenericRelationIterator* rit; // create an Tuple iterater for the next array element bool makeNextRelIter() { if (rit) delete rit; while (current < array->getSize()) { Relation* r=static_cast(array->getElement(current).addr); if (r->GetNoTuples() > 0) { current++; rit = r->MakeScan(); return true; } else { current++; continue; } } rit=0; return false; } public: ArrayIterator(Array* a, const int pos=0) : current(pos), array(a), rit(0) { makeNextRelIter(); } ~ArrayIterator() { if (rit) delete rit; } Tuple* getNextTuple() // try to get next tuple { if (!rit) return 0; Tuple* t = rit->GetNextTuple(); if ( !t ) { if (!makeNextRelIter()) return 0; else return rit->GetNextTuple(); } return t; } }; ArrayIterator* ait = 0; ait = (ArrayIterator*)local.addr; switch (message) { case OPEN : { ait = new ArrayIterator( (Array*)args[0].addr ); local.addr = ait; return 0; } case REQUEST : { Tuple* t = ait->getNextTuple(); if (t != 0) { result = SetWord(t); return YIELD; } return CANCEL; } case CLOSE : { if(local.addr) { ait = (ArrayIterator*)local.addr; delete ait; local = SetWord(Address(0)); } return 0; } default : { return 0; } } return 0; } const string summarizeSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array (rel t))) -> (stream t)" "_ summarize" "Produces a stream of the tuples from all relations in the " "array." "query prel summarize consume ))"; Operator summarize ( "summarize", summarizeSpec, summarizeFun, Operator::SimpleSelect, summarizeTypeMap ); /* 4.10 Operator ~loop~ The Operator ~loop~ evaluates each element of an array with a given function and returns an array which contains the resulting values. The formal specification of type mapping is: ---- ((array t) (map t r)) -> (array r) ---- */ static ListExpr loopTypeMap( ListExpr args ) { if (nl->ListLength(args) == 2) { ListExpr arrayDesc = nl->First(args); ListExpr mapDesc = nl->Second(args); if ((nl->ListLength(arrayDesc) == 2) && (nl->ListLength(mapDesc) == 3)) { if (nl->IsEqual(nl->First(arrayDesc), Array::BasicType()) && nl->IsEqual(nl->First(mapDesc), Symbol::MAP()) && !nl->IsEqual(nl->Third(mapDesc), Symbol::TYPEERROR())) { if (nl->Equal(nl->Second(arrayDesc), nl->Second(mapDesc))) { return nl->TwoElemList(nl->SymbolAtom(Array::BasicType()), nl->Third(mapDesc)); } } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int loopFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* array = ((Array*)args[0].addr); FunInfo f(0, "0", args[1].addr); Word funresult; ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = array->getSize(); Word* a = new Word[n]; string info; bool trace = false; if (trace) cout << "Processing ..." << endl; for (int i=0; igetElement(i), funresult, info); a[i] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string loopSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (map t r)) -> (array r)" "_ loop [ fun ]" "Evaluates each element of an array with a given function and " "returns an array which contains the resulting values." "query ai loop [fun(i:int)(i*i)] ))"; Operator loop ( "loop", loopSpec, loopFun, Operator::SimpleSelect, loopTypeMap ); /* 4.11 Operator ~loopa~ The operator ~loopa~ evaluates each pair of elements *with the same index* from two arrays with a given function and returns an array which contains the resulting values. If the two arrays do not have the same size, the remaining elements of the greater array are ignored. The formal specification of type mapping is: ---- ((array t) (array u) (map t u r)) -> (array r) ---- */ static ListExpr loopaTypeMap( ListExpr args ) { if (nl->ListLength(args) == 3) { ListExpr firstArrayDesc = nl->First(args); ListExpr secondArrayDesc = nl->Second(args); ListExpr mapDesc = nl->Third(args); if ((nl->ListLength(firstArrayDesc) == 2) && (nl->ListLength(secondArrayDesc) == 2) && (nl->ListLength(mapDesc) == 4)) { if (nl->IsEqual(nl->First(firstArrayDesc), Array::BasicType()) && nl->IsEqual(nl->First(secondArrayDesc), Array::BasicType()) && nl->IsEqual(nl->First(mapDesc), Symbol::MAP()) && !nl->IsEqual(nl->Fourth(mapDesc), Symbol::TYPEERROR())) { if (nl->Equal(nl->Second(firstArrayDesc), nl->Second(mapDesc)) && nl->Equal(nl->Second(secondArrayDesc), nl->Third(mapDesc))) { return nl->TwoElemList(nl->SymbolAtom(Array::BasicType()), nl->Fourth(mapDesc)); } } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int loopaFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* firstArray = ((Array*)args[0].addr); Array* secondArray = ((Array*)args[1].addr); FunInfo f(0, "0", args[2].addr); Word funresult; ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = min(firstArray->getSize(), secondArray->getSize()); Word* a = new Word[n]; string info; cout << "Processing elements ..." << endl; for (int i=0; igetElement(i), secondArray->getElement(i), funresult, info); a[i] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string loopaSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (array u) (map t u r)) -> (array r)" "_ _ loopa [ fun ]" "Evaluates each pair of elements with the same index from " "two arrays with a given function and returns an array which contains " "the resulting values." "query ai al loopa[fun(i:int,l:int)(i+l)] ))"; Operator loopa ( "loopa", loopaSpec, loopaFun, Operator::SimpleSelect, loopaTypeMap ); /* 4.12 Operator ~loopb~ The operator ~loopb~ evaluates each pair of elements from two arrays with a given function and returns an array which contains the resulting values. The formal specification of type mapping is: ---- ((array t) (array u) (map t u r)) -> (array r) ---- Therefore the type mapping function of the operator ~loopa~ can also be used for the operator ~loopb~. */ static int loopbFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* firstArray = ((Array*)args[0].addr); Array* secondArray = ((Array*)args[1].addr); FunInfo f(0, "0", args[2].addr); Word funresult; ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = firstArray->getSize(); int m = secondArray->getSize(); Word* a = new Word[n * m]; string info; cout << "Processing elements ..." << endl; for (int i=0; igetElement(i), secondArray->getElement(l), funresult, info); a[i * m + l] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } } result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n * m, a); delete[] a; return 0; } const string loopbSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (array u) (map t u r)) -> (array r)" "_ _ loopb [ fun ]" "Evaluates each pair of elements from two arrays with a given " "function and returns an array which contains the resulting " "values." "query ai al loopb[fun(i:int,l:int)(i+l)] ))"; Operator loopb ( "loopb", loopbSpec, loopbFun, Operator::SimpleSelect, loopaTypeMap ); /* 4.13 Operator ~loopswitch~ The operator ~loopswitch~ evaluates each element of an array with one of the given functions using the switch algorithm. So, under certain conditions, all functions may get (approximately) the same time for calculation and the faster functions will process more elements of the array. The formal specification of type mapping is: ---- ((array t) ((name1 (map t r)) ... (namen (map t r)))) -> (array r) ---- */ static ListExpr loopswitchTypeMap( ListExpr args ) { if (nl->ListLength(args) == 2) { ListExpr arrayDesc = nl->First(args); ListExpr funlist = nl->Second(args); if(nl->IsAtom(funlist)){ return nl->SymbolAtom(Symbol::TYPEERROR()); } if(nl->IsEmpty(funlist)){ return nl->SymbolAtom(Symbol::TYPEERROR()); } if ((nl->ListLength(arrayDesc) == 2) && (nl->IsEqual(nl->First(arrayDesc), Array::BasicType()))) { ListExpr firstFunDesc = nl->First(funlist); funlist = nl->Rest(funlist); ListExpr funNames = nl->TheEmptyList(); ListExpr last; ListExpr firstMapDesc; if (nl->ListLength(firstFunDesc) == 2) { ListExpr firstFunName = nl->First(firstFunDesc); firstMapDesc = nl->Second(firstFunDesc); if ((nl->AtomType(firstFunName) == SymbolType) && (nl->ListLength(firstMapDesc) == 3)) { if ((nl->IsEqual(nl->First(firstMapDesc), Symbol::MAP())) && (!nl->IsEqual(nl->Third(firstMapDesc), Symbol::TYPEERROR())) && (nl->Equal(nl->Second(firstMapDesc),nl->Second(arrayDesc)))) { funNames = nl->OneElemList( nl->StringAtom(nl->SymbolValue(firstFunName))); last = funNames; } } } if (!nl->IsEmpty(funNames)) { bool ok = true; while (!nl->IsEmpty(funlist) && ok) { ListExpr funDesc = nl->First(funlist); if (nl->ListLength(funDesc) == 2) { ListExpr funName = nl->First(funDesc); ListExpr mapDesc = nl->Second(funDesc); if ((nl->AtomType(funName) == SymbolType) && (nl->Equal(mapDesc, firstMapDesc))) { last = nl->Append(last, nl->StringAtom(nl->SymbolValue(funName))); } else { ok = false; } } else { ok = false; } funlist = nl->Rest(funlist); } if (ok) { return nl->ThreeElemList( nl->SymbolAtom(Symbol::APPEND()), funNames, nl->TwoElemList( nl->SymbolAtom(Array::BasicType()), nl->Third(firstMapDesc))); } } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int loopswitchFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* array = (Array*)args[0].addr; SwitchAlgorithm sw; sw.load(args[1], &args[2]); ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = array->getSize(); Word* a = new Word[n]; Word funresult; string info; cout << "Processing ..." << endl; for (int i=0; igetElement(i), funresult, info); a[i] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } sw.writeSummary(); result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string loopswitchSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) ((name1 (map t r)) ... (namen (map t r)))) " "-> (array r)" "_ loopswitch [ funlist ]" "Evaluates each element of an array with one of the given " "functions. All functions may get (approximately) the same time for " "calculation, so that the \"faster\" function will process more " "elements of the array." "query ai loopswitch[f:fun(i:int)(i*2), " "g:fun(l:int)(l+l)] ))"; Operator loopswitch ( "loopswitch", loopswitchSpec, loopswitchFun, Operator::SimpleSelect, loopswitchTypeMap ); /* 4.14 Operator ~loopswitcha~ This operator works like operator ~loopa~ extended by the switch algorithm of operator ~loopswitch~. The formal specification of type mapping is: ---- ((array t) (array u) ((name1 (map t u r)) ... (namen (map t u r)))) -> (array r) ---- */ static ListExpr loopswitchaTypeMap( ListExpr args ) { if (nl->ListLength(args) == 3) { ListExpr firstArrayDesc = nl->First(args); ListExpr secondArrayDesc = nl->Second(args); ListExpr funlist = nl->Third(args); if ((nl->ListLength(firstArrayDesc) == 2) && (nl->IsEqual(nl->First(firstArrayDesc), Array::BasicType())) && (nl->ListLength(secondArrayDesc) == 2) && (nl->IsEqual(nl->First(secondArrayDesc), Array::BasicType()))) { ListExpr firstFunDesc = nl->First(funlist); funlist = nl->Rest(funlist); ListExpr funNames = nl->TheEmptyList(); ListExpr last; ListExpr firstMapDesc; if (nl->ListLength(firstFunDesc) == 2) { ListExpr firstFunName = nl->First(firstFunDesc); firstMapDesc = nl->Second(firstFunDesc); if ((nl->AtomType(firstFunName) == SymbolType) && (nl->ListLength(firstMapDesc) == 4)) { if ((nl->IsEqual(nl->First(firstMapDesc), Symbol::MAP())) && (!nl->IsEqual(nl->Fourth(firstMapDesc), Symbol::TYPEERROR())) && (nl->Equal(nl->Second(firstMapDesc), nl->Second(firstArrayDesc))) && (nl->Equal(nl->Third(firstMapDesc), nl->Second(secondArrayDesc)))) { funNames = nl->OneElemList( nl->StringAtom(nl->SymbolValue(firstFunName))); last = funNames; } } } if (!nl->IsEmpty(funNames)) { bool ok = true; while (!nl->IsEmpty(funlist) && ok) { ListExpr funDesc = nl->First(funlist); if (nl->ListLength(funDesc) == 2) { ListExpr funName = nl->First(funDesc); ListExpr mapDesc = nl->Second(funDesc); if ((nl->AtomType(funName) == SymbolType) && (nl->Equal(mapDesc, firstMapDesc))) { last = nl->Append(last, nl->StringAtom(nl->SymbolValue(funName))); } else { ok = false; } } else { ok = false; } funlist = nl->Rest(funlist); } if (ok) { return nl->ThreeElemList( nl->SymbolAtom(Symbol::APPEND()), funNames, nl->TwoElemList( nl->SymbolAtom(Array::BasicType()), nl->Fourth(firstMapDesc))); } } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int loopswitchaFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* firstArray = (Array*)args[0].addr; Array* secondArray = (Array*)args[1].addr; SwitchAlgorithm sw; sw.load(args[2], &args[3]); ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = min(firstArray->getSize(), secondArray->getSize()); Word* a = new Word[n]; Word funresult; string info; cout << "Processing elements ..." << endl; for (int i=0; igetElement(i), secondArray->getElement(i), funresult, info); a[i] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } sw.writeSummary(); result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string loopswitchaSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (array u) ((name1 (map t u r)) ... " "(namen (map t u r)))) -> (array r)" "_ loopswitcha [ funlist ]" "Works like operator loopa extended by the switch algorithm of " "operator loopswitch." "query ai al loopswitcha[f:fun(i1:int,l1:int)(i1 mod l1), " "g:fun(i2:int,l2:int)(i2-(i2 div l2)*l2)] ))"; Operator loopswitcha ( "loopswitcha", loopswitchaSpec, loopswitchaFun, Operator::SimpleSelect, loopswitchaTypeMap ); /* 4.15 Operator ~loopswitchb~ This operator works like operator ~loopb~ extended by the switch algorithm of operator ~loopswitch~. The formal specification of type mapping is: ---- ((array t) (array u) ((name1 (map t u r)) ... (namen (map t u r)))) -> (array r) ---- Therefore the type mapping function of the operator ~loopswitcha~ can also be used for the operator ~loopswitchb~. */ static int loopswitchbFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* firstArray = (Array*)args[0].addr; Array* secondArray = (Array*)args[1].addr; SwitchAlgorithm sw; sw.load(args[2], &args[3]); ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = firstArray->getSize(); int m = secondArray->getSize(); Word* a = new Word[n * m]; Word funresult; string info; cout << "Processing elements ..." << endl; for (int i=0; igetElement(i), secondArray->getElement(l), funresult, info); a[i * m + l] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } } sw.writeSummary(); result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n * m, a); delete[] a; return 0; } const string loopswitchbSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (array u) ((name1 (map t u r)) ... " "(namen (map t u r)))) -> (array r)" "_ loopswitchb [ funlist ]" "Works like operator loopb extended by the switch algorithm of " "operator loopswitch." "query ai al loopswitchb[f:fun(i1:int,l1:int)(i1 mod l1), " "g:fun(i2:int,l2:int)(i2-(i2 div l2)*l2)] ))"; Operator loopswitchb ( "loopswitchb", loopswitchbSpec, loopswitchbFun, Operator::SimpleSelect, loopswitchaTypeMap ); /* 4.16 Operator ~loopselect~ The operator ~loopselect~ evaluates the first n elements of the array with each of the given functions and sums up the used calculation times for each function. The remaining elements are processed with the (so far) fastest function. The formal specification of type mapping is: ---- ((array t) ((name1 (map t r)) ... (namen (map t r))) int real) -> (array r) ---- The above mentioned parameter n is given by the int- and the real-parameter. Let x the int- and y the real-parameter, than n is calculated similar to the operator ~sample~ of the Relation Algebra: n := min(arraySize, max(x, y * arraySize)) */ static ListExpr loopselectTypeMap( ListExpr args ) { if (nl->ListLength(args) == 4) { ListExpr arrayDesc = nl->First(args); ListExpr funlist = nl->Second(args); if (nl->IsEqual(nl->Third(args), CcInt::BasicType()) && nl->IsEqual(nl->Fourth(args), CcReal::BasicType())) { return loopswitchTypeMap(nl->TwoElemList(arrayDesc, funlist)); } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int loopselectFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* array = ((Array*)args[0].addr); CcInt* absTestCcInt = ((CcInt*)args[2].addr); int absTest = absTestCcInt->GetIntval(); CcReal* relTestCcReal = ((CcReal*)args[3].addr); double relTest = relTestCcReal->GetRealval(); SelectAlgorithm se; se.load(args[1], &args[4]); ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = array->getSize(); if (absTest < 1) { absTest = 1; } if (absTest > n) { absTest = n; } if (relTest < 0) { relTest = 0; } if (relTest > 1) { relTest = 1; } se.setTestSize( max(absTest, (int)(n * relTest + 0.5)) ); Word* a = new Word[n]; Word funresult; string info; cout << "Processing elements ..." << endl; for (int i=0; igetElement(i), funresult, info); a[i] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } se.writeSummary(); result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string loopselectSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) ((name1 (map t r)) ... (namen (map t r))) int real) " "-> (array r)" "_ loopselect [ funlist; _, _ ]" "Evaluates the first \"n\" elements of the array with each of " "the given functions and cumulates the used calculation times. The " "remaining elements are processed with the (so far) \"fastest\" " "function." "query ai loopselect[f:fun(i:int)(i*2), g:fun(l:int)(l+l); " "10, 0.1] ))"; Operator loopselect ( "loopselect", loopselectSpec, loopselectFun, Operator::SimpleSelect, loopselectTypeMap ); /* 4.17 Operator ~loopselecta~ This operator works like operator ~loopa~ extended by the select algorithm of operator ~loopselect~. The formal specification of type mapping is: ---- ((array t) (array u) ((name1 (map t u r)) ... (namen (map t u r))) int real) -> (array r) ---- */ static ListExpr loopselectaTypeMap( ListExpr args ) { if (nl->ListLength(args) == 5) { ListExpr firstArrayDesc = nl->First(args); ListExpr secondArrayDesc = nl->Second(args); ListExpr funlist = nl->Third(args); if (nl->IsEqual(nl->Fourth(args), CcInt::BasicType()) && nl->IsEqual(nl->Fifth(args), CcReal::BasicType())) { return loopswitchaTypeMap(nl->ThreeElemList(firstArrayDesc, secondArrayDesc, funlist)); } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int loopselectaFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* firstArray = (Array*)args[0].addr; Array* secondArray = (Array*)args[1].addr; CcInt* absTestCcInt = (CcInt*)args[3].addr; int absTest = absTestCcInt->GetIntval(); CcReal* relTestCcReal = (CcReal*)args[4].addr; double relTest = relTestCcReal->GetRealval(); SelectAlgorithm se; se.load(args[2], &args[5]); ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = min(firstArray->getSize(), secondArray->getSize()); if (absTest < 1) { absTest = 1; } if (absTest > n) { absTest = n; } if (relTest < 0) { relTest = 0; } if (relTest > 1) { relTest = 1; } se.setTestSize( max(absTest, (int)(n * relTest + 0.5)) ); Word* a = new Word[n]; Word funresult; string info; cout << "Processing elements ..." << endl; for (int i=0; igetElement(i), secondArray->getElement(i), funresult, info); a[i] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } se.writeSummary(); result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string loopselectaSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (array u) ((name1 (map t u r)) ... " "(namen (map t u r))) int real) -> (array r)" "_ _ loopselecta [ funlist; _, _ ]" "Works like operator loopa extended by the select algorithm of " "operator loopselect." "query ai al loopselecta[f:fun(i1:int,l1:int)(i1 mod l1), " "g:fun(i2:int,l2:int)(i2-(i2 div l2)*l2); 10, 0.1] ))"; Operator loopselecta ( "loopselecta", loopselectaSpec, loopselectaFun, Operator::SimpleSelect, loopselectaTypeMap ); /* 4.18 Operator ~loopselectb~ This operator works like operator ~loopb~ extended by the select algorithm of operator ~loopselect~. The formal specification of type mapping is: ---- ((array t) (array u) ((name1 (map t u r)) ... (namen (map t u r))) int real) -> (array r) ---- Therefore the type mapping function of operator ~loopselecta~ can also be used for operator ~loopselectb~. */ static int loopselectbFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* firstArray = (Array*)args[0].addr; Array* secondArray = (Array*)args[1].addr; CcInt* absTestCcInt = (CcInt*)args[3].addr; int absTest = absTestCcInt->GetIntval(); CcReal* relTestCcReal = (CcReal*)args[4].addr; double relTest = relTestCcReal->GetRealval(); SelectAlgorithm se; se.load(args[2], &args[5]); ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = firstArray->getSize(); int m = secondArray->getSize(); int r = m * n; if (absTest < 1) { absTest = 1; } if (absTest > r) { absTest = r; } if (relTest < 0) { relTest = 0; } if (relTest > 1) { relTest = 1; } se.setTestSize( max(absTest, (int)(r * relTest + 0.5)) ); Word* a = new Word[r]; Word funresult; string info; cout << "Processing"; for (int i=0; igetElement(i), secondArray->getElement(l), funresult, info); a[i * m + l] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); } } se.writeSummary(); result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, r, a); delete[] a; return 0; } const string loopselectbSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array t) (array u) ((name1 (map t u r)) ... " "(namen (map t u r))) int real) -> (array r)" "_ _ loopselecta [ funlist; _, _ ]" "Works like operator loopb extended by the select algorithm of " "operator loopselect." "query ai al loopselectb[f:fun(i1:int,l1:int)(i1 mod l1), " "g:fun(i2:int,l2:int)(i2-(i2 div l2)*l2); 10, 0.1] ))"; Operator loopselectb ( "loopselectb", loopselectbSpec, loopselectbFun, Operator::SimpleSelect, loopselectaTypeMap ); /* 4.19 Operator ~partjoin~ The operator ~partjoin~ allows to calculate joins between two partitioned relations (two arrays of relations) in a special way. The formal specification of type mapping is: ---- ((array (rel t)) (array (rel u)) (map (rel t) (rel u) r)) -> (array r) at which t and u are of the type tuple, r may be any type ---- The following functions appends (the tuples of) a relation to an other relation and is used by the partjoin algorithm. */ static void appendToRel( Word& relation, Word append ) // This auxiliary routine is used by the partjoin algorithm to append a // relation to an other relation. { GenericRelation* rel = (GenericRelation*)(relation.addr); GenericRelation* part = (GenericRelation*)(append.addr); GenericRelationIterator* rit = part->MakeScan(); Tuple* tuple; while ((tuple = rit->GetNextTuple()) != 0) { if (rel == 0) { rel = new Relation(tuple->GetTupleType()); rel->Clear(); relation = SetWord(rel); } rel->AppendTuple(tuple); tuple->DeleteIfAllowed(); } delete rit; } /* Implementation of the operator. */ static ListExpr partjoinTypeMap( ListExpr args ) { if (nl->ListLength(args) == 3) { ListExpr firstArrayDesc = nl->First(args); ListExpr secondArrayDesc = nl->Second(args); ListExpr mapDesc = nl->Third(args); if ((nl->ListLength(firstArrayDesc) == 2) && (nl->ListLength(secondArrayDesc) == 2) && (nl->ListLength(mapDesc) == 4)) { if (nl->IsEqual(nl->First(firstArrayDesc), Array::BasicType()) && nl->IsEqual(nl->First(secondArrayDesc), Array::BasicType()) && !nl->IsAtom(nl->Second(firstArrayDesc)) && !nl->IsAtom(nl->Second(secondArrayDesc)) && nl->IsEqual(nl->First(mapDesc), Symbol::MAP())) { ListExpr firstElementDesc = nl->Second(firstArrayDesc); ListExpr secondElementDesc = nl->Second(secondArrayDesc); if (nl->IsEqual(nl->First(firstElementDesc), Relation::BasicType()) && nl->IsEqual(nl->First(secondElementDesc), Relation::BasicType()) && nl->Equal(firstElementDesc, nl->Second(mapDesc)) && nl->Equal(secondElementDesc, nl->Third(mapDesc))) { ListExpr firstTupleDesc = nl->Second(firstElementDesc); ListExpr secondTupleDesc = nl->Second(secondElementDesc); if ((nl->ListLength(firstTupleDesc) == 2) && (nl->ListLength(secondTupleDesc) == 2) && (nl->IsEqual(nl->First(firstTupleDesc), Tuple::BasicType())) && (nl->IsEqual(nl->First(secondTupleDesc), Tuple::BasicType()))) { return nl->TwoElemList(nl->SymbolAtom(Array::BasicType()), nl->Fourth(mapDesc)); } } } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int partjoinFun( Word* args, Word& result, int message, Word& local, Supplier s ) { // INITIALIZATION SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* firstArray = ((Array*)args[0].addr); Array* secondArray = ((Array*)args[1].addr); FunInfo f(0, "0", args[2].addr); Word funresult; string info; ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = firstArray->getSize(); int m = secondArray->getSize(); Word* a = new Word[n + m - 1]; int c = 0; int i = 0; int j = 0; Word Acum = SetWord(Address(0)); Word Bcum = SetWord(Address(0)); // FIRST PART OF THE PARTJOIN ALGORITHM // Process the first elements individually to get an "entry point" for the // second part (see below). cout << "Processing (phase 1):" << endl; if ((i-j) <= (n-m)) { while ((i-j) <= (n-m)) { info = "(" + toString(i) + ", " + toString(j) + ")"; f.request(firstArray->getElement(i), secondArray->getElement(j), funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); if (i > 0) { appendToRel(Acum, firstArray->getElement(i-1)); } i++; } j++; appendToRel(Bcum, secondArray->getElement(0)); } else { while ((i-j) >= (n-m)) { info = "(" + toString(i) + ", " + toString(j) + ")"; f.request(firstArray->getElement(i), secondArray->getElement(j), funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); appendToRel(Bcum, secondArray->getElement(j)); j++; } i++; } // SECOND PART OF THE PARTJOIN ALGORITHM // The next loop implements the main concept of the partjoin algorithm: // the step by step summarization and evaluation of larger parts of the // participating arrays of relations. cout << "Processing (phase 2):" << endl; while (i < n) { appendToRel(Acum, firstArray->getElement(i-1)); appendToRel(Bcum, secondArray->getElement(j)); info = "[0;" + toString(i-1) + "] and " + toString(j); f.request(Acum, secondArray->getElement(j), funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); info = toString(i) + " and [0;" + toString(j) + "]"; f.request(firstArray->getElement(i), Bcum, funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); i++; j++; } // SET RESULT AND CLEAN UP result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, c, a); ((Relation*)Acum.addr)->Delete(); ((Relation*)Bcum.addr)->Delete(); delete[] a; return 0; } const string partjoinSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array (rel t)) (array (rel u)) (map (rel t) (rel u) r)) " "-> (array r)" "_ _ partjoin [ fun ]" "Allows to calculate joins between two arrays of relations " "in an efficient way." "query ar ar partjoin[fun(r1:reltype,r2:reltype)r1 feed r2 feed " "rename[A] product count] ))"; Operator partjoin ( "partjoin", partjoinSpec, partjoinFun, Operator::SimpleSelect, partjoinTypeMap ); /* 4.20 Operator ~partjoinswitch~ This operator works like operator ~partjoin~ extended by the switch algorithm of operator ~loopswitch~. The formal specification of type mapping is: ---- ((array (rel t)) (array (rel u)) ((name1 (map (rel t) (rel u) r)) ... (namen (map (rel t) (rel u) r)))) -> (array r) at which t and u are of the type tuple, r may be any type ---- */ static ListExpr partjoinswitchTypeMap( ListExpr args ) { if (nl->ListLength(args) == 3) { ListExpr firstArrayDesc = nl->First(args); ListExpr secondArrayDesc = nl->Second(args); ListExpr funlist = nl->Third(args); if ((nl->ListLength(firstArrayDesc) == 2) && (nl->ListLength(secondArrayDesc) == 2)) { if ((nl->IsEqual(nl->First(firstArrayDesc), Array::BasicType())) && (nl->IsEqual(nl->First(secondArrayDesc), Array::BasicType())) && !nl->IsAtom(nl->Second(firstArrayDesc)) && !nl->IsAtom(nl->Second(secondArrayDesc))) { ListExpr firstElementDesc = nl->Second(firstArrayDesc); ListExpr secondElementDesc = nl->Second(secondArrayDesc); if ((nl->ListLength(firstElementDesc) == 2) && (nl->ListLength(secondElementDesc) == 2) && (nl->IsEqual(nl->First(firstElementDesc), Relation::BasicType())) && (nl->IsEqual(nl->First(secondElementDesc), Relation::BasicType()))) { ListExpr firstTupleDesc = nl->Second(firstElementDesc); ListExpr secondTupleDesc = nl->Second(secondElementDesc); if ((nl->ListLength(firstTupleDesc) == 2) && (nl->ListLength(secondTupleDesc) == 2) && (nl->IsEqual(nl->First(firstTupleDesc), Tuple::BasicType())) && (nl->IsEqual(nl->First(secondTupleDesc), Tuple::BasicType()))) { ListExpr firstFunDesc = nl->First(funlist); funlist = nl->Rest(funlist); ListExpr funNames = nl->TheEmptyList(); ListExpr last; ListExpr firstMapDesc; if (nl->ListLength(firstFunDesc) == 2) { ListExpr firstFunName = nl->First(firstFunDesc); firstMapDesc = nl->Second(firstFunDesc); if ((nl->AtomType(firstFunName) == SymbolType) && (nl->ListLength(firstMapDesc) == 4)) { if ((nl->IsEqual(nl->First(firstMapDesc), Symbol::MAP())) && (!nl->IsEqual(nl->Fourth(firstMapDesc), Symbol::TYPEERROR())) && (nl->Equal(firstElementDesc, nl->Second(firstMapDesc))) && (nl->Equal(secondElementDesc, nl->Third(firstMapDesc)))) { funNames = nl->OneElemList( nl->StringAtom(nl->SymbolValue(firstFunName))); last = funNames; } } } if (!nl->IsEmpty(funNames)) { bool ok = true; while (!nl->IsEmpty(funlist) && ok) { ListExpr funDesc = nl->First(funlist); if (nl->ListLength(funDesc) == 2) { ListExpr funName = nl->First(funDesc); ListExpr mapDesc = nl->Second(funDesc); if ((nl->AtomType(funName) == SymbolType) && (nl->Equal(mapDesc, firstMapDesc))) { last = nl->Append(last, nl->StringAtom(nl->SymbolValue(funName))); } else { ok = false; } } else { ok = false; } funlist = nl->Rest(funlist); } if (ok) { return nl->ThreeElemList( nl->SymbolAtom(Symbol::APPEND()), funNames, nl->TwoElemList( nl->SymbolAtom(Array::BasicType()), nl->Fourth(firstMapDesc))); } } } } } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int partjoinswitchFun( Word* args, Word& result, int message, Word& local, Supplier s ) { // See value mapping function of operator partjoin for remarks with regard to // the partjoin algorithm. SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* firstArray = ((Array*)args[0].addr); Array* secondArray = ((Array*)args[1].addr); SwitchAlgorithm sw; sw.load(args[2], &args[3]); Word funresult; string info; ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = firstArray->getSize(); int m = secondArray->getSize(); Word* a = new Word[n + m - 1]; int c = 0; int i = 0; int j = 0; Word Acum = SetWord(Address(0)); Word Bcum = SetWord(Address(0)); cout << "Processing (phase1):" << endl; if ((i-j) <= (n-m)) { while ((i-j) <= (n-m)) { info = "(" + toString(i) + ", " + toString(j) + ")"; sw.request(firstArray->getElement(i), secondArray->getElement(j), funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); if (i > 0) { appendToRel(Acum, firstArray->getElement(i-1)); } i++; } j++; appendToRel(Bcum, secondArray->getElement(0)); } else { while ((i-j) >= (n-m)) { info = "(" + toString(i) + ", " + toString(j) + ")"; sw.request(firstArray->getElement(i), secondArray->getElement(j), funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); appendToRel(Bcum, secondArray->getElement(j)); j++; } i++; } cout << "Processing (phase2):" << endl; while (i < n) { appendToRel(Acum, firstArray->getElement(i-1)); appendToRel(Bcum, secondArray->getElement(j)); info = "[0;" + toString(i-1) + "] and " + toString(j); sw.request(Acum, secondArray->getElement(j), funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); info = toString(i) + " and [0;" + toString(j) + "]"; sw.request(firstArray->getElement(i), Bcum, funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); i++; j++; } sw.writeSummary(); result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, c, a); ((Relation*)Acum.addr)->Delete(); ((Relation*)Bcum.addr)->Delete(); delete[] a; return 0; } const string partjoinswitchSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array (rel t)) (array (rel u)) ((name1 (map (rel t) (rel u) r))" " ... (namen (map (rel t) (rel u) r)))) -> (array r)" "_ _ partjoinswitch [ funlist ]" "Works like operator partjoin extended by the switch algorithm " "of operator loopswitch." "query ar ar partjoinswitch[f:fun(r11:reltype,r12:reltype)" "r11 feed r12 feed rename[A] sortmergejoin[no,no_A] count, " "g:fun(r21:reltype,r22:reltype)r21 feed r22 feed rename[A] product " "filter[.no=.no_A] count] ))"; Operator partjoinswitch ( "partjoinswitch", partjoinswitchSpec, partjoinswitchFun, Operator::SimpleSelect, partjoinswitchTypeMap ); /* 4.21 Operator ~partjoinselect~ This operator works like operator ~partjoin~ extended by the select algorithm of operator ~loopselect~. The formal specification of type mapping is: ---- ((array (rel t)) (array (rel u)) ((name1 (map (rel t) (rel u) r)) ... namen (map (rel t) (rel u) r))) int real) -> (array r) at which t and u are of the type mtuple, r may be any type ---- */ static ListExpr partjoinselectTypeMap( ListExpr args ) { if (nl->ListLength(args) == 5) { ListExpr firstArrayDesc = nl->First(args); ListExpr secondArrayDesc = nl->Second(args); ListExpr funlist = nl->Third(args); if (nl->IsEqual(nl->Fourth(args), CcInt::BasicType()) && nl->IsEqual(nl->Fifth(args), CcReal::BasicType())) { return partjoinswitchTypeMap(nl->ThreeElemList(firstArrayDesc, secondArrayDesc, funlist)); } } return nl->SymbolAtom(Symbol::TYPEERROR()); } static int partjoinselectFun( Word* args, Word& result, int message, Word& local, Supplier s ) { // See value mapping function of operator partjoin for remarks with regard to // the partjoin algorithm. SecondoCatalog* sc = SecondoSystem::GetCatalog(); Array* firstArray = ((Array*)args[0].addr); Array* secondArray = ((Array*)args[1].addr); CcInt* absTestCcInt = ((CcInt*)args[3].addr); int absTest = absTestCcInt->GetIntval(); CcReal* relTestCcReal = ((CcReal*)args[4].addr); double relTest = relTestCcReal->GetRealval(); SelectAlgorithm se; se.load(args[2], &args[5]); Word funresult; string info; ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; extractIds(typeOfElement, algebraId, typeId); int n = firstArray->getSize(); int m = secondArray->getSize(); int r = n + m - 1; if (absTest < 1) { absTest = 1; } if (absTest > r) { absTest = r; } if (relTest < 0) { relTest = 0; } if (relTest > 1) { relTest = 1; } se.setTestSize( max(absTest, (int)(r * relTest + 0.5)) ); Word* a = new Word[r]; int c = 0; int i = 0; int j = 0; Word Acum = SetWord(Address(0)); Word Bcum = SetWord(Address(0)); cout << "Processing (phase 1):" << endl; if ((i-j) <= (n-m)) { while ((i-j) <= (n-m)) { info = "(" + toString(i) + ", " + toString(j) + ")"; se.request(firstArray->getElement(i), secondArray->getElement(j), funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); if (i > 0) { appendToRel(Acum, firstArray->getElement(i-1)); } i++; } j++; appendToRel(Bcum, secondArray->getElement(0)); } else { while ((i-j) >= (n-m)) { info = "(" + toString(i) + ", " + toString(j) + ")"; se.request(firstArray->getElement(i), secondArray->getElement(j), funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); appendToRel(Bcum, secondArray->getElement(j)); j++; } i++; } cout << "Processing (phase 2):" << endl; while (i < n) { appendToRel(Acum, firstArray->getElement(i-1)); appendToRel(Bcum, secondArray->getElement(j)); info = "[0;" + toString(i-1) + "] and " + toString(j); se.request(Acum, secondArray->getElement(j), funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); info = toString(i) + " and [0;" + toString(j) + "]"; se.request(firstArray->getElement(i), Bcum, funresult, info); a[c++] = Array::genericClone(algebraId, typeId, typeOfElement, funresult); i++; j++; } se.writeSummary(); result = qp->ResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, c, a); ((Relation*)Acum.addr)->Delete(); ((Relation*)Bcum.addr)->Delete(); delete[] a; return 0; } const string partjoinselectSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( ((array (rel t)) (array (rel u)) ((name1 (map (rel t) (rel u) r))" " ... (namen (map (rel t) (rel u) r))) int real) -> (array r)" "_ _ partjoinselect [ funlist ]" "Works like operator partjoin extended by the select algorithm " "of operator loopselect." "query ar ar partjoinselect[f:fun(r11:reltype,r12:reltype)" "r11 feed r12 feed rename[A] sortmergejoin[no,no_A] count, " "g:fun(r21:reltype,r22:reltype)r21 feed r22 feed rename[A] product " "filter[.no=.no_A] count; 10, 0.1] ))"; Operator partjoinselect ( "partjoinselect", partjoinselectSpec, partjoinselectFun, Operator::SimpleSelect, partjoinselectTypeMap ); /* 4.22 Type Operator ~ELEMENT~ Type operators are used only for inferring argument types of parameter functions. They have a type mapping but no evaluation function. This type operator extracts the type of the elements from an array type given as the first argument. ---- ((array t) ...) -> t ---- */ ListExpr ELEMENTTypeMap( ListExpr args ) { if(nl->ListLength(args) >= 1) { ListExpr first = nl->First(args); if (nl->ListLength(first) == 2) { if (nl->IsEqual(nl->First(first), Array::BasicType())) { return nl->Second(first); } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } const string ELEMENTSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Remarks\" )" "( ((array t) ... ) -> t" "type operator" "Extracts the type of the elements from an array type given " "as the first argument." "not for use with sos-syntax ))"; Operator ELEMENT ( "ELEMENT", ELEMENTSpec, 0, Operator::SimpleSelect, ELEMENTTypeMap ); /* 4.23 Type Operator ~ELEMENT2~ This type operator extracts the type of the elements from the second array type within a list of argument types. ---- ((array t) (array u) ...) -> u ---- (The first argument must not be an array. It may also be any other type) */ ListExpr ELEMENT2TypeMap( ListExpr args ) { if(nl->ListLength(args) >= 2) { ListExpr second = nl->Second(args); if (nl->ListLength(second) == 2) { if (nl->IsEqual(nl->First(second), Array::BasicType())) { return nl->Second(second); } } } return nl->SymbolAtom(Symbol::TYPEERROR()); } const string ELEMENT2Spec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Remarks\" )" "( ((array t) (array u) ... ) -> u" "type operator" "Extracts the type of the elements from an array type given " "as the second argument." "not for use with sos-syntax. The first argument must not " "be an array. It may also be any other type. ))"; Operator ELEMENT2 ( "ELEMENT2", ELEMENT2Spec, 0, Operator::SimpleSelect, ELEMENT2TypeMap ); /* 4.24 Operator ~makearrayn~ This operator creates an array containing N clones of the element given. The type mapping is: ---- (t int) -> (array t) ---- */ static ListExpr makearrayNTypeMap( ListExpr args ) { if (nl->ListLength(args) == 2) { ListExpr typeOfElement = nl->First(args); ListExpr noIfClones = nl->Second(args); if ( nl->IsEqual(noIfClones, CcInt::BasicType()) ){ return nl->TwoElemList(nl->SymbolAtom(Array::BasicType()), typeOfElement); } } ErrorReporter::ReportError("Operator 'makearrayN' expects a list " "(elem int)."); return nl->SymbolAtom(Symbol::TYPEERROR()); } static int makearrayNFun( Word* args, Word& result, int message, Word& local, Supplier s ) { SecondoCatalog* sc = SecondoSystem::GetCatalog(); ListExpr type = qp->GetType(s); ListExpr typeOfElement = sc->NumericType(nl->Second(type)); int algebraId; int typeId; int n = 0; extractIds(typeOfElement, algebraId, typeId); CcInt* cN = (CcInt*) args[1].addr; if ( !cN->IsDefined() || cN->GetIntval()<1 ){ ErrorReporter::ReportError("ArrayAlgebra::makearrayN: " "Illegeal or undefined Array size."); n = 0; } else{ n = cN->GetIntval(); } Word* a = new Word[n]; for (int i=0; iResultStorage(s); ((Array*)result.addr)->initialize(algebraId, typeId, n, a); delete[] a; return 0; } const string makearrayNSpec = "(( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )" "( (t int) -> (array t)" "makearrayN ( Element, N )" "Creates an array containing N clones of a given Element." "" "let ai = makearrayN (3, 4) ))"; Operator makearrayN( "makearrayN", makearrayNSpec, makearrayNFun, Operator::SimpleSelect, makearrayNTypeMap ); } // end of namespace arrayalgebra /* 5 Creating the Algebra */ class ArrayAlgebra : public Algebra { public: ArrayAlgebra() : Algebra() { AddTypeConstructor( &arrayalgebra::array ); arrayalgebra::array.AssociateKind(Kind::ARRAY()); AddOperator( &arrayalgebra::size ); AddOperator( &arrayalgebra::get ); AddOperator( &arrayalgebra::put ); AddOperator( &arrayalgebra::makearray ); AddOperator( &arrayalgebra::makearrayN ); AddOperator( &arrayalgebra::sortarray ); AddOperator( &arrayalgebra::tie ); AddOperator( &arrayalgebra::cumulate ); AddOperator( &arrayalgebra::distribute ); AddOperator( &arrayalgebra::summarize ); AddOperator( &arrayalgebra::loop ); AddOperator( &arrayalgebra::loopa ); AddOperator( &arrayalgebra::loopb ); AddOperator( &arrayalgebra::loopswitch ); AddOperator( &arrayalgebra::loopswitcha ); AddOperator( &arrayalgebra::loopswitchb ); AddOperator( &arrayalgebra::loopselect ); AddOperator( &arrayalgebra::loopselecta ); AddOperator( &arrayalgebra::loopselectb ); AddOperator( &arrayalgebra::partjoin ); AddOperator( &arrayalgebra::partjoinswitch ); AddOperator( &arrayalgebra::partjoinselect ); AddOperator( &arrayalgebra::ELEMENT ); AddOperator( &arrayalgebra::ELEMENT2 ); } ~ArrayAlgebra() {}; }; /* 6 Initialization ["]Each algebra module needs an initialization function. The algebra manager has a reference to this function if this algebra is included in the list of required algebras, thus forcing the linker to include this module. The algebra manager invokes this function to get a reference to the instance of the algebra class and to provide references to the global nested list container (used to store constructor, type, operator and object information) and to the query processor. The function has a C interface to make it possible to load the algebra dynamically at runtime.["] [Point02] */ extern "C" Algebra* InitializeArrayAlgebra( NestedList* nlRef, QueryProcessor* qpRef, AlgebraManager* amRef ) { nl = nlRef; qp = qpRef; am = amRef; return new ArrayAlgebra(); } /* 7 References [Point02] Algebra Module PointRectangleAlgebra. FernUniversit[ae]t Hagen, Praktische Informatik IV, Secondo System, Directory ["]Algebras/PointRectangle["], file ["]PointRectangleAlgebra.cpp["], since July 2002 */