############################################################################
#
#	File:     mset.icn
#
#	Subject:  Procedures for multi-sets
#
#	Author:   Jan P. de Ruiter
#
#	Date:     January 3, 1994
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
# The idea of the mset type is that no two identical data-structures can be
# present in a set, where identity is defined as "containing the same
# elements".
#
# Definitions implicit in the procedure same_value(..,..):
#
# TYPE              IDENTITY TEST
#
# all types            ===          and if this test fails...
#
# integer               =
# real                  =
# cset, string          ==
# record            all fields have same value
# list              all elements are the same, including ordering
# table             same keys, and every key has the same associated value
# set               contain the same elements
#
############################################################################

#
# This is the core routine.
# It succeeds if two things have the same value(s).
#
procedure same_value(d1,d2)
    if d1 === d2 then return              # same object
    else
    if type(d1) ~== type(d2) then fail  # not the same type
    else
    if *d1 ~= *d2 then fail             # not the same size
    else
    case type(d1)  of {                 # the same type and size
       ("set"  | "table"  ) : return same_elements(sort(d1,1),sort(d2,1))
       ("list")             : return same_elements(d1,d2)
       ("real" | "integer") : return(d1 = d2)
       ("cset" | "string" ) : return(d1 == d2)
       default              : return same_elements(d1,d2) # user defined type
    }
end

#
# used in same_value:
#

procedure same_elements(l1,l2)
   local i
    if l1 === l2 then return   # same objects
    else
    if *l1 ~= *l2 then fail    # not the same size
    else {
        if *l1 = 0 then return # both lists empty
        else {
            every(i := 1 to *l1) do 
                if not same_value(l1[i],l2[i]) then fail  # recursion
            return
        }
    }
end
    
#
# The new insert operation. Insert2 always succeeds
#
procedure insert2(S,el)
    every (if same_value(el,!S) then return)
    return insert(S,el)
end     

#
# The new member operation, that also detects equal-valued elements
#
procedure member2(S,el)
    every(if same_value(!S,el) then return) 
    fail
end

#
# The new delete operation, that detects equal-valued elements.
# Always succeeds
#
procedure delete2(S,el)
    local t
    every(t := !S) do if same_value(t,el) then return delete(S,t)
    return
end

#
# conversion of standard icon set into new mset.
#
procedure reduce2(iset)
    local temp
    temp := set()
    every(insert2(temp,!iset))
    return temp
end

