Set: Difference between revisions

Content added Content deleted
(Added Algol 68)
Line 853: Line 853:
true
true
false</pre>
false</pre>

=={{header|ATS}}==
{{libheader|xxHash}}
The following demonstrates sets of strings stored as a hash-keyed AVL tree. It requires the [http://www.xxhash.net xxHash] C library.

You might notice the operator overloading.
<lang ATS>(*------------------------------------------------------------------*)

#define ATS_DYNLOADFLAG 0

#include "share/atspre_staload.hats"

(*------------------------------------------------------------------*)
(* String hashing using XXH3_64bits from the xxHash suite. *)

#define ATS_EXTERN_PREFIX "hashsets_postiats_"

%{^ /* Embedded C code. */

#include <xxhash.h>

ATSinline() atstype_uint64
hashsets_postiats_mem_hash (atstype_ptr data, atstype_size len)
{
return (atstype_uint64) XXH3_64bits (data, len);
}

%}

extern fn mem_hash : (ptr, size_t) -<> uint64 = "mac#%"

fn
string_hash (s : string) :<> uint64 =
let
val len = string_length s
in
mem_hash ($UNSAFE.cast{ptr} s, len)
end

(*------------------------------------------------------------------*)
(* A trimmed down version of the AVL trees from the AVL Tree task. *)

datatype bal_t =
| bal_minus1
| bal_zero
| bal_plus1

datatype avl_t (key_t : t@ype+,
data_t : t@ype+,
size : int) =
| avl_t_nil (key_t, data_t, 0)
| {size_L, size_R : nat}
avl_t_cons (key_t, data_t, size_L + size_R + 1) of
(key_t, data_t, bal_t,
avl_t (key_t, data_t, size_L),
avl_t (key_t, data_t, size_R))
typedef avl_t (key_t : t@ype+,
data_t : t@ype+) =
[size : int] avl_t (key_t, data_t, size)

extern fun {key_t : t@ype}
avl_t$compare (u : key_t, v : key_t) :<> int

#define NIL avl_t_nil ()
#define CONS avl_t_cons
#define LNIL list_nil ()
#define :: list_cons
#define F false
#define T true

typedef fixbal_t = bool

prfn
lemma_avl_t_param {key_t : t@ype} {data_t : t@ype} {size : int}
(avl : avl_t (key_t, data_t, size)) :<prf>
[0 <= size] void =
case+ avl of NIL => () | CONS _ => ()

fn {}
minus_neg_bal (bal : bal_t) :<> bal_t =
case+ bal of
| bal_minus1 () => bal_plus1
| _ => bal_zero ()

fn {}
minus_pos_bal (bal : bal_t) :<> bal_t =
case+ bal of
| bal_plus1 () => bal_minus1
| _ => bal_zero ()

fn
avl_t_is_empty {key_t : t@ype} {data_t : t@ype} {size : int}
(avl : avl_t (key_t, data_t, size)) :<>
[b : bool | b == (size == 0)] bool b =
case+ avl of
| NIL => T
| CONS _ => F

fn
avl_t_isnot_empty {key_t : t@ype} {data_t : t@ype} {size : int}
(avl : avl_t (key_t, data_t, size)) :<>
[b : bool | b == (size <> 0)] bool b =
~avl_t_is_empty avl

fn {key_t : t@ype} {data_t : t@ype}
avl_t_search_ref {size : int}
(avl : avl_t (key_t, data_t, size),
key : key_t,
data : &data_t? >> opt (data_t, found),
found : &bool? >> bool found) :<!wrt>
#[found : bool] void =
let
fun
search (p : avl_t (key_t, data_t),
data : &data_t? >> opt (data_t, found),
found : &bool? >> bool found) :<!wrt,!ntm>
#[found : bool] void =
case+ p of
| NIL =>
{
prval _ = opt_none {data_t} data
val _ = found := F
}
| CONS (k, d, _, left, right) =>
begin
case+ avl_t$compare<key_t> (key, k) of
| cmp when cmp < 0 => search (left, data, found)
| cmp when cmp > 0 => search (right, data, found)
| _ =>
{
val _ = data := d
prval _ = opt_some {data_t} data
val _ = found := T
}
end
in
$effmask_ntm search (avl, data, found)
end

fn {key_t : t@ype} {data_t : t@ype}
avl_t_search_opt {size : int}
(avl : avl_t (key_t, data_t, size),
key : key_t) :<>
Option (data_t) =
let
var data : data_t?
var found : bool?
val _ = $effmask_wrt avl_t_search_ref (avl, key, data, found)
in
if found then
let
prval _ = opt_unsome data
in
Some {data_t} data
end
else
let
prval _ = opt_unnone data
in
None {data_t} ()
end
end

fn {key_t : t@ype} {data_t : t@ype}
avl_t_insert_or_replace {size : int}
(avl : avl_t (key_t, data_t, size),
key : key_t,
data : data_t) :<>
[sz : pos] (avl_t (key_t, data_t, sz), bool) =
let
fun
search {size : nat}
(p : avl_t (key_t, data_t, size),
fixbal : fixbal_t,
found : bool) :<!ntm>
[sz : pos]
(avl_t (key_t, data_t, sz), fixbal_t, bool) =
case+ p of
| NIL => (CONS (key, data, bal_zero, NIL, NIL), T, F)
| CONS (k, d, bal, left, right) =>
case+ avl_t$compare<key_t> (key, k) of
| cmp when cmp < 0 =>
let
val (p1, fixbal, found) = search (left, fixbal, found)
in
case+ (fixbal, bal) of
| (F, _) => (CONS (k, d, bal, p1, right), F, found)
| (T, bal_plus1 ()) =>
(CONS (k, d, bal_zero (), p1, right), F, found)
| (T, bal_zero ()) =>
(CONS (k, d, bal_minus1 (), p1, right), fixbal, found)
| (T, bal_minus1 ()) =>
let
val+ CONS (k1, d1, bal1, left1, right1) = p1
in
case+ bal1 of
| bal_minus1 () =>
let
val q = CONS (k, d, bal_zero (), right1, right)
val q1 = CONS (k1, d1, bal_zero (), left1, q)
in
(q1, F, found)
end
| _ =>
let
val p2 = right1
val- CONS (k2, d2, bal2, left2, right2) = p2
val q = CONS (k, d, minus_neg_bal bal2,
right2, right)
val q1 = CONS (k1, d1, minus_pos_bal bal2,
left1, left2)
val q2 = CONS (k2, d2, bal_zero (), q1, q)
in
(q2, F, found)
end
end
end
| cmp when cmp > 0 =>
let
val (p1, fixbal, found) = search (right, fixbal, found)
in
case+ (fixbal, bal) of
| (F, _) => (CONS (k, d, bal, left, p1), F, found)
| (T, bal_minus1 ()) =>
(CONS (k, d, bal_zero (), left, p1), F, found)
| (T, bal_zero ()) =>
(CONS (k, d, bal_plus1 (), left, p1), fixbal, found)
| (T, bal_plus1 ()) =>
let
val+ CONS (k1, d1, bal1, left1, right1) = p1
in
case+ bal1 of
| bal_plus1 () =>
let
val q = CONS (k, d, bal_zero (), left, left1)
val q1 = CONS (k1, d1, bal_zero (), q, right1)
in
(q1, F, found)
end
| _ =>
let
val p2 = left1
val- CONS (k2, d2, bal2, left2, right2) = p2
val q = CONS (k, d, minus_pos_bal bal2,
left, left2)
val q1 = CONS (k1, d1, minus_neg_bal bal2,
right2, right1)
val q2 = CONS (k2, d2, bal_zero (), q, q1)
in
(q2, F, found)
end
end
end
| _ => (CONS (key, data, bal, left, right), F, T)
in
if avl_t_is_empty avl then
(CONS (key, data, bal_zero, NIL, NIL), F)
else
let
prval _ = lemma_avl_t_param avl
val (avl, _, found) = $effmask_ntm search (avl, F, F)
in
(avl, found)
end
end

fn {key_t : t@ype} {data_t : t@ype}
avl_t_insert {size : int}
(avl : avl_t (key_t, data_t, size),
key : key_t,
data : data_t) :<>
[sz : pos] avl_t (key_t, data_t, sz) =
(avl_t_insert_or_replace<key_t><data_t> (avl, key, data)).0

fun {key_t : t@ype} {data_t : t@ype}
push_all_the_way_left (stack : List (avl_t (key_t, data_t)),
p : avl_t (key_t, data_t)) :
List0 (avl_t (key_t, data_t)) =
let
prval _ = lemma_list_param stack
in
case+ p of
| NIL => stack
| CONS (_, _, _, left, _) =>
push_all_the_way_left (p :: stack, left)
end

fun {key_t : t@ype} {data_t : t@ype}
update_generator_stack (stack : List (avl_t (key_t, data_t)),
right : avl_t (key_t, data_t)) :
List0 (avl_t (key_t, data_t)) =
let
prval _ = lemma_list_param stack
in
if avl_t_is_empty right then
stack
else
push_all_the_way_left<key_t><data_t> (stack, right)
end

fn {key_t : t@ype} {data_t : t@ype}
avl_t_make_data_generator {size : int}
(avl : avl_t (key_t, data_t, size)) :
() -<cloref1> Option data_t =
let
typedef avl_t = avl_t (key_t, data_t)

val stack = push_all_the_way_left<key_t><data_t> (LNIL, avl)
val stack_ref = ref stack

(* Cast stack_ref to its (otherwise untyped) pointer, so it can be
enclosed within ‘generate’. *)
val p_stack_ref = $UNSAFE.castvwtp0{ptr} stack_ref

fun
generate () :<cloref1> Option data_t =
let
(* Restore the type information for stack_ref. *)
val stack_ref =
$UNSAFE.castvwtp0{ref (List avl_t)} p_stack_ref

var stack : List0 avl_t = !stack_ref
var retval : Option data_t
in
begin
case+ stack of
| LNIL => retval := None ()
| p :: tail =>
let
val- CONS (_, d, _, left, right) = p
in
retval := Some d;
stack :=
update_generator_stack<key_t><data_t> (tail, right)
end
end;
!stack_ref := stack;
retval
end
in
generate
end

(*------------------------------------------------------------------*)
(* Sets implemented with a hash function, AVL trees and association *)
(* lists. *)

(* The interface - - - - - - - - - - - - - - - - - - - - - - - - - *)

(* For simplicity, let us support only 64-bit hashes. *)

typedef hashset_t (key_t : t@ype+) =
avl_t (uint64, List1 key_t)

extern fun {key_t : t@ype} (* Implement a hash function with this. *)
hashset_t$hashfunc : key_t -<> uint64

extern fun {key_t : t@ype} (* Implement key equality with this. *)
hashset_t$key_eq : (key_t, key_t) -<> bool

extern fun
hashset_t_nil :
{key_t : t@ype}
() -<> hashset_t key_t

extern fun {key_t : t@ype}
hashset_t_add_member :
(hashset_t key_t, key_t) -<> hashset_t key_t

(*
"remove_member" is not implemented here, because the trimmed down AVL
tree implementation above does not include deletion. We shall
implement everything else without using a member deletion routine.

extern fun {key_t : t@ype}
hashset_t_remove_member :
(hashset_t key_t, key_t) -<> hashset_t key_t

Of course you can remove a member by using hashset_t_difference.
*)

extern fun {key_t : t@ype}
hashset_t_has_member :
(hashset_t key_t, key_t) -<> bool

typedef hashset_t_binary_operation (key_t : t@ype) =
(hashset_t key_t, hashset_t key_t) -> hashset_t key_t

extern fun {key_t : t@ype}
hashset_t_union : hashset_t_binary_operation key_t

extern fun {key_t : t@ype}
hashset_t_intersection : hashset_t_binary_operation key_t

extern fun {key_t : t@ype}
hashset_t_difference : hashset_t_binary_operation key_t

extern fun {key_t : t@ype}
hashset_t_subset :
(hashset_t key_t, hashset_t key_t) -> bool

extern fun {key_t : t@ype}
hashset_t_equal :
(hashset_t key_t, hashset_t key_t) -> bool

(* Note: generators for hashset_t produce their output in unspecified
order. *)
extern fun {key_t : t@ype}
hashset_t_make_generator :
hashset_t key_t -> () -<cloref1> Option key_t

(* The implementation - - - - - - - - - - - - - - - - - - - - - - - *)

(* I make no promises that these are the most efficient
implementations I could devise. They certainly are not! But they
were easy to write and will work. *)

implement
hashset_t_nil () =
avl_t_nil ()

fun {key_t : t@ype}
find_key {n : nat} .<n>.
(lst : list (key_t, n),
key : key_t) :<>
List0 key_t =
(* This implementation is tail recursive. It will not build up the
stack. *)
case+ lst of
| list_nil () => lst
| list_cons (head, tail) =>
if hashset_t$key_eq<key_t> (key, head) then
lst
else
find_key (tail, key)

implement {key_t}
hashset_t_add_member (set, key) =
(* The following implementation assumes equal keys are
interchangeable. *)
let
implement
avl_t$compare<uint64> (u, v) =
if u < v then ~1 else if v < u then 1 else 0
typedef lst_t = List1 key_t
val hash = hashset_t$hashfunc<key_t> key
val lst_opt = avl_t_search_opt<uint64><lst_t> (set, hash)
in
case+ lst_opt of
| Some lst =>
begin
case+ find_key<key_t> (lst, key) of
| list_cons _ => set
| list_nil () =>
avl_t_insert<uint64><lst_t>
(set, hash, list_cons (key, lst))
end
| None () =>
avl_t_insert<uint64><lst_t>
(set, hash, list_cons (key, list_nil ()))
end

implement {key_t}
hashset_t_has_member (set, key) =
let
implement
avl_t$compare<uint64> (u, v) =
if u < v then ~1 else if v < u then 1 else 0
typedef lst_t = List1 key_t
val hash = hashset_t$hashfunc<key_t> key
val lst_opt = avl_t_search_opt<uint64><lst_t> (set, hash)
in
case+ lst_opt of
| None () => false
| Some lst =>
begin
case+ find_key<key_t> (lst, key) of
| list_nil () => false
| list_cons _ => true
end
end

implement {key_t}
hashset_t_union (u, v) =
let
val gen_u = hashset_t_make_generator<key_t> u
val gen_v = hashset_t_make_generator<key_t> v
var w : hashset_t key_t = hashset_t_nil ()
var k_opt : Option key_t
in
for (k_opt := gen_u (); option_is_some k_opt; k_opt := gen_u ())
w := hashset_t_add_member (w, option_unsome k_opt);
for (k_opt := gen_v (); option_is_some k_opt; k_opt := gen_v ())
w := hashset_t_add_member (w, option_unsome k_opt);
w
end

implement {key_t}
hashset_t_intersection (u, v) =
let
val gen_u = hashset_t_make_generator<key_t> u
var w : hashset_t key_t = hashset_t_nil ()
var k_opt : Option key_t
in
for (k_opt := gen_u (); option_is_some k_opt; k_opt := gen_u ())
let
val+ Some k = k_opt
in
if hashset_t_has_member<key_t> (v, k) then
w := hashset_t_add_member (w, k)
end;
w
end

implement {key_t}
hashset_t_difference (u, v) =
let
val gen_u = hashset_t_make_generator<key_t> u
var w : hashset_t key_t = hashset_t_nil ()
var k_opt : Option key_t
in
for (k_opt := gen_u (); option_is_some k_opt; k_opt := gen_u ())
let
val+ Some k = k_opt
in
if ~hashset_t_has_member<key_t> (v, k) then
w := hashset_t_add_member (w, k)
end;
w
end

implement {key_t}
hashset_t_subset (u, v) =
let
val gen_u = hashset_t_make_generator<key_t> u
var subset : bool = true
var done : bool = false
in
while (~done)
case+ gen_u () of
| None () => done := true
| Some k =>
if ~hashset_t_has_member<key_t> (v, k) then
begin
subset := false;
done := true
end;
subset
end

implement {key_t}
hashset_t_equal (u, v) =
hashset_t_subset<key_t> (u, v)
&& hashset_t_subset<key_t> (v, u)

implement {key_t}
hashset_t_make_generator (set) =
let
typedef lst_t = List1 key_t
typedef lst_t_0 = List0 key_t

val avl_gen = avl_t_make_data_generator<uint64><lst_t> (set)

val current_list_ref : ref lst_t_0 = ref (list_nil ())
val current_list_ptr =
$UNSAFE.castvwtp0{ptr} current_list_ref
in
lam () =>
let
val current_list_ref =
$UNSAFE.castvwtp0{ref lst_t_0} current_list_ptr
in
case+ !current_list_ref of
| list_nil () =>
begin
case+ avl_gen () of
| None () => None ()
| Some lst =>
begin
case+ lst of
| list_cons (head, tail) =>
begin
!current_list_ref := tail;
Some head
end
end
end
| list_cons (head, tail) =>
begin
!current_list_ref := tail;
Some head
end
end
end

(*------------------------------------------------------------------*)

implement
hashset_t$hashfunc<string> (s) =
string_hash s

implement
hashset_t$key_eq<string> (s, t) =
s = t

typedef strset_t = hashset_t string

fn {}
strset_t_nil () :<> strset_t =
hashset_t_nil ()

fn
strset_t_add_member (set : strset_t,
member : string) :<> strset_t =
hashset_t_add_member<string> (set, member)

fn {}
strset_t_member_add (member : string,
set : strset_t) :<> strset_t =
strset_t_add_member (set, member)

#define SNIL strset_t_nil ()
infixr ( :: ) ++ (* Right associative, same precedence as :: *)
overload ++ with strset_t_member_add

fn
strset_t_has_member (set : strset_t,
member : string) :<> bool =
hashset_t_has_member<string> (set, member)
overload [] with strset_t_has_member

fn
strset_t_union (u : strset_t, v : strset_t) : strset_t =
hashset_t_union<string> (u, v)
overload + with strset_t_union

fn
strset_t_intersection (u : strset_t, v : strset_t) : strset_t =
hashset_t_intersection<string> (u, v)
infixl ( + ) ^
overload ^ with strset_t_intersection

fn
strset_t_difference (u : strset_t, v : strset_t) : strset_t =
hashset_t_difference<string> (u, v)
overload - with strset_t_difference

fn
strset_t_subset (u : strset_t, v : strset_t) : bool =
hashset_t_subset<string> (u, v)
overload <= with strset_t_subset

fn
strset_t_equal (u : strset_t, v : strset_t) : bool =
hashset_t_equal<string> (u, v)
overload = with strset_t_equal

fn
strset_t_make_generator (set : strset_t) :
() -<cloref1> Option string =
hashset_t_make_generator<string> set

fn
strset_t_print (set : strset_t) : void =
let
val gen = strset_t_make_generator set
var s_opt : Option string
var separator : string = ""
in
print! ("#<strset_t ");
for (s_opt := gen (); option_is_some s_opt; s_opt := gen ())
case+ s_opt of
| Some s =>
begin
(* The following quick and dirty implemenetation does not
insert escape sequences. *)
print! (separator, "\"", s, "\"");
separator := " "
end;
print! (">")
end

implement
main0 () =
let
val set1 =
"one" ++ "two" ++ "three" ++ "guide" ++ "design" ++ SNIL
val set2 =
"ett" ++ "två" ++ "tre" ++ "guide" ++ "design" ++ SNIL
in
print! ("set1 = ");
strset_t_print set1;

println! ();
println! ();
println! ("set1[\"one\"] = ", set1["one"]);
println! ("set1[\"two\"] = ", set1["two"]);
println! ("set1[\"three\"] = ", set1["three"]);
println! ("set1[\"four\"] = ", set1["four"]);

println! ();
print! ("set2 = ");
strset_t_print set2;

println! ();
println! ();
println! ("set2[\"ett\"] = ", set2["ett"]);
println! ("set2[\"två\"] = ", set2["två"]);
println! ("set2[\"tre\"] = ", set2["tre"]);
println! ("set2[\"fyra\"] = ", set2["fyra"]);

println! ();
print! ("Union\nset1 + set2 = ");
strset_t_print (set1 + set2);
println! ();
println! ();
print! ("Intersection\nset1 ^ set2 = ");
strset_t_print (set1 ^ set2);
println! ();
println! ();
print! ("Difference\nset1 - set2 = ");
strset_t_print (set1 - set2);
println! ();

println! ();
println! ("Subset");
println! ("set1 <= set1: ", set1 <= set1);
println! ("set2 <= set2: ", set2 <= set2);
println! ("set1 <= set2: ", set1 <= set2);
println! ("set2 <= set1: ", set2 <= set1);
println! ("(set1 ^ set2) <= set1: ", (set1 ^ set2) <= set1);
println! ("(set1 ^ set2) <= set2: ", (set1 ^ set2) <= set2);

println! ();
println! ("Equal");
println! ("set1 = set1: ", set1 = set1);
println! ("set2 = set2: ", set2 = set2);
println! ("set1 = set2: ", set1 = set2);
println! ("set2 = set1: ", set2 = set1);
println! ("(set1 ^ set2) = (set2 ^ set1): ",
(set1 ^ set2) = (set2 ^ set1))
end

(*------------------------------------------------------------------*)</lang>

{{out}}
<pre>$ patscc -O2 -DATS_MEMALLOC_GCBDW hashsets-postiats.dats -lxxhash -lgc && ./a.out
set1 = #<strset_t "guide" "design" "two" "one" "three">

set1["one"] = true
set1["two"] = true
set1["three"] = true
set1["four"] = false

set2 = #<strset_t "två" "guide" "design" "ett" "tre">

set2["ett"] = true
set2["två"] = true
set2["tre"] = true
set2["fyra"] = false

Union
set1 + set2 = #<strset_t "två" "guide" "design" "two" "ett" "one" "three" "tre">

Intersection
set1 ^ set2 = #<strset_t "guide" "design">

Difference
set1 - set2 = #<strset_t "two" "one" "three">

Subset
set1 <= set1: true
set2 <= set2: true
set1 <= set2: false
set2 <= set1: false
(set1 ^ set2) <= set1: true
(set1 ^ set2) <= set2: true

Equal
set1 = set1: true
set2 = set2: true
set1 = set2: false
set2 = set1: false
(set1 ^ set2) = (set2 ^ set1): true</pre>




=={{header|AutoHotkey}}==
=={{header|AutoHotkey}}==