% BEGIN LICENSE BLOCK % Version: CMPL 1.1 % % The contents of this file are subject to the Cisco-style Mozilla Public % License Version 1.1 (the "License"); you may not use this file except % in compliance with the License. You may obtain a copy of the License % at www.eclipse-clp.org/license. % % Software distributed under the License is distributed on an "AS IS" % basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See % the License for the specific language governing rights and limitations % under the License. % % The Original Code is The List Collection for ECLiPSe. % The Initial Developer of the Original Code is Lukasz Domagala. % Portions created by the Initial Developer are Copyright (C) 2009. % All Rights Reserved. :- module(list_collection). :- comment(categories, ["Data Structures"]). :- comment(summary, "Collection of lists library"). :- comment(author, "Lukasz Domagala"). :- comment(copyright, "Silesian University of Technology"). :- comment(date, "$Date: 2009/07/16 09:11:23 $"). :- comment(desc,ascii(" A library for creation and management of list collections (LCOL). Internally the library uses either the a hashtable or arrays to store lists and their tails. The advantage of a hash LCOL is that its size does not have to be known in advance and that non number indexes can be used to address lists in the collection. The drawback of a hash LCOL is that access to collection elements is slower than for array LCOL. Elements of array LCOL can be addressed only by positive integers which internally are array indexes. See create/2 for more details. The motivation for creating this library was to be able to easily build input lists for global constraints from collections of input structures (see example). For this purpose an array(3) LCOL was usually sufficient, and only a limited set of predicates needed (create/2 , append_element/3, terminate_all_lists/2, get_list/3). But the idea was extended, additional predicates were added for prefixing the lists, and reinitialisation of lists. The support for hash LCOL has been added. Hash LCOL may be useful for some filtering, grouping predicates in cases when the number of lists may vary or indexing by atoms/ground terms is more readable/convenient. ")). :- comment(eg," llcol_example:- lib(ic), lib(ic_edge_finder), ActivityList=[ act(4,7 ,2,machine_1), act(3,4 ,1,machine_2 ), act(2,4 ,2,removed), act(7,10,1,machine_1), act(6,15,3,machine_3) % ... ], %create a collection of activity lists for corresponding machines list_collection:create(hash,LCOL_Machines), %sort the activities for machines (foreach(Activity,ActivityList), param(LCOL_Machines) do Activity=act(_Start,_End,_Resource,Machine), (Machine \\= removed -> %add an activity to the list for the particular machine list_collection:append_element(LCOL_Machines,Machine,Activity) ;true) ), %close the lists get the machine names list_collection:terminate_all_lists(LCOL_Machines,Machines_Indexes), %for each machine (foreach(Machines_Index,Machines_Indexes), param(LCOL_Machines) do %Note that get_list/3 can be used even if the lists are not terminated but in such a case the operation %is more time consuming, because all the list elements are copied to new closed list and then returned. %Instead of closing all the lists at once it is also possible to use terminate_and_get_list/3 to ensure %that the list is closed. %get the activity list for the machine list_collection:get_list(LCOL_Machines,Machines_Index,MActivityList), %create a collection for start,duration and resource variable lists list_collection:create(array(3),LCOL_Start_Dur_Res), (foreach(MActivity,MActivityList), param(LCOL_Start_Dur_Res) do MActivity=act(Start,End,Resource,_Machine), ic:(Duration #= End - Start), list_collection:append_element(LCOL_Start_Dur_Res, 1, Start), list_collection:append_element(LCOL_Start_Dur_Res, 2, Duration), list_collection:append_element(LCOL_Start_Dur_Res, 3, Resource) ), %terminate the start,duration,resource lists list_collection:terminate_all_lists(LCOL_Start_Dur_Res,_Indexes), %get the lists for a cumulative constraint list_collection:get_list(LCOL_Start_Dur_Res, 1, StartList), list_collection:get_list(LCOL_Start_Dur_Res, 2, DurationList), list_collection:get_list(LCOL_Start_Dur_Res, 3, ResourceList), %post the constraint ic_edge_finder:cumulative(StartList, DurationList, ResourceList, 3) ). "). :- lib(hash). :- local struct(list_collection( type, open_list_array, tail_array, list_tail_hash )). :- comment(create/2, [ amode:(create(++,-) is det), args:["Type":"A ground term", "LCOL":"A list collection"], summary:"Creates a list collection", desc:ascii(" If (Type==hash) a LCOL internally uses a hashtable, this is useful when the number of lists is unknown. If (Type=array(Size)) where Size is a natural number, the LCOL internally uses an array to collect lists, the access to any of the lists is faster, but the Size has to be known in advance and only number indexes can be used to identify lists. 0 %if the list is closed there is no need to copy the elements, just get list List = OpenList ; (free(OpenList)-> %empty list List=[] ; (fromto(OpenList,[Elem|OpenListNext],OpenListOut,[]), foreach(Elem,List), param(OpenListTail) do (OpenListNext\==OpenListTail-> OpenListOut = OpenListNext ; OpenListOut=[] ) ) ) ), true. :- comment(terminate_and_get_list/3, [ amode:(terminate_and_get_list(+,++,-) is det), args:["LCOL":"A list collection", "Index":"A ground term or number", "List":"A list of terms"], summary:"Retrieves the List of elements identified by Index", desc:"Ensures that the internal List of elements identified by Index is terminated, and returns the internal List. Complexity O(1).", %eg:"", see_also:[get_list/3, terminate_all_lists/2,reinit/2], % fail_if:"" , exceptions:[ 5:"LCOL is not a list collection.", 5:"Index is not a number or is out of range.", 5:"Index is not ground." ] ]). :-export terminate_and_get_list/3. :- mode(terminate_and_get_list(+,++,-)). terminate_and_get_list(LCOL, Index, List):- check_lcol_exc(LCOL), check_index_exc(LCOL, Index), ensure_entry_exists_4_index(LCOL, Index,LTTerm), inner_tail_get(LCOL,Index,LTTerm,[]), inner_list_get(LCOL,Index,LTTerm,List), true. :- comment(terminate_all_lists/2, [ amode:(terminate_all_lists(+,-) is det), args:["LCOL":"A list collection", "Indexes":"List of indexes"], summary:"Terminates all internal lists in the collection, and gets the list of indexes", desc:"Complexity O(ListCount).", %eg:"", see_also:[get_list/3,terminate_and_get_list/3,reinit/2,get_indexes/2], %fail_if:"" exceptions:[ 5:"LCOL is not a list collection." ] ]). :-export terminate_all_lists/2. :- mode(terminate_all_lists(+,-)). terminate_all_lists(LCOL,Indexes):- check_lcol_exc(LCOL), LCOL=list_collection{ type:hash, open_list_array:_OpenListArray, tail_array :_TailArray, list_tail_hash :ListTailHash }, !, hash_list(ListTailHash,IndexesUnsorted,LTTermLi), sort(IndexesUnsorted,Indexes), (foreach(list_tail(_,[]),LTTermLi) do true ), true. terminate_all_lists(LCOL,Indexes):- check_lcol_exc(LCOL), LCOL=list_collection{ type:array(_Size), open_list_array:_OpenListArray, tail_array :TailArray, list_tail_hash :_ListTailHash }, !, (foreacharg([],TailArray,Index), foreach(Index,Indexes) do true ), true. :- comment(get_indexes/2, [ amode:(get_indexes(+,-) is det), args:["LCOL":"A list collection", "Indexes":"List of indexes"], summary:"Retrieves the list of indexes", desc:"Complexity O(ListCount).", %eg:"", see_also:[get_list/3,terminate_and_get_list/3,terminate_all_lists/2], %fail_if:"" exceptions:[ 5:"LCOL is not a list collection." ] ]). :-export get_indexes/2. :- mode(get_indexes(+,-)). get_indexes(LCOL,Indexes):- check_lcol_exc(LCOL), LCOL=list_collection{ type:hash, open_list_array:_OpenListArray, tail_array :_TailArray, list_tail_hash :ListTailHash }, !, hash_list(ListTailHash,IndexesUnsorted,_LTTermLi), sort(IndexesUnsorted,Indexes), true. get_indexes(LCOL,Indexes):- check_lcol_exc(LCOL), LCOL=list_collection{ type:array(_Size), open_list_array:_OpenListArray, tail_array :TailArray, list_tail_hash :_ListTailHash }, !, (foreacharg(_Tail,TailArray,Index), foreach(Index,Indexes) do true ), true. :- comment(reinit/2, [ amode:(reinit(+,++) is det), args:["LCOL":"A list collection", "Index":"A ground term or number"], summary:"Creates a new list identified by Index", desc:"Discards the internal list of elements identified by Index, and replaces with a new list. Complexity O(1).", %eg:"", see_also:[terminate_all_lists/2,terminate_and_get_list/3], %fail_if:"" , exceptions:[ 5:"LCOL is not a list collection.", 5:"Index is not a number or is out of range.", 5:"Index is not ground." ] ]). :-export reinit/2. :- mode(reinit(+,++)). reinit(LCOL, Index):- check_lcol_exc(LCOL), !, check_index_exc(LCOL, Index), ensure_entry_exists_4_index(LCOL, Index,LTTerm), inner_list_set(LCOL,Index,LTTerm,ListTail), inner_tail_set(LCOL,Index,LTTerm,ListTail), true. inner_tail_get(LCOL,Index,LTTerm,Tail):- LCOL=list_collection{ type:Type, open_list_array:_OpenListArray, tail_array :TailArray, list_tail_hash :_ListTailHash }, (Type == hash, not free(LTTerm) -> LTTerm = list_tail(_OpenList,Tail) ; (instance(Type,array(_Size))-> arg(Index,TailArray,Tail) ; exc_not_supported_type ) ). inner_tail_set(LCOL,Index,LTTerm,Tail):- LCOL=list_collection{ type:Type, open_list_array:_OpenListArray, tail_array :TailArray, list_tail_hash :_ListTailHash }, (Type == hash,not free(LTTerm) -> setarg(2,LTTerm,Tail) ; (instance(Type,array(_Size))-> setarg(Index,TailArray,Tail) ; exc_not_supported_type ) ). inner_list_get(LCOL,Index,LTTerm,OpenList):- LCOL=list_collection{ type:Type, open_list_array:OpenListArray, tail_array :_TailArray }, (Type == hash,not free(LTTerm) -> LTTerm= list_tail(OpenList,_Tail) ; (instance(Type,array(_Size))-> arg(Index,OpenListArray,OpenList) ; exc_not_supported_type ) ). inner_list_set(LCOL,Index,LTTerm,OpenList):- LCOL=list_collection{ type:Type, open_list_array:OpenListArray, tail_array :_TailArray, list_tail_hash :_ListTailHash }, (Type == hash,not free(LTTerm) -> setarg(1,LTTerm,OpenList) ; (instance(Type,array(_Size))-> setarg(Index,OpenListArray,OpenList) ; exc_not_supported_type ) ). ensure_entry_exists_4_index(LCOL, Index,LTTerm):- LCOL=list_collection{ type:hash, open_list_array:_OpenListArray, tail_array :_TailArray, list_tail_hash :ListTailHash }, !, (hash_contains(ListTailHash,Index)-> hash_get(ListTailHash,Index,LTTerm) ; functor(LTTerm,list_tail,2), setarg(1,LTTerm,ListTail), setarg(2,LTTerm,ListTail), hash_add(ListTailHash,Index,LTTerm) ), true. ensure_entry_exists_4_index(LCOL, _Index,_LTTerm):- LCOL=list_collection{ type:array(_Size), open_list_array:_OpenListArray, tail_array :_TailArray }, !, true. check_index_exc(LCOL, Index):- LCOL=list_collection{ type:hash, open_list_array:_OpenListArray, tail_array :_TailArray, list_tail_hash :_ListTailHash }, (ground(Index)-> true ; exc_index_is_not_ground ),!. check_index_exc(LCOL, Index):- LCOL=list_collection{ type:array(Size), open_list_array:_OpenListArray, tail_array :_TailArray, list_tail_hash :_ListTailHash }, (number(Index),0 true ; exc_index_is_not_a_num_or_out_of_range ),!. check_open_tail_exc(LCOL, Index,LTTerm):- inner_tail_get(LCOL,Index,LTTerm,Tail), (free(Tail)-> true ; exc_list_is_closed ). check_lcol_exc(LCOL):- (instance(LCOL,list_collection{}), LCOL=list_collection{ type:Type, open_list_array:_OpenListArray, tail_array :_TailArray, list_tail_hash :_ListTailHash }, ground(Type) -> true ; exc_lcol_is_not_a_proper_structure ), true. exc_lcol_is_not_a_proper_structure:- writeln(error,"LCOL is not a list collection."), exit_block(lcol_exception). exc_not_supported_type:- writeln(error,"Not supported Type."), exit_block(lcol_exception). exc_list_is_closed:- writeln(error,"List identified by Index is closed."), exit_block(lcol_exception). exc_index_is_not_a_num_or_out_of_range:- writeln(error,"Index is not a number or is out of range."), exit_block(lcol_exception). exc_index_is_not_ground:- writeln(error,"Index is not ground."), exit_block(lcol_exception). %---------------------------------------------------------------------- end_of_file. %---------------------------------------------------------------------- %just for testing if documentation is generated properly lcol_test_generate_doc:- get_flag(cwd, Cwd), concat_string([Cwd,"list_collection.ecl"],File), document:icompile(File), document:eci_to_html("list_collection.eci", Cwd, ""), true. %self testing of the library, the predicate has to be true lcol_self_test:- block( lcol_self_test_lcol_type(wrong_type) ,lcol_exception, true), lcol_self_test_lcol_type(hash), lcol_self_test_lcol_type(array(3)), true. lcol_self_test_lcol_type(LCOL_Type):- %Create a Collection Of Lists list_collection:create(LCOL_Type,LCOL), %appending elements to List1 and List2 list_collection:append_element(LCOL, 1, list_1_elem_1), list_collection:append_element(LCOL, 1, list_1_elem_2), list_collection:append_element(LCOL, 2, list_2_elem_1), list_collection:append_element(LCOL, 2, list_2_elem_2), %prefixig the List1 by an element list_collection:prefix_element(LCOL, 1, list_1_pref_1), %prefixing the List2 by a list of elements list_collection:prefix_list(LCOL,2,[list_2_pref_2,list_2_pref_1]), %appending a list of alements to List3 list_collection:append_list(LCOL,3,[list_3_elem_1,list_3_elem_2]), %getting the List1 elements without closing the internal List1 get_list(LCOL,1,List1Elements), List1Elements = [list_1_pref_1,list_1_elem_1,list_1_elem_2], %still possible to append to List1 because it is not closed list_collection:append_element(LCOL, 1, list_1_elem_3), %terminating the List1 and retrieving it terminate_and_get_list(LCOL, 1, List1), List1 == [list_1_pref_1,list_1_elem_1,list_1_elem_2,list_1_elem_3], block( %the following would cause an exception because the List1 is closed ( list_collection:append_element(LCOL, 1, list_1_elem_4), fail ) ,lcol_exception, true), %but it is possible to prefix the List1 list_collection:prefix_element(LCOL, 1, list_1_pref_2), terminate_and_get_list(LCOL, 1, List1Prefixed), List1Prefixed == [list_1_pref_2,list_1_pref_1,list_1_elem_1,list_1_elem_2,list_1_elem_3], %internally List1 is discarded and a new List1 is created reinit(LCOL,1), get_list(LCOL,1,List1NewElements), List1NewElements == [], list_collection:append_element(LCOL, 1, list_1_elem_1), terminate_and_get_list(LCOL, 1, List1New), List1New == [list_1_elem_1], block( %the following would cause an exception but only for the array(3) LCOL %because the index is out of range (LCOL_Type == array(3) -> terminate_and_get_list(LCOL, 4, _List4Treminated), fail ;true) %for a hash LCOL it would not cause an exception, %a new list for index 4 would be created closed and returned ,lcol_exception, true), %terminate all the lists terminate_all_lists(LCOL,Indexes), Indexes = [1,2,3], %check the get_indexes get_indexes(LCOL,Indexes), %after all the lists are terminated get_list/3 does not need to copy the elements %to a new terminated list as above, it just gives back the internal lists get_list(LCOL, 2, List2Treminated), List2Treminated == [list_2_pref_2,list_2_pref_1,list_2_elem_1,list_2_elem_2], get_list(LCOL, 3, List3Treminated), List3Treminated == [list_3_elem_1,list_3_elem_2], true. %example lcol_example:- lib(ic), lib(ic_edge_finder), ActivityList=[ act(4,7 ,2,machine_1), act(3,4 ,1,machine_2 ), act(2,4 ,2,removed), act(7,10,1,machine_1), act(6,15,3,machine_3) % ... ], %create a collection of activity lists for corresponding machines list_collection:create(hash,LCOL_Machines), %sort the activities for machines (foreach(Activity,ActivityList), param(LCOL_Machines) do Activity=act(_Start,_End,_Resource,Machine), (Machine \= removed -> %add an activity to the list for the particular machine list_collection:append_element(LCOL_Machines,Machine,Activity) ;true) ), %close the lists get the machine names list_collection:terminate_all_lists(LCOL_Machines,Machines_Indexes), %for each machine (foreach(Machines_Index,Machines_Indexes), param(LCOL_Machines) do %Note that get_list/3 can be used even if the lists are not terminated but in such a case the operation %is more time consuming, because all the list elements are copied to new closed list and then returned. %Instead of closing all the lists at once it is also possible to use terminate_and_get_list/3 to ensure %that the list is closed. %get the activity list for the machine list_collection:get_list(LCOL_Machines,Machines_Index,MActivityList), %create a collection for start,duration and resource variable lists list_collection:create(array(3),LCOL_Start_Dur_Res), (foreach(MActivity,MActivityList), param(LCOL_Start_Dur_Res) do MActivity=act(Start,End,Resource,_Machine), ic:(Duration #= End - Start), list_collection:append_element(LCOL_Start_Dur_Res, 1, Start), list_collection:append_element(LCOL_Start_Dur_Res, 2, Duration), list_collection:append_element(LCOL_Start_Dur_Res, 3, Resource) ), %terminate the start,duration,resource lists list_collection:terminate_all_lists(LCOL_Start_Dur_Res,_Indexes), %get the lists for a cumulative constraint list_collection:get_list(LCOL_Start_Dur_Res, 1, StartList), list_collection:get_list(LCOL_Start_Dur_Res, 2, DurationList), list_collection:get_list(LCOL_Start_Dur_Res, 3, ResourceList), %post the constraint ic_edge_finder:cumulative(StartList, DurationList, ResourceList, 3) ).