open Common

open Ocollection

class virtual ['a] oset =
object(o: 'o)
  inherit ['a] ocollection

  (* no need virtual, but better to redefine (efficiency) *)
  method virtual union: 'o -> 'o
  method virtual inter: 'o -> 'o
  method virtual minus: 'o -> 'o

  (* allow binary methods tricks, generate exception when not good type *)
  method  tosetb: 'a Setb.t = raise (Impossible 10)
  method  tosetpt: SetPt.t = raise (Impossible 11)
  method  toseti: Seti.seti    = raise (Impossible 12)
  method virtual toset: 'b. 'b (* generic (not safe) tricks *)

  (* is_intersect, equal, subset *)
  method is_subset_of: 'o -> bool = fun o2 ->
    ((o2#minus o)#cardinal >= 0) && ((o#minus o2)#cardinal = 0)

  method is_equal: 'o -> bool = fun o2 ->
    ((o2#minus o)#cardinal = 0) && ((o#minus o2)#cardinal = 0)


  method is_singleton: bool = (* can be short circuited *)
    o#length = 1
  method cardinal: int = (* just to keep naming conventions *)
    o#length
      (* don't work:
         method big_union: 'b. ('a -> 'b oset) -> 'b oset = fun f -> todo()
      *)

end

let ($??$) e xs = xs#mem e
let ($++$) xs ys = xs#union ys
let ($**$) xs ys = xs#inter ys
let ($--$) xs ys = xs#minus ys
let ($<<=$) xs ys = xs#is_subset_of ys
let ($==$) xs ys = xs#is_equal ys

(* todo: pas beau le seed.  I don't put the type otherwise have to
 * put explicit :>
 *)
let (mapo: ('a -> 'b) -> 'b oset -> 'a oset -> 'b oset) = fun f seed xs ->
  xs#fold (fun acc x -> acc#add (f x)) seed
