             return false unless ok
         return true
 
+class Labelling
+    constructor: (@in=[], @out=[], @undec=[]) ->
+        # todo: sort arrays on construction? (not unless you can sort on insertion)
+        # check that @in, @out and @undec are disjoint
+        if intersection(@in, @out).length>0 
+            throw new Error('invalid labelling - dup found in in/out')
+        if intersection(@in, @undec).length>0
+            throw new Error('invalid labelling - dup found in in/undec')
+        if intersection(@out, @undec).length>0
+            throw new Error('invalid labelling - dup found in out/undec')
+            
+    # returns true if labelling is the same as this
+    equals: (labelling) ->
+        arrtest = (arr1, arr2) ->
+            arr1.length is arr2.length and arr1[idx] is val for val, idx in arr2 
+        arrtest(@in.sort(), labelling.in.sort()) and 
+            arrtest(@out.sort(), labelling.out.sort()) and 
+            arrtest(@undec.sort(), labelling.undec.sort())
+
+    # returns complement array of the passed array of args and the array of all labelled args
+    complement: (args) ->
+        return complement @undec, complement @out, complement @in, args
+
+    # returns copy of this labelling
+    clone: () ->
+        # note that .slice(0) creates shallow clone of array
+        return new Labelling(@in.slice(0),@out.slice(0),@undec.slice(0))
+
 # abstract class to be extended by particular reasoners
 class Reasoner
     constructor: (@af) ->

# todo: sort arrays on construction?
class Labelling
    constructor: (@in=[], @out=[], @undec=[]) ->
        # todo: check that @in, @out and @undec are disjoint

    # returns true if labelling is the same as this
    equals: (labelling) ->
        arrtest = (arr1, arr2) ->
            arr1.length is arr2.length and arr1[idx] is val for val, idx in arr2 
        arrtest(@in.sort(), labelling.in.sort()) and 
            arrtest(@out.sort(), labelling.out.sort()) and 
            arrtest(@undec.sort(), labelling.undec.sort())

    # returns complement of all labelling args and passed array of args
    complement: (args) ->
        return complement @undec, complement @out, complement @in, args

    # returns copy of this labelling
    clone: () ->
        # note that .slice(0) creates shallow clone of array
        return new Labelling(@in.slice(0),@out.slice(0),@undec.slice(0))

class ArgumentFramework
    # defeatermap: object that maps argument ids to arrays of defeating argument ids
    constructor: (@defeatermap={}) ->

    labellings: () ->
        labelling = new Labelling()
        extendinout = () =>
            others = labelling.complement @af.argids
            added = []
            # extendin
            for arg in others
                # label arg 'in' if all it's defeaters are labelled 'out' (or it has no defeaters)
                if isSubset @af.defeatermap[arg], labelling.out
                    added.push arg
                    labelling.in.push arg
            # extendout
            others = complement(added, others) if added.length>0
            for arg in others
                # label arg 'out' if one of it's defeaters is labelled 'in'
                if @af.isDefeated(arg, labelling.in)
                    labelling.out.push arg
                    added.push arg
            extendinout() if added.length>0
        extendinout()
        labelling.undec = labelling.complement @af.argids
        return [labelling]
        
# returns the array of all sub-arrays of S, see https://gist.github.com/joyrexus/5423644
powerset = (S) ->
    P = [[]]
    P.push P[j].concat S[i] for j of P for i of S
    P

# returns an array whose elements are elements of array B which are not members of array A
complement = (A, B) ->
    (el for el in B when el not in A)

# returns true if all elements of array A are members of array B
isSubset = (A, B) ->
    for el in A
        return false if not (el in B)
    return true

# returns an array whose elements are in both arrays A and B
intersection = (A, B) ->
    result = []
    for el in A
        result.push el if el in B
    result

root = exports ? window
root.Labelling = Labelling
