--This file contains an example of how to deal with a backtracking problem
--in PSF. The problem is to find all permutations of a list of items, which
--satisfy a given test function. It is assumed that the test function is
--monotonous in the sense that if the test of list l fails, then the test
--of any sequence beginning with l false. The solutions are not calculated by
--first generating all permutations and then delete those which do not
--satisfy the test. Rather, an initial sequence of a collection of permutations
--is tested before generating these permutations. Thus the state space is
--pruned.
--Note: solve(emptyl) = add(emptyl,emptyll) for any test function. 

data module boolean
begin
  exports
    begin
      sorts
	boolean
      functions
	true : -> boolean
	false : -> boolean
    end
end boolean

data module list
begin
  parameters
    items
      begin
	sorts
	  item
      end items
  exports
    begin
      sorts
	list
      functions
	emptyl : -> list
	add : item # list -> list
	_+_ : list # list -> list     --concatenation of lists
    end
  variables
    l,l' : -> list
    d : -> item
  equations
    [1] emptyl + l = l
    [2] add(d,l) + l' = l + add(d,l')
end list
  
data module test
begin
  parameters
    test
      begin
	functions
	  test : list -> boolean
      end test
  imports
    list, boolean
end test

data module find-solutions
begin
  exports
    begin
      sorts
	list-of-list
      functions
	emptyll : -> list-of-list
	add : list # list-of-list -> list-of-list
	solve : list -> list-of-list
	--calculate all permutations of the list satisfying the test function
    end
  imports
    test
  sorts
    triple,
    list-of-triple
  functions
    triple : list # list # list -> triple
	     --the disjoint union of these lists has the same elements as
	     --the original list
	     --1: consider the class of permutations starting with this list
	     --2: the next element of the permutation should not be taken from
	     --   this list because these have been tried already
	     --3: the next element of the permutation should be taken from
	     --   this list
    emptylt : -> list-of-triple
    add : triple # list-of-triple -> list-of-triple
    _+_ : list-of-triple # list-of-triple -> list-of-triple  --concatenation
    if : boolean # list-of-triple # list-of-triple -> list-of-triple
    solve : list-of-list # list-of-triple -> list-of-list
	   --auxiliary function that does the job
	   --1: permutations calculated and tested so far (the result list)
	   --2: triples representing the remaining search space

  variables
    k,k',k'' : -> list
    kk : -> list-of-list
    lt,lt' : -> list-of-triple
    d : -> item
    t : -> triple
  equations
    [1] emptylt + lt = lt
    [2] add(t,lt) + lt' = lt + add(t,lt')

    [3] if(true,lt,lt') = lt
    [4] if(false,lt,lt') = lt'

    [5] solve(k) = solve(emptyll, add(triple(emptyl,emptyl,k),emptylt))

    [6] solve(kk,emptylt) = kk
	 --mission completed
    [7] solve(kk,add(triple(k,emptyl,   emptyl),    lt)) = solve(add(k,kk),lt)
	 --new permutation k calculated, add to result list
	 --k has succeeded the test in clause [9]
    [8] solve(kk,add(triple(k,add(d,k'),emptyl),    lt)) = solve(kk,lt)
	 --all permutations starting with k have been considered
    [9] solve(kk,add(triple(k,k',       add(d,k'')),lt)) =
	  solve(kk,
		 if(test(add(d,k)),
		    add(triple(add(d,k),emptyl,k' + k''),emptylt),
		    emptylt) +
	         add(triple(k,add(d,k'),k''), lt)
	      )
	 --calculate all permutations starting with k and continuing with
	 --an element of add(d,k''); continuations from k' have already
	 --been considered
	 --only accept continuations add(d,k) that pass the test
end find-solutions



--Parameters items and test function are bound to sample modules.
data module the-items
begin
  exports
    begin
      sorts
	constant
      functions
	1 : -> constant
	2 : -> constant
	3 : -> constant
	4 : -> constant
	5 : -> constant
    end
end the-items

data module the-test
begin
  exports
    begin
      functions
	test : list -> boolean
    end
  imports
    list {items bound by [item -> constant] to the-items},
    boolean
  variables
    l : -> list
  equations
    [1] test(l) = true --just a sample test function, always returning true
end the-test

data module permuteconstant
begin
  imports
    find-solutions
      {items bound by [item -> constant] to the-items
       test bound by [test -> test] to the-test}
end permuteconstant
