module namespace set = "http://qt4cg.org/atomic-set";
   
   (:
      This package defines a type set:atomic-set which represents
      a set of distinct atomic items. Atomic items are considered
      distinct based on the comparison function fn:atomic-equal.
      
      An instance of an atomic set can be constructed using a function
      call such as set:build((1, 3, 5, 7, 9)).
      
      If $A and $B are instances of set:atomic-set, then they
      can be manipulated using methods including:
      
      $A=?>size() - returns the number of items in the set
      $A=?>empty() - returns true if the set is empty
      $A=?>contains($k) - determines whether $k is a member of the set
      $A=?>contains-all($B) - returns true if $B is a subset of $A
      $A=?>values() - returns the items in $A, as a sequence
      $A=?>add($k) - returns a new atomic set containing an additional item
      $A=?>remove($k) - returns a new atomic set in which the given item is absent
      $A=?>union($B) - returns a new atomic set holding the union of $A and $B
      $A=?>intersect($B) - returns a new atomic set holding the intersection of $A and $B
      $A=?>except($B) - returns a new atomic set holding the difference of $A and $B
   :)
   
   declare %public record set:atomic-set (
       _data        as map(xs:anyAtomicType, xs:boolean),
       size         as fn($set as set:atomic-set) as xs:integer,
       empty        as fn($set as set:atomic-set) as xs:boolean,
       contains     as fn($set as set:atomic-set, $value as xs:anyAtomicType) as xs:boolean,
       contains-all as fn($set as set:atomic-set, $value as set:atomic-set) as xs:boolean,
       add          as fn($set as set:atomic-set, $item as xs:anyAtomicType) as set:atomic-set,
       remove       as fn($set as set:atomic-set, $item as xs:anyAtomicType) as set:atomic-set,
       union        as fn($set as set:atomic-set, $value as set:atomic-set) as set:atomic-set,
       intersect    as fn($set as set:atomic-set, $value as set:atomic-set) as set:atomic-set,
       except       as fn($set as set:atomic-set, $value as set:atomic-set) as set:atomic-set,
       * );

   declare %private variable DATA := "'_data'"; 
   
   (:
      The private function set:replaceData processes the internal map
      by applying a supplied function, and returns a new atomic set
      with the resulting internal map 
   :)
   
   declare %private function set:replaceData (
       $input as set:atomic-set,
       $update as fn(map(*)) as map(*)) as map(xs:anyAtomicType, xs:boolean) {
       
       map:put($input, $DATA, $update($input?$DATA))
   }
   
   declare %public function set:build (
       $values as xs:anyAtomicType* := ()) {
       
       {
         _data:
             map:build($values, values:=true#0, {'duplicates': 'use-first'}),
         size: fn($set as set:atomic-set) as xs:integer 
                     { map:size($set?$DATA) },
         empty: fn($set as set:atomic-set) as xs:boolean 
                     { map:empty($set?$DATA) },
         contains: fn($set as set:atomic-set, $value as xs:anyAtomicType) as xs:boolean 
                     { map:contains($set?$DATA, $value) },
         contains-all: fn($set as set:atomic-set, $other as set:atomic-set) as xs:boolean 
                     { every($other, map:contains($set?$DATA, ?)) },
         values: fn($set as set:atomic-set) as xs:anyAtomicType* 
                     { keys($set?$DATA) },
         add: fn($set as set:atomic-set, $value as xs:anyAtomicType) as xs:anyAtomicType*
                     { set:replaceData($set, map:put(?, $value, true())) },
         remove: fn($set as set:atomic-set, $value as xs:anyAtomicType) as xs:anyAtomicType* 
                     { set:replaceData($set, map:remove(?, $value)) },
         union: fn($set as set:atomic-set, $other as set:atomic-set) as set:atomic-set 
                     { set:replaceData($set, fn($this) {map:merge(($this, $other?$DATA),
                                                    {'duplicates': 'use-first'})})
                     },
         intersect: fn($set as set:atomic-set, $other as set:atomic-set) as set:atomic-set 
                     { set:replaceData($set, map:filter(?, $other?contains)) },
         except: fn($set as set:atomic-set, $other as set:atomic-set) as set:atomic-set 
                     { set:replaceData($set, map:remove(?, $other?values())) }
      }
   };