diff --git a/gen.ml b/gen.ml index 4c5374ac..494e8586 100644 --- a/gen.ml +++ b/gen.ml @@ -25,262 +25,427 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (** {1 Restartable generators} *) +(** {2 Global type declarations} *) + exception EOG (** End of Generation *) -type 'a t = unit -> 'a generator - (** An enum is a generator of generators *) -and 'a generator = unit -> 'a +type 'a t = unit -> 'a (** A generator may be called several times, yielding the next value each time. It raises EOG when it reaches the end. *) -(** {2 Generator functions} *) +type 'a gen = 'a t -let start enum = enum () +(** {2 Common signature for transient and restartable generators} *) + +module type S = sig + type 'a t + + val empty : 'a t + (** Empty generator, with no elements *) + + val singleton : 'a -> 'a t + (** One-element generator *) + + val repeat : 'a -> 'a t + (** Repeat same element endlessly *) + + val iterate : 'a -> ('a -> 'a) -> 'a t + (** [iterate x f] is [[x; f x; f (f x); f (f (f x)); ...]] *) + + val unfold : ('b -> ('a * 'b) option) -> 'b -> 'a t + (** Dual of {!fold}, with a deconstructing operation. It keeps on + unfolding the ['b] value into a new ['b], and a ['a] which is yielded, + until [None] is returned. *) + + (** {2 Basic combinators} *) + + val is_empty : _ t -> bool + (** Check whether the enum is empty. *) + + val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b + (** Fold on the generator, tail-recursively *) + + val fold2 : ('c -> 'a -> 'b -> 'c) -> 'c -> 'a t -> 'b t -> 'c + (** Fold on the two enums in parallel. Stops once one of the enums + is exhausted. *) + + val reduce : ('a -> 'a -> 'a) -> 'a t -> 'a + (** Fold on non-empty sequences (otherwise raise Invalid_argument) *) + + val scan : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b t + (** Like {!fold}, but keeping successive values of the accumulator *) + + val iter : ('a -> unit) -> 'a t -> unit + (** Iterate on the enum *) + + val iteri : (int -> 'a -> unit) -> 'a t -> unit + (** Iterate on elements with their index in the enum, from 0 *) + + val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit + (** Iterate on the two sequences. Stops once one of them is exhausted.*) + + val length : _ t -> int + (** Length of an enum (linear time) *) + + val map : ('a -> 'b) -> 'a t -> 'b t + (** Lazy map. No iteration is performed now, the function will be called + when the result is traversed. *) + + val append : 'a t -> 'a t -> 'a t + (** Append the two enums; the result contains the elements of the first, + then the elements of the second enum. *) + + val flatten : 'a gen t -> 'a t + (** Flatten the enumeration of generators *) + + val flatMap : ('a -> 'b gen) -> 'a t -> 'b t + (** Monadic bind; each element is transformed to a sub-enum + which is then iterated on, before the next element is processed, + and so on. *) + + val mem : ?eq:('a -> 'a -> bool) -> 'a -> 'a t -> bool + (** Is the given element, member of the enum? *) + + val take : int -> 'a t -> 'a t + (** Take at most n elements *) + + val drop : int -> 'a t -> 'a t + (** Drop n elements *) + + val nth : int -> 'a t -> 'a + (** n-th element, or Not_found + @raise Not_found if the generator contains less than [n] arguments *) + + val filter : ('a -> bool) -> 'a t -> 'a t + (** Filter out elements that do not satisfy the predicate. *) + + val takeWhile : ('a -> bool) -> 'a t -> 'a t + (** Take elements while they satisfy the predicate *) + + val dropWhile : ('a -> bool) -> 'a t -> 'a t + (** Drop elements while they satisfy the predicate *) + + val filterMap : ('a -> 'b option) -> 'a t -> 'b t + (** Maps some elements to 'b, drop the other ones *) + + val zipWith : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t + (** Combine common part of the enums (stops when one is exhausted) *) + + val zip : 'a t -> 'b t -> ('a * 'b) t + (** Zip together the common part of the enums *) + + val zipIndex : 'a t -> (int * 'a) t + (** Zip elements with their index in the enum *) + + val unzip : ('a * 'b) t -> 'a t * 'b t + (** Unzip into two sequences, splitting each pair *) + + val partition : ('a -> bool) -> 'a t -> 'a t * 'a t + (** [partition p l] returns the elements that satisfy [p], + and the elements that do not satisfy [p] *) + + val for_all : ('a -> bool) -> 'a t -> bool + (** Is the predicate true for all elements? *) + + val exists : ('a -> bool) -> 'a t -> bool + (** Is the predicate true for at least one element? *) + + val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool + + val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool + + val min : ?lt:('a -> 'a -> bool) -> 'a t -> 'a + (** Minimum element, according to the given comparison function *) + + val max : ?lt:('a -> 'a -> bool) -> 'a t -> 'a + (** Maximum element, see {!min} *) + + val eq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t -> bool + (** Equality of generators. *) + + val lexico : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> int + (** Lexicographic comparison of generators. If the common prefix is + the same, the shortest one is considered as smaller than the other. *) + + val compare : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> int + (** Synonym for {! lexico} *) + + (** {2 Complex combinators} *) + + val merge : 'a gen t -> 'a t + (** Pick elements fairly in each sub-generator. The given enum + must be finite (not its elements, though). The merge of enums + [e1, e2, ... en] picks one element in [e1], then one element in [e2], + then in [e3], ..., then in [en], and then starts again at [e1]. Once + a generator is empty, it is skipped; when they are all empty, + their merge is also empty. + For instance, [merge [1;3;5] [2;4;6]] will be [1;2;3;4;5;6]. *) + + val intersection : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> 'a t + (** Intersection of two sorted sequences. Only elements that occur in both + inputs appear in the output *) + + val sorted_merge : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> 'a t + (** Merge two sorted sequences into a sorted sequence *) + + val sorted_merge_n : ?cmp:('a -> 'a -> int) -> 'a gen t -> 'a t + (** Sorted merge of multiple sorted sequences *) + + val tee : ?n:int -> 'a t -> 'a gen list + (** Duplicate the enum into [n] generators (default 2). The generators + share the same underlying instance of the enum, so the optimal case is + when they are consumed evenly *) + + val round_robin : ?n:int -> 'a t -> 'a gen list + (** Split the enum into [n] generators in a fair way. Elements with + [index = k mod n] with go to the k-th enum. [n] default value + is 2. *) + + val interleave : 'a t -> 'a t -> 'a t + (** [interleave a b] yields an element of [a], then an element of [b], + and so on until the end of [a] or [b] is reached. *) + + val intersperse : 'a -> 'a t -> 'a t + (** Put the separator element between all elements of the given enum *) + + val product : 'a t -> 'b t -> ('a * 'b) t + (** Cartesian product, in no predictable order. Works even if some of the + arguments are infinite. *) + + val group : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t + (** Group equal consecutive elements together. *) + + val uniq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t + (** Remove consecutive duplicate elements. Basically this is + like [fun e -> map List.hd (group e)]. *) + + val sort : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t + (** Sort according to the given comparison function. The enum must be finite. *) + + val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t + (** Sort and remove duplicates. The enum must be finite. *) + + (* TODO later + val permutations : 'a t -> 'a gen t + (** Permutations of the enum. Each permutation becomes unavailable once + the next one is produced. *) + + val combinations : int -> 'a t -> 'a t t + (** Combinations of given length. *) + + val powerSet : 'a t -> 'a t t + (** All subsets of the enum (in no particular order) *) + *) + + (** {2 Basic conversion functions} *) + + val of_list : 'a list -> 'a t + (** Enumerate elements of the list *) + + val to_list : 'a t -> 'a list + (** non tail-call trasnformation to list, in the same order *) + + val to_rev_list : 'a t -> 'a list + (** Tail call conversion to list, in reverse order (more efficient) *) + + val to_array : 'a t -> 'a array + (** Convert the enum to an array (not very efficient) *) + + val of_array : ?start:int -> ?len:int -> 'a array -> 'a t + (** Iterate on (a slice of) the given array *) + + val rand_int : int -> int t + (** Random ints in the given range. *) + + val int_range : int -> int -> int t + (** [int_range a b] enumerates integers between [a] and [b], included. [a] + is assumed to be smaller than [b]. *) + + module Infix : sig + val (--) : int -> int -> int t + (** Synonym for {! int_range} *) + + val (>>=) : 'a t -> ('a -> 'b gen) -> 'b t + (** Monadic bind operator *) + end + + val (--) : int -> int -> int t + (** Synonym for {! int_range} *) + + val (>>=) : 'a t -> ('a -> 'b gen) -> 'b t + (** Monadic bind operator *) + + val pp : ?start:string -> ?stop:string -> ?sep:string -> ?horizontal:bool -> + (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit + (** Pretty print the content of the generator on a formatter. *) +end (** {2 Transient generators} *) -module Gen = struct - let empty () = raise EOG - - let next gen = gen () - - let junk gen = ignore (gen ()) - - let fold f acc gen = - let acc = ref acc in - (try - while true do acc := f !acc (gen ()) done - with EOG -> ()); - !acc - - let iter f gen = - try - while true do f (gen ()) done - with EOG -> - () - - let length gen = - fold (fun acc _ -> acc + 1) 0 gen - - let of_list l = - let l = ref l in - fun () -> - match !l with - | [] -> raise EOG - | x::l' -> l := l'; x - - (* non-tailrec construction of (small) list *) - let to_list gen = - let rec fold () = - try - let x = gen () in - x :: fold () - with EOG -> [] - in fold () - - let to_rev_list gen = - fold (fun acc x -> x :: acc) [] gen - - let int_range i j = - let r = ref i in - fun () -> - let x = !r in - if x > j then raise EOG - else begin - incr r; - x - end -end - -(** {2 Basic constructors} *) - -let empty () = fun () -> raise EOG +let empty () = raise EOG let singleton x = + let first = ref true in fun () -> - let stop = ref false in - fun () -> - if !stop - then raise EOG - else begin stop := true; x end + if !first then (first := false; x) else raise EOG -let repeat x = - let f () = x in - fun () -> f +let rec repeat x () = x -let repeatedly f = - fun () -> - fun () -> - f () +let repeatedly f () = f () -(** [iterate x f] is [[x; f x; f (f x); f (f (f x)); ...]] *) let iterate x f = + let cur = ref x in fun () -> - let acc = ref x in - fun () -> - let cur = !acc in - acc := f cur; - cur + let x = !cur in + cur := f !cur; + x -(** Dual of {!fold}, with a deconstructing operation *) +let next gen = gen () + +let get gen = gen () + +let get_safe gen = + try Some (gen ()) + with EOG -> None + +let junk gen = ignore (gen ()) + +let rec fold f acc gen = + let acc, stop = + try f acc (gen ()), false + with EOG -> acc, true + in + if stop then acc else fold f acc gen + +let rec fold2 f acc e1 e2 = + let acc, stop = + try f acc (e1()) (e2()), false + with EOG -> acc, true + in + if stop then acc else fold2 f acc e1 e2 + +let reduce f g = + let acc = try g () with EOG -> raise (Invalid_argument "reduce") in + fold f acc g + +(* Dual of {!fold}, with a deconstructing operation *) let unfold f acc = + let acc = ref acc in fun () -> - let acc = ref acc in - fun () -> - match f !acc with - | None -> raise EOG - | Some (x, acc') -> - acc := acc'; - x + match f !acc with + | None -> raise EOG + | Some (x, acc') -> + acc := acc'; + x -(** {2 Basic combinators} *) +let iter f gen = + try + while true do f (gen ()) done + with EOG -> + () + +let iteri f gen = + let n = ref 0 in + try + while true do f !n (gen ()); incr n done + with EOG -> + () let is_empty enum = - try ignore ((enum ()) ()); false + try ignore (enum ()); false with EOG -> true -let fold f acc enum = - Gen.fold f acc (enum ()) +let length gen = + fold (fun acc _ -> acc + 1) 0 gen -let fold2 f acc e1 e2 = +let scan f acc g = let acc = ref acc in - let gen1 = e1 () and gen2 = e2 () in - (try - while true do acc := f !acc (gen1 ()) (gen2 ()) done - with EOG -> ()); - !acc - -let reduce f enum = - let gen = enum () in - let acc = try gen () with EOG -> raise (Invalid_argument "reduce") in - Gen.fold f acc gen - -(** Successive values of the accumulator *) -let scan f acc e = + let first = ref true in fun () -> - let acc = ref acc in - let first = ref true in - let gen = e () in - fun () -> - if !first - then (first := false; !acc) - else begin - acc := f !acc (gen ()); - !acc - end + if !first + then (first := false; !acc) + else begin + acc := f !acc (g ()); + !acc + end -let iter f enum = - Gen.iter f (enum ()) - -(** Iterate on elements with their index in the enum, from 0 *) -let iteri f enum = - let r = ref 0 in - let gen = enum () in - try - while true do f !r (gen ()); incr r; done - with EOG -> () - -let iter2 f e1 e2 = - let gen1 = e1 () and gen2 = e2 () in +let iter2 f gen1 gen2 = try while true do f (gen1 ()) (gen2 ()) done; with EOG -> () -let length enum = - Gen.length (enum ()) - -let map f enum = - (* another enum *) - fun () -> - let gen = enum () in - (* the mapped generator *) - fun () -> - f (gen ()) +(** {3 Lazy} *) -let append e1 e2 = - fun () -> - let gen = ref (e1 ()) in - let first = ref true in - (* get next element *) - let rec next () = - try !gen () - with EOG -> - if !first then begin - first := false; - gen := e2 (); (* switch to the second generator *) - next () - end else raise EOG (* done *) - in next +let map f gen () = + f (gen ()) -let cycle enum = - assert (not (is_empty enum)); - fun () -> - let gen = ref (enum ()) in - let rec next () = - try !gen () - with EOG -> - gen := enum (); +let append gen1 gen2 = + let gen = ref gen1 in + let first = ref true in + (* get next element *) + let rec next () = + try !gen () + with EOG -> + if !first then begin + first := false; + gen := gen2; (* switch to the second generator *) next () - in next + end else raise EOG (* done *) + in next -let flatten enum = - fun () -> - let next_gen = enum () in - let gen = ref Gen.empty in - (* get next element *) - let rec next () = - try !gen () - with EOG -> - (* jump to next sub-enum *) - gen := (next_gen ()) (); - next () - in next - -let flatMap f enum = - fun () -> - let next_elem = enum () in - let gen = ref Gen.empty in - (* get next element *) - let rec next () = - try !gen () - with EOG -> - (* enumerate f (next element) *) - let x = next_elem () in - gen := (f x) (); - next () (* try again, with [gen = f x] *) - in next +let flatten next_gen = + let gen = ref empty in + (* get next element *) + let rec next () = + try !gen () + with EOG -> + (* jump to next sub-enum *) + gen := next_gen (); + next () + in next -let mem ?(eq=(=)) x enum = +let flatMap f next_elem = + let gen = ref empty in + (* get next element *) + let rec next () = + try !gen () + with EOG -> + (* enumerate f (next element) *) + let x = next_elem () in + gen := f x; + next () (* try again, with [gen = f x] *) + in next + +let mem ?(eq=(=)) x gen = try - iter (fun y -> if eq x y then raise Exit) enum; + iter (fun y -> if eq x y then raise Exit) gen; false with Exit -> true -let take n enum = +let take n gen = assert (n >= 0); + let count = ref 0 in (* how many yielded elements *) fun () -> - let gen = enum () in - let count = ref 0 in (* how many yielded elements *) - fun () -> - if !count = n then raise EOG - else begin incr count; gen () end + if !count = n then raise EOG + else begin incr count; gen () end -let drop n enum = +let drop n gen = assert (n >= 0); + let dropped = ref false in fun () -> - let gen = enum () in - let count = ref 0 in (* how many droped elements? *) - let rec next () = - if !count < n - then begin incr count; Gen.junk gen; next () end - else gen () - in next + if !dropped + then gen() + else begin + (* drop [n] elements and yield the next element *) + dropped := true; + for i = 0 to n-1 do ignore (gen()) done; + gen() + end -let nth n enum = +let nth n gen = assert (n>=0); - let gen = enum () in let rec iter i = let x = gen () in if n = i then x else iter (i+1) @@ -288,90 +453,122 @@ let nth n enum = try iter 0 with EOG -> raise Not_found -let filter p enum = - fun () -> - let gen = enum () in - let rec next () = - (* wrap exception into option, for next to be tailrec *) - match (try Some (gen ()) with EOG -> None) with - | None -> raise EOG - | Some x -> - if p x - then x (* yield element *) - else next () (* discard element *) - in next +let filter p gen = + let rec next () = + (* wrap exception into option, for next to be tailrec *) + match (try Some (gen ()) with EOG -> None) with + | None -> raise EOG + | Some x -> + if p x + then x (* yield element *) + else next () (* discard element *) + in next -let takeWhile p enum = - fun () -> - let gen = enum () in - let rec next () = - let x = gen () in - if p x then x else raise EOG - in next +let takeWhile p gen = + let rec next () = + let x = gen () in + if p x then x else raise EOG + in next -let dropWhile p enum = - fun () -> - let gen = enum () in - let stop_drop = ref false in - let rec next () = - let x = gen () in - if !stop_drop - then x (* yield *) - else if p x - then next () (* continue dropping *) - else (stop_drop := true; x) (* stop dropping *) - in next +let dropWhile p gen = + let stop_drop = ref false in + let rec next () = + let x = gen () in + if !stop_drop + then x (* yield *) + else if p x + then next () (* continue dropping *) + else (stop_drop := true; x) (* stop dropping *) + in next -let filterMap f enum = - fun () -> - let gen = enum () in - (* tailrec *) - let rec next () = - let x = gen () in - match f x with - | None -> next () - | Some y -> y - in next +let filterMap f gen = + (* tailrec *) + let rec next () = + let x = gen () in + match f x with + | None -> next () + | Some y -> y + in next let zipWith f a b = + fun () -> f (a()) (b()) + +let zip a b = + fun () -> a(), b() + +let zipIndex gen = + let r = ref 0 in fun () -> - let gen_a = a () in - let gen_b = b () in - fun () -> - f (gen_a ()) (gen_b ()) (* combine elements *) + let x = gen () in + let n = !r in + incr r; + n, x -let zip a b = zipWith (fun x y -> x,y) a b +let unzip gen = + let stop = ref false in + let q1 = Queue.create () in + let q2 = Queue.create () in + let next_left () = + if Queue.is_empty q1 + then if !stop then raise EOG + else try + let x, y = gen() in + Queue.push y q2; + x + with EOG -> stop := true; raise EOG + else Queue.pop q1 + in + let next_right () = + if Queue.is_empty q2 + then if !stop then raise EOG + else try + let x, y = gen() in + Queue.push x q1; + y + with EOG -> stop := true; raise EOG + else Queue.pop q2 + in + next_left, next_right -let zipIndex enum = - fun () -> - let r = ref 0 in - let gen = enum () in - fun () -> - let x = gen () in - let n = !r in - incr r; - n, x +(* [partition p l] returns the elements that satisfy [p], + and the elements that do not satisfy [p] *) +let partition p gen = + let qtrue = Queue.create () in + let qfalse = Queue.create () in + let stop = ref false in + let rec nexttrue () = + if Queue.is_empty qtrue + then if !stop then raise EOG + else try + let x = gen() in + if p x then x else (Queue.push x qfalse; nexttrue()) + with EOG -> stop:=true; raise EOG + else Queue.pop qtrue + and nextfalse() = + if Queue.is_empty qfalse + then if !stop then raise EOG + else try + let x = gen() in + if p x then (Queue.push x qtrue; nextfalse()) else x + with EOG -> stop:= true; raise EOG + else Queue.pop qfalse + in + nexttrue, nextfalse -let unzip e = - map fst e, map snd e +exception GenExit -(** [partition p l] returns the elements that satisfy [p], - and the elements that do not satisfy [p] *) -let partition p enum = - filter p enum, filter (fun x -> not (p x)) enum - -let for_all p enum = +let for_all p gen = try - iter (fun x -> if not (p x) then raise Exit) enum; + iter (fun x -> if not (p x) then raise GenExit) gen; true - with Exit -> + with GenExit -> false -let exists p enum = +let exists p gen = try - iter (fun x -> if p x then raise Exit) enum; + iter (fun x -> if p x then raise GenExit) gen; false - with Exit -> + with GenExit -> true let for_all2 p e1 e2 = @@ -388,19 +585,15 @@ let exists2 p e1 e2 = with Exit -> true -let min ?(lt=fun x y -> x < y) enum = - let gen = enum () in +let min ?(lt=fun x y -> x < y) gen = let first = try gen () with EOG -> raise Not_found in - Gen.fold (fun min x -> if lt x min then x else min) first gen + fold (fun min x -> if lt x min then x else min) first gen -let max ?(lt=fun x y -> x < y) enum = - let gen = enum () in +let max ?(lt=fun x y -> x < y) gen = let first = try gen () with EOG -> raise Not_found in - Gen.fold (fun max x -> if lt max x then x else max) first gen + fold (fun max x -> if lt max x then x else max) first gen -let eq ?(eq=(=)) e1 e2 = - let gen1 = e1 () - and gen2 = e2 () in +let eq ?(eq=(=)) gen1 gen2 = let rec check () = let x1 = try Some (gen1 ()) with EOG -> None in let x2 = try Some (gen2 ()) with EOG -> None in @@ -411,9 +604,7 @@ let eq ?(eq=(=)) e1 e2 = in check () -let lexico ?(cmp=Pervasives.compare) e1 e2 = - let gen1 = e1() in - let gen2 = e2() in +let lexico ?(cmp=Pervasives.compare) gen1 gen2 = let rec lexico () = let x1 = try Some (gen1 ()) with EOG -> None in let x2 = try Some (gen2 ()) with EOG -> None in @@ -426,32 +617,69 @@ let lexico ?(cmp=Pervasives.compare) e1 e2 = | None, Some _ -> -1 in lexico () -let compare ?cmp e1 e2 = lexico ?cmp e1 e2 +let compare ?cmp gen1 gen2 = lexico ?cmp gen1 gen2 -(** {2 Complex combinators} *) +(** {3 Complex combinators} *) -(** Pick elements fairly in each sub-enum *) -let merge enum = +let merge gen = (* list of sub-enums *) - let l = fold (fun acc x -> x::acc) [] enum in + let l = fold (fun acc x -> x::acc) [] gen in let l = List.rev l in - fun () -> - let q = Queue.create () in - List.iter (fun enum' -> Queue.push (enum' ()) q) l; - (* recursive function to get next element *) - let rec next () = - if Queue.is_empty q - then raise EOG - else - let gen = Queue.pop q in - match (try Some (gen ()) with EOG -> None) with - | None -> next () (* exhausted generator, drop it *) - | Some x -> - Queue.push gen q; (* put generator at the end, return x *) - x - in next + let q = Queue.create () in + List.iter (fun gen' -> Queue.push gen' q) l; + (* recursive function to get next element *) + let rec next () = + if Queue.is_empty q + then raise EOG + else + let gen = Queue.pop q in + match (try Some (gen ()) with EOG -> None) with + | None -> next () (* exhausted generator, drop it *) + | Some x -> + Queue.push gen q; (* put generator at the end, return x *) + x + in next -(** {3 Mutable heap (taken from heap.ml to avoid dependencies)} *) +let intersection ?(cmp=Pervasives.compare) gen1 gen2 = + let next1 () = try Some (gen1 ()) with EOG -> None in + let next2 () = try Some (gen2 ()) with EOG -> None in + let x1 = ref (next1 ()) in + let x2 = ref (next2 ()) in + let rec next () = + match !x1, !x2 with + | None, None -> raise EOG + | Some y1, Some y2 -> + let c = cmp y1 y2 in + if c = 0 (* equal elements, yield! *) + then (x1 := next1 (); x2 := next2 (); y1) + else if c < 0 (* drop y1 *) + then (x1 := next1 (); next ()) + else (* drop y2 *) + (x2 := next2 (); next ()) + | Some _, None + | None, Some _ -> raise EOG + in next + +let sorted_merge ?(cmp=Pervasives.compare) gen1 gen2 = + let next1 () = try Some (gen1 ()) with EOG -> None in + let next2 () = try Some (gen2 ()) with EOG -> None in + let x1 = ref (next1 ()) in + let x2 = ref (next2 ()) in + fun () -> + match !x1, !x2 with + | None, None -> raise EOG + | Some y1, Some y2 -> + if cmp y1 y2 <= 0 + then (x1 := next1 (); y1) + else (x2 := next2 (); y2) + | Some y1, None -> + x1 := next1 (); + y1 + | None, Some y2 -> + x2 := next2 (); + y2 + +(** {4 Mutable heap (taken from heap.ml to avoid dependencies)} *) module Heap = struct type 'a t = { mutable tree : 'a tree; @@ -489,81 +717,498 @@ module Heap = struct x end -(** Intersection of two sorted sequences. Only elements that occur in both - inputs appear in the output *) -let intersection ?(cmp=Pervasives.compare) e1 e2 = +let sorted_merge_n ?(cmp=Pervasives.compare) gen = + (* make a heap of (value, generator) *) + let cmp (v1,_) (v2,_) = cmp v1 v2 in + let heap = Heap.empty ~cmp in + (* add initial values *) + iter + (fun gen' -> + try + let x = gen' () in + Heap.insert heap (x, gen') + with EOG -> ()) + gen; fun () -> - let gen1, gen2 = e1 (), e2 () in - let next1 () = try Some (gen1 ()) with EOG -> None in - let next2 () = try Some (gen2 ()) with EOG -> None in - let x1 = ref (next1 ()) in - let x2 = ref (next2 ()) in + if Heap.is_empty heap then raise EOG + else begin + let x, gen = Heap.pop heap in + try + let y = gen () in + Heap.insert heap (y, gen); (* insert next value *) + x + with EOG -> + x (* gen is empty *) + end + +let round_robin ?(n=2) gen = + (* array of queues, together with their index *) + let qs = Array.init n (fun i -> Queue.create ()) in + let cur = ref 0 in + (* get next element for the i-th queue *) + let rec next i = + let q = qs.(i) in + if Queue.is_empty q + then update_to_i i (* consume generator *) + else Queue.pop q + (* consume [gen] until some element for [i]-th generator is + available. It raises EOG if [gen] is exhausted before *) + and update_to_i i = + let x = gen () in + let j = !cur in + cur := (j+1) mod n; (* move cursor to next generator *) + let q = qs.(j) in + if j = i + then begin + assert (Queue.is_empty q); + x (* return the element *) + end else begin + Queue.push x q; + update_to_i i (* continue consuming [gen] *) + end + in + (* generators *) + let l = Array.mapi (fun i _ -> (fun () -> next i)) qs in + Array.to_list l + +(* Duplicate the enum into [n] generators (default 2). The generators + share the same underlying instance of the enum, so the optimal case is + when they are consumed evenly *) +let tee ?(n=2) gen = + (* array of queues, together with their index *) + let qs = Array.init n (fun i -> Queue.create ()) in + let finished = ref false in (* is [gen] exhausted? *) + (* get next element for the i-th queue *) + let rec next i = + if Queue.is_empty qs.(i) + then + if !finished then raise EOG + else get_next i (* consume generator *) + else Queue.pop qs.(i) + (* consume one more element *) + and get_next i = + try + let x = gen () in + for j = 0 to n-1 do + if j <> i then Queue.push x qs.(j) + done; + x + with EOG -> + finished := true; + raise EOG + in + (* generators *) + let l = Array.mapi (fun i _ -> (fun () -> next i)) qs in + Array.to_list l + +let round_robin ?(n=2) gen = + (* array of queues, together with their index *) + let qs = Array.init n (fun i -> Queue.create ()) in + let cur = ref 0 in + let stop = ref false in + (* get next element for the i-th queue *) + let rec next i = + let q = qs.(i) in + if Queue.is_empty q + then update_to_i i (* consume generator *) + else Queue.pop q + (* consume [gen] until some element for [i]-th generator is + available. It raises EOG if [gen] is exhausted before *) + and update_to_i i = + (if !stop then raise EOG); + let x = try gen () with EOG -> stop := true; raise EOG in + let j = !cur in + cur := (j+1) mod n; (* move cursor to next generator *) + let q = qs.(j) in + if j = i + then begin + assert (Queue.is_empty q); + x (* return the element *) + end else begin + Queue.push x q; + update_to_i i (* continue consuming [gen] *) + end + in + (* generators *) + let l = Array.mapi (fun i _ -> fun () -> next i) qs in + Array.to_list l + +(* Yield elements from a and b alternatively *) +let interleave gen_a gen_b = + let left = ref true in (* left or right? *) + fun () -> + if !left + then (left := false; gen_a ()) + else (left := true; gen_b ()) + +(* Put [x] between elements of [enum] *) +let intersperse x gen = + let next_elem = ref None in + (* must see whether the gen is empty (first element must be from enum) *) + try + next_elem := Some (gen ()); + (* get next element *) let rec next () = - match !x1, !x2 with - | None, None -> raise EOG - | Some y1, Some y2 -> - let c = cmp y1 y2 in - if c = 0 (* equal elements, yield! *) - then (x1 := next1 (); x2 := next2 (); y1) - else if c < 0 (* drop y1 *) - then (x1 := next1 (); next ()) - else (* drop y2 *) - (x2 := next2 (); next ()) - | Some _, None - | None, Some _ -> raise EOG + match !next_elem with + | None -> next_elem := Some (gen ()); x (* yield x, gen is not exhausted *) + | Some y -> next_elem := None; y (* yield element of gen *) in next + with EOG -> + fun () -> raise EOG -(** Binary sorted merge of two sorted sequences *) -let sorted_merge ?(cmp=Pervasives.compare) e1 e2 = - fun () -> - let gen1, gen2 = e1 (), e2 () in - let next1 () = try Some (gen1 ()) with EOG -> None in - let next2 () = try Some (gen2 ()) with EOG -> None in - let x1 = ref (next1 ()) in - let x2 = ref (next2 ()) in - fun () -> - match !x1, !x2 with - | None, None -> raise EOG - | Some y1, Some y2 -> - if cmp y1 y2 <= 0 - then (x1 := next1 (); y1) - else (x2 := next2 (); y2) - | Some y1, None -> - x1 := next1 (); - y1 - | None, Some y2 -> - x2 := next2 (); - y2 +(* Cartesian product *) +let product gena genb = + let all_a = ref [] in + let all_b = ref [] in + let cur = ref `GetLeft in + let rec next () = + match !cur with + | `Stop -> raise EOG + | `GetLeft -> + let xa = try Some (gena()) with EOG -> None in + begin match xa with + | None -> cur := `GetRight + | Some a -> all_a := a :: !all_a; cur := `ProdLeft (a, !all_b) + end; + next () + | `GetRight -> + let xb = try Some (genb()) with EOG -> None in + begin match xb with + | None -> cur := `Stop; raise EOG + | Some b -> all_b := b::!all_b; cur := `ProdRight (b, !all_a) + end; + next () + | `ProdLeft (_, []) -> + cur := `GetRight; + next() + | `ProdLeft (x, y::l) -> + cur := `ProdLeft (x, l); + x, y + | `ProdRight (_, []) -> + cur := `GetLeft; + next() + | `ProdRight (y, x::l) -> + cur := `ProdRight (y, l); + x, y + in + next -(** Assuming subsequences are sorted in increasing order, merge them - into an increasing sequence *) -let sorted_merge_n ?(cmp=Pervasives.compare) enum = +(* Group equal consecutive elements together. *) +let group ?(eq=(=)) gen = + try + let cur = ref [gen ()] in + let rec next () = + (* try to get an element *) + let next_x = + if !cur = [] + then None + else try Some (gen ()) with EOG -> None in + match next_x, !cur with + | None, [] -> raise EOG + | None, l -> + cur := []; + l + | Some x, y::_ when eq x y -> + cur := x::!cur; + next () (* same group *) + | Some x, l -> + cur := [x]; + l + in next + with EOG -> + fun () -> raise EOG + +let uniq ?(eq=(=)) gen = + let prev = ref (Obj.magic 0) in + let first = ref true in + let rec next () = + let x = gen () in + if !first then (first := false; prev := x; x) + else if eq x !prev then next () + else (prev := x; x) + in next + +let sort ?(cmp=Pervasives.compare) gen = + (* build heap *) + let h = Heap.empty ~cmp in + iter (Heap.insert h) gen; fun () -> - (* make a heap of (value, generator) *) - let cmp (v1,_) (v2,_) = cmp v1 v2 in - let heap = Heap.empty ~cmp in - (* add initial values *) - iter - (fun enum' -> - let gen = enum' () in - try - let x = gen () in - Heap.insert heap (x, gen) - with EOG -> ()) - enum; - fun () -> - if Heap.is_empty heap then raise EOG + if Heap.is_empty h + then raise EOG + else Heap.pop h + +(* FIXME: use a set *) +let sort_uniq ?(cmp=Pervasives.compare) gen = + uniq ~eq:(fun x y -> cmp x y = 0) (sort ~cmp gen) + +(* +let permutations enum = + failwith "not implemented" (* TODO *) + +let combinations n enum = + assert (n >= 0); + failwith "not implemented" (* TODO *) + +let powerSet enum = + failwith "not implemented" +*) + +(** {3 Conversion} *) + +let of_list l = + let l = ref l in + fun () -> + match !l with + | [] -> raise EOG + | x::l' -> l := l'; x + +let to_rev_list gen = + fold (fun acc x -> x :: acc) [] gen + +let to_list gen = List.rev (to_rev_list gen) + +let to_array gen = + let l = to_rev_list gen in + let a = Array.of_list l in + let n = Array.length a in + for i = 0 to (n-1) / 2 do + let tmp = a.(i) in + a.(i) <- a.(n-i-1); + a.(n-i-1) <- tmp + done; + a + +let of_array ?(start=0) ?len a = + let len = match len with + | None -> Array.length a - start + | Some n -> assert (n + start < Array.length a); n in + let i = ref start in + fun () -> + if !i >= start + len + then raise EOG + else (let x = a.(!i) in incr i; x) + +let rand_int i = + repeatedly (fun () -> Random.int i) + +let int_range i j = + let r = ref i in + fun () -> + let x = !r in + if x > j then raise EOG else begin - let x, gen = Heap.pop heap in - try - let y = gen () in - Heap.insert heap (y, gen); (* insert next value *) - x - with EOG -> - x (* gen is empty *) + incr r; + x end -(** {3 Mutable double-linked list, similar to {! Deque.t} *) +let pp ?(start="") ?(stop="") ?(sep=",") ?(horizontal=false) pp_elem formatter gen = + (if horizontal + then Format.pp_open_hbox formatter () + else Format.pp_open_hvbox formatter 0); + Format.pp_print_string formatter start; + let rec next is_first = + let continue_ = + try + let x = gen () in + (if not is_first + then begin + Format.pp_print_string formatter sep; + Format.pp_print_space formatter (); + pp_elem formatter x + end else pp_elem formatter x); + true + with EOG -> false in + if continue_ then next false + in + next true; + Format.pp_print_string formatter stop; + Format.pp_close_box formatter () + +module Infix = struct + let (--) = int_range + + let (>>=) x f = flatMap f x +end + +include Infix + +module Restart = struct + type 'a t = unit -> 'a gen + + type 'a restartable = 'a t + + let lift f e = f (e ()) + let lift2 f e1 e2 = f (e1 ()) (e2 ()) + + let empty () = empty + + let singleton x () = singleton x + + let iterate x f () = iterate x f + + let repeat x () = repeat x + + let repeatedly f () = repeatedly f + + let unfold f acc () = unfold f acc + + let cycle enum = + assert (not (is_empty enum)); + fun () -> + let gen = ref (enum ()) in + let rec next () = + try !gen () + with EOG -> + gen := enum (); + next () + in next + + let is_empty e = is_empty (e ()) + + let fold f acc e = fold f acc (e ()) + + let fold2 f acc e1 e2 = fold2 f acc (e1 ()) (e2 ()) + + let reduce f e = reduce f (e ()) + + let scan f acc e () = scan f acc (e ()) + + let iter f e = iter f (e ()) + + let iteri f e = iteri f (e ()) + + let iter2 f e1 e2 = iter2 f (e1 ()) (e2 ()) + + let length e = length (e ()) + + let map f e () = map f (e ()) + + let append e1 e2 () = append (e1 ()) (e2 ()) + + let flatten e () = flatten (e ()) + + let flatMap f e () = flatMap f (e ()) + + let mem ?eq x e = mem ?eq x (e ()) + + let take n e () = take n (e ()) + + let drop n e () = drop n (e ()) + + let nth n e = nth n (e ()) + + let filter p e () = filter p (e ()) + + let takeWhile p e () = takeWhile p (e ()) + + let dropWhile p e () = dropWhile p (e ()) + + let filterMap f e () = filterMap f (e ()) + + let zipWith f e1 e2 () = zipWith f (e1 ()) (e2 ()) + + let zip e1 e2 () = zip (e1 ()) (e2 ()) + + let zipIndex e () = zipIndex (e ()) + + let unzip e = map fst e, map snd e + + let partition p e = + filter p e, filter (fun x -> not (p x)) e + + let for_all p e = + for_all p (e ()) + + let exists p e = + exists p (e ()) + + let for_all2 p e1 e2 = + for_all2 p (e1 ()) (e2 ()) + + let exists2 p e1 e2 = + exists2 p (e1 ()) (e2 ()) + + let min ?lt e = min ?lt (e ()) + + let max ?lt e = max ?lt (e ()) + + let ___eq = eq + let eq ?eq e1 e2 = ___eq ?eq (e1 ()) (e2 ()) + + let lexico ?cmp e1 e2 = lexico ?cmp (e1 ()) (e2 ()) + + let compare ?cmp e1 e2 = compare ?cmp (e1 ()) (e2 ()) + + let merge e () = merge (e ()) + + let intersection ?cmp e1 e2 () = + intersection ?cmp (e1 ()) (e2 ()) + + let sorted_merge ?cmp e1 e2 () = + sorted_merge ?cmp (e1 ()) (e2 ()) + + let sorted_merge_n ?cmp e () = + sorted_merge_n ?cmp (e ()) + + let tee ?n e = tee ?n (e ()) + + let round_robin ?n e = round_robin ?n (e ()) + + let interleave e1 e2 () = interleave (e1 ()) (e2 ()) + + let intersperse x e () = intersperse x (e ()) + + let product e1 e2 () = product (e1 ()) (e2 ()) + + let group ?eq e () = group ?eq (e ()) + + let uniq ?eq e () = uniq ?eq (e ()) + + let sort ?(cmp=Pervasives.compare) enum = + fun () -> + (* build heap *) + let h = Heap.empty ~cmp in + iter (Heap.insert h) enum; + fun () -> + if Heap.is_empty h + then raise EOG + else Heap.pop h + + let sort_uniq ?(cmp=Pervasives.compare) e = + let e' = sort ~cmp e in + uniq ~eq:(fun x y -> cmp x y = 0) e' + + let of_list l () = of_list l + + let to_rev_list e = to_rev_list (e ()) + + let to_list e = to_list (e ()) + + let to_array e = to_array (e ()) + + let of_array ?start ?len a () = of_array ?start ?len a + + let rand_int i () = rand_int i + + let int_range i j () = int_range i j + + module Infix = struct + let (--) = int_range + + let (>>=) x f = flatMap f x + end + + include Infix + + let pp ?start ?stop ?sep ?horizontal pp_elem fmt e = + pp ?start ?stop ?sep ?horizontal pp_elem fmt (e ()) +end + +(** {2 Generator functions} *) + +let start g = g () + +(** {4 Mutable double-linked list, similar to {! Deque.t} *) module MList = struct type 'a t = 'a node option ref and 'a node = { @@ -615,294 +1260,3 @@ let persistent gen = ()); (* done recursing through the generator *) MList.to_enum l - -let round_robin ?(n=2) enum = - fun () -> - (* array of queues, together with their index *) - let qs = Array.init n (fun i -> Queue.create ()) in - let gen = enum () in (* unique generator! *) - let cur = ref 0 in - (* get next element for the i-th queue *) - let rec next i = - let q = qs.(i) in - if Queue.is_empty q - then update_to_i i (* consume generator *) - else Queue.pop q - (* consume [gen] until some element for [i]-th generator is - available. It raises EOG if [gen] is exhausted before *) - and update_to_i i = - let x = gen () in - let j = !cur in - cur := (j+1) mod n; (* move cursor to next generator *) - let q = qs.(j) in - if j = i - then begin - assert (Queue.is_empty q); - x (* return the element *) - end else begin - Queue.push x q; - update_to_i i (* continue consuming [gen] *) - end - in - (* generator of generators *) - let i = ref 0 in - fun () -> - let j = !i in - if j = n then raise EOG else (incr i; fun () -> next j) - -(** Duplicate the enum into [n] generators (default 2). The generators - share the same underlying instance of the enum, so the optimal case is - when they are consumed evenly *) -let tee ?(n=2) enum = - fun () -> - (* array of queues, together with their index *) - let qs = Array.init n (fun i -> Queue.create ()) in - let gen = enum () in (* unique generator! *) - let finished = ref false in (* is [gen] exhausted? *) - (* get next element for the i-th queue *) - let rec next i = - if Queue.is_empty qs.(i) - then - if !finished then raise EOG - else get_next i (* consume generator *) - else Queue.pop qs.(i) - (* consume one more element *) - and get_next i = - try - let x = gen () in - for j = 0 to n-1 do - if j <> i then Queue.push x qs.(j) - done; - x - with EOG -> - finished := true; - raise EOG - in - (* generator of generators *) - let i = ref 0 in - fun () -> - let j = !i in - if j = n then raise EOG else (incr i; fun () -> next j) - -(** Yield elements from a and b alternatively *) -let interleave a b = - fun () -> - let gen_a = a () in - let gen_b = b () in - let left = ref true in (* left or right? *) - fun () -> - if !left - then (left := false; gen_a ()) - else (left := true; gen_b ()) - -(** Put [x] between elements of [enum] *) -let intersperse x enum = - fun () -> - let next_elem = ref None in - let gen = enum () in - (* must see whether the gen is empty (first element must be from enum) *) - try - next_elem := Some (gen ()); - (* get next element *) - let rec next () = - match !next_elem with - | None -> next_elem := Some (gen ()); x (* yield x, gen is not exhausted *) - | Some y -> next_elem := None; y (* yield element of gen *) - in next - with EOG -> - fun () -> raise EOG - -(** Cartesian product *) -let product a b = - fun () -> - if is_empty a || is_empty b then fun () -> raise EOG - else - (* [a] is the outer relation *) - let gen_a = a () in - (* current element of [a] *) - let cur_a = ref (gen_a ()) in - let gen_b = ref (b ()) in - let rec next () = - try !cur_a, !gen_b () - with EOG -> - (* gen_b exhausted, get next elem of [a] *) - cur_a := gen_a (); - gen_b := b (); - next () - in - next - -let fair_product a b = - fun () -> - let gena = a () in - let genb = b () in - let all_a = ref [] in - let all_b = ref [] in - let cur = ref `GetLeft in - let rec next () = - match !cur with - | `Stop -> raise EOG - | `GetLeft -> - let xa = try Some (gena()) with EOG -> None in - begin match xa with - | None -> cur := `GetRight - | Some a -> all_a := a :: !all_a; cur := `ProdLeft (a, !all_b) - end; - next () - | `GetRight -> - let xb = try Some (genb()) with EOG -> None in - begin match xb with - | None -> cur := `Stop; raise EOG - | Some b -> all_b := b::!all_b; cur := `ProdRight (b, !all_a) - end; - next () - | `ProdLeft (_, []) -> - cur := `GetRight; - next() - | `ProdLeft (x, y::l) -> - cur := `ProdLeft (x, l); - x, y - | `ProdRight (_, []) -> - cur := `GetLeft; - next() - | `ProdRight (y, x::l) -> - cur := `ProdRight (y, l); - x, y - in - next - -(** Group equal consecutive elements together. *) -let group ?(eq=(=)) enum = - fun () -> - let gen = enum () in - try - let cur = ref [gen ()] in - let rec next () = - (* try to get an element *) - let next_x = - if !cur = [] - then None - else try Some (gen ()) with EOG -> None in - match next_x, !cur with - | None, [] -> raise EOG - | None, l -> - cur := []; - l - | Some x, y::_ when eq x y -> - cur := x::!cur; - next () (* same group *) - | Some x, l -> - cur := [x]; - l - in next - with EOG -> - fun () -> raise EOG - -let uniq ?(eq=(=)) enum = - fun () -> - let gen = enum () in - let prev = ref (Obj.magic 0) in - let first = ref true in - let rec next () = - let x = gen () in - if !first then (first := false; prev := x; x) - else if eq x !prev then next () - else (prev := x; x) - in next - -let sort ?(cmp=Pervasives.compare) enum = - fun () -> - (* build heap *) - let h = Heap.empty ~cmp in - iter (Heap.insert h) enum; - fun () -> - if Heap.is_empty h - then raise EOG - else Heap.pop h - -let sort_uniq ?(cmp=Pervasives.compare) enum = - uniq ~eq:(fun x y -> cmp x y = 0) (sort ~cmp enum) - -(* -let permutations enum = - failwith "not implemented" (* TODO *) - -let combinations n enum = - assert (n >= 0); - failwith "not implemented" (* TODO *) - -let powerSet enum = - failwith "not implemented" -*) - -(** {2 Basic conversion functions} *) - -let to_list enum = - Gen.to_list (enum ()) - -let of_list l = - fun () -> - Gen.of_list l - -let to_rev_list enum = - Gen.to_rev_list (enum ()) - -let to_array enum = - let n = length enum in - let a = Array.make n (Obj.magic 0) in - iteri (fun i x -> a.(i) <- x) enum; - a - -let of_array ?(start=0) ?len a = - let len = match len with - | None -> Array.length a - start - | Some n -> assert (n + start < Array.length a); n in - fun () -> - let i = ref start in - fun () -> - if !i >= start + len - then raise EOG - else (let x = a.(!i) in incr i; x) - - -let rand_int i = - repeatedly (fun () -> Random.int i) - -let int_range i j = - fun () -> Gen.int_range i j - -let pp ?(start="") ?(stop="") ?(sep=",") ?(horizontal=false) pp_elem formatter enum = - (if horizontal - then Format.pp_open_hbox formatter () - else Format.pp_open_hvbox formatter 0); - Format.pp_print_string formatter start; - let gen = enum () in - let rec next is_first = - let continue_ = - try - let x = gen () in - (if not is_first - then begin - Format.pp_print_string formatter sep; - Format.pp_print_space formatter (); - pp_elem formatter x - end else pp_elem formatter x); - true - with EOG -> false in - if continue_ then next false - in - next true; - Format.pp_print_string formatter stop; - Format.pp_close_box formatter () - -module Infix = struct - let (@@) = append - - let (>>=) e f = flatMap f e - - let (--) = int_range - - let (|>) x f = f x -end - -include Infix diff --git a/gen.mli b/gen.mli index 1faabf17..f1447bab 100644 --- a/gen.mli +++ b/gen.mli @@ -23,327 +23,318 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) -(** {1 Restartable generators} *) +(** {1 Generators} -(** This structure is inspired from Ocaml Batteries' BatEnum.t. It features - restartable generators. A value of type ['a Gen.t] represents a finite or - infinite lazy enumeration of values of type ['a]. It can be instantiated - as many times as needed into a ['a generator], which is a consumable - enumeration. The next element of a ['a generator] is obtained by - invoking it as a function; an exception, [EOG], is raised when no - more elements are available. *) +Values of type ['a Gen.t] represent a possibly infinite sequence of values +of type 'a. One can only iterate once on the sequence, as it is consumed +by iteration/deconstruction/access. The exception {!EOG} (end of generator) +is raised when the generator is empty. + +The submodule {!Restart} provides utilities to work with +{b restartable generators}, that is, functions [unit -> 'a Gen.t] that +allow to build as many generators from the same source as needed. +*) + +(** {2 Global type declarations} *) exception EOG (** End of Generation *) -type 'a t = unit -> 'a generator - (** An enum is a generator of generators *) -and 'a generator = unit -> 'a +type 'a t = unit -> 'a (** A generator may be called several times, yielding the next value each time. It raises EOG when it reaches the end. *) -(** {2 Generator functions} *) +type 'a gen = 'a t -val start : 'a t -> 'a generator - (** Create a new generator on the given restartable generator. *) +(** {2 Common signature for transient and restartable generators} *) + +module type S = sig + type 'a t + + val empty : 'a t + (** Empty generator, with no elements *) + + val singleton : 'a -> 'a t + (** One-element generator *) + + val repeat : 'a -> 'a t + (** Repeat same element endlessly *) + + val iterate : 'a -> ('a -> 'a) -> 'a t + (** [iterate x f] is [[x; f x; f (f x); f (f (f x)); ...]] *) + + val unfold : ('b -> ('a * 'b) option) -> 'b -> 'a t + (** Dual of {!fold}, with a deconstructing operation. It keeps on + unfolding the ['b] value into a new ['b], and a ['a] which is yielded, + until [None] is returned. *) + + (** {2 Basic combinators} *) + + val is_empty : _ t -> bool + (** Check whether the enum is empty. *) + + val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b + (** Fold on the generator, tail-recursively *) + + val fold2 : ('c -> 'a -> 'b -> 'c) -> 'c -> 'a t -> 'b t -> 'c + (** Fold on the two enums in parallel. Stops once one of the enums + is exhausted. *) + + val reduce : ('a -> 'a -> 'a) -> 'a t -> 'a + (** Fold on non-empty sequences (otherwise raise Invalid_argument) *) + + val scan : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b t + (** Like {!fold}, but keeping successive values of the accumulator *) + + val iter : ('a -> unit) -> 'a t -> unit + (** Iterate on the enum *) + + val iteri : (int -> 'a -> unit) -> 'a t -> unit + (** Iterate on elements with their index in the enum, from 0 *) + + val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit + (** Iterate on the two sequences. Stops once one of them is exhausted.*) + + val length : _ t -> int + (** Length of an enum (linear time) *) + + val map : ('a -> 'b) -> 'a t -> 'b t + (** Lazy map. No iteration is performed now, the function will be called + when the result is traversed. *) + + val append : 'a t -> 'a t -> 'a t + (** Append the two enums; the result contains the elements of the first, + then the elements of the second enum. *) + + val flatten : 'a gen t -> 'a t + (** Flatten the enumeration of generators *) + + val flatMap : ('a -> 'b gen) -> 'a t -> 'b t + (** Monadic bind; each element is transformed to a sub-enum + which is then iterated on, before the next element is processed, + and so on. *) + + val mem : ?eq:('a -> 'a -> bool) -> 'a -> 'a t -> bool + (** Is the given element, member of the enum? *) + + val take : int -> 'a t -> 'a t + (** Take at most n elements *) + + val drop : int -> 'a t -> 'a t + (** Drop n elements *) + + val nth : int -> 'a t -> 'a + (** n-th element, or Not_found + @raise Not_found if the generator contains less than [n] arguments *) + + val filter : ('a -> bool) -> 'a t -> 'a t + (** Filter out elements that do not satisfy the predicate. *) + + val takeWhile : ('a -> bool) -> 'a t -> 'a t + (** Take elements while they satisfy the predicate *) + + val dropWhile : ('a -> bool) -> 'a t -> 'a t + (** Drop elements while they satisfy the predicate *) + + val filterMap : ('a -> 'b option) -> 'a t -> 'b t + (** Maps some elements to 'b, drop the other ones *) + + val zipWith : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t + (** Combine common part of the enums (stops when one is exhausted) *) + + val zip : 'a t -> 'b t -> ('a * 'b) t + (** Zip together the common part of the enums *) + + val zipIndex : 'a t -> (int * 'a) t + (** Zip elements with their index in the enum *) + + val unzip : ('a * 'b) t -> 'a t * 'b t + (** Unzip into two sequences, splitting each pair *) + + val partition : ('a -> bool) -> 'a t -> 'a t * 'a t + (** [partition p l] returns the elements that satisfy [p], + and the elements that do not satisfy [p] *) + + val for_all : ('a -> bool) -> 'a t -> bool + (** Is the predicate true for all elements? *) + + val exists : ('a -> bool) -> 'a t -> bool + (** Is the predicate true for at least one element? *) + + val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool + + val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool + + val min : ?lt:('a -> 'a -> bool) -> 'a t -> 'a + (** Minimum element, according to the given comparison function *) + + val max : ?lt:('a -> 'a -> bool) -> 'a t -> 'a + (** Maximum element, see {!min} *) + + val eq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t -> bool + (** Equality of generators. *) + + val lexico : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> int + (** Lexicographic comparison of generators. If the common prefix is + the same, the shortest one is considered as smaller than the other. *) + + val compare : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> int + (** Synonym for {! lexico} *) + + (** {2 Complex combinators} *) + + val merge : 'a gen t -> 'a t + (** Pick elements fairly in each sub-generator. The given enum + must be finite (not its elements, though). The merge of enums + [e1, e2, ... en] picks one element in [e1], then one element in [e2], + then in [e3], ..., then in [en], and then starts again at [e1]. Once + a generator is empty, it is skipped; when they are all empty, + their merge is also empty. + For instance, [merge [1;3;5] [2;4;6]] will be [1;2;3;4;5;6]. *) + + val intersection : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> 'a t + (** Intersection of two sorted sequences. Only elements that occur in both + inputs appear in the output *) + + val sorted_merge : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> 'a t + (** Merge two sorted sequences into a sorted sequence *) + + val sorted_merge_n : ?cmp:('a -> 'a -> int) -> 'a gen t -> 'a t + (** Sorted merge of multiple sorted sequences *) + + val tee : ?n:int -> 'a t -> 'a gen list + (** Duplicate the enum into [n] generators (default 2). The generators + share the same underlying instance of the enum, so the optimal case is + when they are consumed evenly *) + + val round_robin : ?n:int -> 'a t -> 'a gen list + (** Split the enum into [n] generators in a fair way. Elements with + [index = k mod n] with go to the k-th enum. [n] default value + is 2. *) + + val interleave : 'a t -> 'a t -> 'a t + (** [interleave a b] yields an element of [a], then an element of [b], + and so on until the end of [a] or [b] is reached. *) + + val intersperse : 'a -> 'a t -> 'a t + (** Put the separator element between all elements of the given enum *) + + val product : 'a t -> 'b t -> ('a * 'b) t + (** Cartesian product, in no predictable order. Works even if some of the + arguments are infinite. *) + + val group : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t + (** Group equal consecutive elements together. *) + + val uniq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t + (** Remove consecutive duplicate elements. Basically this is + like [fun e -> map List.hd (group e)]. *) + + val sort : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t + (** Sort according to the given comparison function. The enum must be finite. *) + + val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t + (** Sort and remove duplicates. The enum must be finite. *) + + (* TODO later + val permutations : 'a t -> 'a gen t + (** Permutations of the enum. Each permutation becomes unavailable once + the next one is produced. *) + + val combinations : int -> 'a t -> 'a t t + (** Combinations of given length. *) + + val powerSet : 'a t -> 'a t t + (** All subsets of the enum (in no particular order) *) + *) + + (** {2 Basic conversion functions} *) + + val of_list : 'a list -> 'a t + (** Enumerate elements of the list *) + + val to_list : 'a t -> 'a list + (** non tail-call trasnformation to list, in the same order *) + + val to_rev_list : 'a t -> 'a list + (** Tail call conversion to list, in reverse order (more efficient) *) + + val to_array : 'a t -> 'a array + (** Convert the enum to an array (not very efficient) *) + + val of_array : ?start:int -> ?len:int -> 'a array -> 'a t + (** Iterate on (a slice of) the given array *) + + val rand_int : int -> int t + (** Random ints in the given range. *) + + val int_range : int -> int -> int t + (** [int_range a b] enumerates integers between [a] and [b], included. [a] + is assumed to be smaller than [b]. *) + + module Infix : sig + val (--) : int -> int -> int t + (** Synonym for {! int_range} *) + + val (>>=) : 'a t -> ('a -> 'b gen) -> 'b t + (** Monadic bind operator *) + end + + val (--) : int -> int -> int t + (** Synonym for {! int_range} *) + + val (>>=) : 'a t -> ('a -> 'b gen) -> 'b t + (** Monadic bind operator *) + + val pp : ?start:string -> ?stop:string -> ?sep:string -> ?horizontal:bool -> + (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit + (** Pretty print the content of the generator on a formatter. *) +end (** {2 Transient generators} *) -module Gen : sig - val empty : 'a generator +val get : 'a t -> 'a + (** Get the next value + @raise EOG if there is no next value *) - val next : 'a generator -> 'a - (** Get next element, or raise EOG *) +val next : 'a t -> 'a + (** Synonym for {!get} *) - val junk : 'a generator -> unit - (** Drop element *) +val get_safe : 'a t -> 'a option + (** Get the next value, or return None *) - val fold : ('b -> 'a -> 'b) -> 'b -> 'a generator -> 'b - (** Fold over the generator *) - - val iter : ('a -> unit) -> 'a generator -> unit - (** Iterate on the generator *) - - val length : 'a generator -> int - (** Consume generator to compute its length *) - - val of_list : 'a list -> 'a generator - - val to_list : 'a generator -> 'a list (* not tailrec *) - - val to_rev_list : 'a generator -> 'a list - - val int_range : int -> int -> int generator -end - -(** {2 Basic constructors} *) - -val empty : 'a t - (** Empty enum, with no elements *) - -val singleton : 'a -> 'a t - (** One-element enum *) - -val repeat : 'a -> 'a t - (** Repeat same element endlessly *) +val junk : 'a t -> unit + (** Drop the next value, discarding it. + @raise EOG if there is no next value *) val repeatedly : (unit -> 'a) -> 'a t (** Call the same function an infinite number of times (useful for instance if the function is a random generator). *) -val iterate : 'a -> ('a -> 'a) -> 'a t - (** [iterate x f] is [[x; f x; f (f x); f (f (f x)); ...]] *) +include S with type 'a t := 'a gen -val unfold : ('b -> ('a * 'b) option) -> 'b -> 'a t - (** Dual of {!fold}, with a deconstructing operation. It keeps on - unfolding the ['b] value into a new ['b], and a ['a] which is yielded, - until [None] is returned. *) +(** {2 Restartable generators} *) -(** {2 Basic combinators} *) +module Restart : sig + type 'a t = unit -> 'a gen -val is_empty : _ t -> bool - (** Check whether the enum is empty. *) + type 'a restartable = 'a t -val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b - (** Fold on the generator, tail-recursively *) + include S with type 'a t := 'a restartable -val fold2 : ('c -> 'a -> 'b -> 'c) -> 'c -> 'a t -> 'b t -> 'c - (** Fold on the two enums in parallel. Stops once one of the enums - is exhausted. *) + val cycle : 'a t -> 'a t + (** Cycle through the enum, endlessly. The enum must not be empty. *) -val reduce : ('a -> 'a -> 'a) -> 'a t -> 'a - (** Fold on non-empty sequences (otherwise raise Invalid_argument) *) + val lift : ('a gen -> 'b) -> 'a t -> 'b -val scan : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b t - (** Like {!fold}, but keeping successive values of the accumulator *) - -val iter : ('a -> unit) -> 'a t -> unit - (** Iterate on the enum *) - -val iteri : (int -> 'a -> unit) -> 'a t -> unit - (** Iterate on elements with their index in the enum, from 0 *) - -val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit - (** Iterate on the two sequences. Stops once one of them is exhausted.*) - -val length : _ t -> int - (** Length of an enum (linear time) *) - -val map : ('a -> 'b) -> 'a t -> 'b t - (** Lazy map. No iteration is performed now, the function will be called - when the result is traversed. *) - -val append : 'a t -> 'a t -> 'a t - (** Append the two enums; the result contains the elements of the first, - then the elements of the second enum. *) - -val cycle : 'a t -> 'a t - (** Cycle through the enum, endlessly. The enum must not be empty. *) - -val flatten : 'a t t -> 'a t - (** Flatten the enum of enum. *) - -val flatMap : ('a -> 'b t) -> 'a t -> 'b t - (** Monadic bind; each element is transformed to a sub-enum - which is then iterated on, before the next element is processed, - and so on. *) - -val mem : ?eq:('a -> 'a -> bool) -> 'a -> 'a t -> bool - (** Is the given element, member of the enum? *) - -val take : int -> 'a t -> 'a t - (** Take at most n elements *) - -val drop : int -> 'a t -> 'a t - (** Drop n elements *) - -val nth : int -> 'a t -> 'a - (** n-th element, or Not_found *) - -val filter : ('a -> bool) -> 'a t -> 'a t - (** Filter out elements that do not satisfy the predicate. *) - -val takeWhile : ('a -> bool) -> 'a t -> 'a t - (** Take elements while they satisfy the predicate *) - -val dropWhile : ('a -> bool) -> 'a t -> 'a t - (** Drop elements while they satisfy the predicate *) - -val filterMap : ('a -> 'b option) -> 'a t -> 'b t - (** Maps some elements to 'b, drop the other ones *) - -val zipWith : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t - (** Combine common part of the enums (stops when one is exhausted) *) - -val zip : 'a t -> 'b t -> ('a * 'b) t - (** Zip together the common part of the enums *) - -val zipIndex : 'a t -> (int * 'a) t - (** Zip elements with their index in the enum *) - -val unzip : ('a * 'b) t -> 'a t * 'b t - (** Unzip into two sequences, splitting each pair *) - -val partition : ('a -> bool) -> 'a t -> 'a t * 'a t - (** [partition p l] returns the elements that satisfy [p], - and the elements that do not satisfy [p] *) - -val for_all : ('a -> bool) -> 'a t -> bool - (** Is the predicate true for all elements? *) - -val exists : ('a -> bool) -> 'a t -> bool - (** Is the predicate true for at least one element? *) - -val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool - -val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool - -val min : ?lt:('a -> 'a -> bool) -> 'a t -> 'a - (** Minimum element, according to the given comparison function *) - -val max : ?lt:('a -> 'a -> bool) -> 'a t -> 'a - (** Maximum element, see {!min} *) - -val eq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t -> bool - (** Equality of generators. *) - -val lexico : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> int - (** Lexicographic comparison of generators. If the common prefix is - the same, the shortest one is considered as smaller than the other. *) - -val compare : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> int - (** Synonym for {! lexico} *) - -(** {2 Complex combinators} *) - -val merge : 'a t t -> 'a t - (** Pick elements fairly in each sub-enum. The given enum - must be finite (not its elements, though). The merge of enums - [e1, e2, ... en] picks one element in [e1], then one element in [e2], - then in [e3], ..., then in [en], and then starts again at [e1]. Once - a generator is empty, it is skipped; when they are all empty, - their merge is also empty. - For instance, [merge [1;3;5] [2;4;6]] will be [1;2;3;4;5;6]. *) - -(** {3 Mutable heap (taken from heap.ml to avoid dependencies)} *) -module Heap : sig - type 'a t (** A heap containing values of type 'a *) - val empty : cmp:('a -> 'a -> int) -> 'a t - val insert : 'a t -> 'a -> unit - val is_empty : 'a t -> bool - val pop : 'a t -> 'a + val lift2 : ('a gen -> 'b gen -> 'c) -> 'a t -> 'b t -> 'c end -val intersection : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> 'a t - (** Intersection of two sorted sequences. Only elements that occur in both - inputs appear in the output *) +(** {2 Utils} *) -val sorted_merge : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t -> 'a t - (** Merge two sorted sequences into a sorted sequence *) - -val sorted_merge_n : ?cmp:('a -> 'a -> int) -> 'a t t -> 'a t - (** Sorted merge of multiple sorted sequences *) - -val persistent : 'a generator -> 'a t +val persistent : 'a t -> 'a Restart.t (** Store content of the generator in memory, to be able to iterate on it several times later *) -val round_robin : ?n:int -> 'a t -> 'a generator t - (** Split the enum into [n] generators in a fair way. Elements with - [index = k mod n] with go to the k-th enum. [n] default value - is 2. *) - -val tee : ?n:int -> 'a t -> 'a generator t - (** Duplicate the enum into [n] generators (default 2). The generators - share the same underlying instance of the enum, so the optimal case is - when they are consumed evenly *) - -val interleave : 'a t -> 'a t -> 'a t - (** [interleave a b] yields an element of [a], then an element of [b], - and so on until the end of [a] or [b] is reached. *) - -val intersperse : 'a -> 'a t -> 'a t - (** Put the separator element between all elements of the given enum *) - -val product : 'a t -> 'b t -> ('a * 'b) t - (** Cartesian product. If the first sequence is infinite, some pairs - will never be generated. *) - -val fair_product : 'a t -> 'b t -> ('a * 'b) t - (** Cartesian product, in no predictable order. Contrary to {!product} this - function does eventually yield every pair *) - -val group : ?eq:('a -> 'a -> bool) -> 'a t -> 'a list t - (** Group equal consecutive elements together. *) - -val uniq : ?eq:('a -> 'a -> bool) -> 'a t -> 'a t - (** Remove consecutive duplicate elements. Basically this is - like [fun e -> map List.hd (group e)]. *) - -val sort : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t - (** Sort according to the given comparison function. The enum must be finite. *) - -val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a t -> 'a t - (** Sort and remove duplicates. The enum must be finite. *) - -(* TODO later -val permutations : 'a t -> 'a t t - (** Permutations of the enum. Each permutation becomes unavailable once - the next one is produced. *) - -val combinations : int -> 'a t -> 'a t t - (** Combinations of given length. *) - -val powerSet : 'a t -> 'a t t - (** All subsets of the enum (in no particular order) *) -*) - -(** {2 Basic conversion functions} *) - -val of_list : 'a list -> 'a t - (** Enumerate elements of the list *) - -val to_list : 'a t -> 'a list - (** non tail-call trasnformation to list, in the same order *) - -val to_rev_list : 'a t -> 'a list - (** Tail call conversion to list, in reverse order (more efficient) *) - -val to_array : 'a t -> 'a array - (** Convert the enum to an array (not very efficient) *) - -val of_array : ?start:int -> ?len:int -> 'a array -> 'a t - (** Iterate on (a slice of) the given array *) - -val rand_int : int -> int t - (** Random ints in the given range. *) - -val int_range : int -> int -> int t - (** [int_range a b] enumerates integers between [a] and [b], included. [a] - is assumed to be smaller than [b]. *) - -val pp : ?start:string -> ?stop:string -> ?sep:string -> ?horizontal:bool -> - (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit - (** Pretty print an enum on a formatter. *) - -module Infix : sig - val (@@) : 'a t -> 'a t -> 'a t - (** Synonym for {! append} *) - - val (>>=) : 'a t -> ('a -> 'b t) -> 'b t - (** Synonym for {! flatMap}, with arguments reversed *) - - val (--) : int -> int -> int t - (** Synonym for {! int_range} *) - - val (|>) : 'a -> ('a -> 'b) -> 'b - (** Function application, reversed *) -end - -val (@@) : 'a t -> 'a t -> 'a t - -val (>>=) : 'a t -> ('a -> 'b t) -> 'b t - -val (--) : int -> int -> int t - -val (|>) : 'a -> ('a -> 'b) -> 'b +val start : 'a Restart.t -> 'a t + (** Create a new transient generator *) diff --git a/skipList.ml b/skipList.ml index 23c44234..7b427c31 100644 --- a/skipList.ml +++ b/skipList.ml @@ -178,15 +178,14 @@ let length l = l.size (** Iterator on the skip list *) let gen l = + let x = ref (next l.data 0) in fun () -> - let x = ref (next l.data 0) in - fun () -> - match !x with - | Nil -> raise Gen.EOG - | Init _ -> assert false - | Node (k, v, a) -> - x := a.(0); - k, !v + match !x with + | Nil -> raise Gen.EOG + | Init _ -> assert false + | Node (k, v, a) -> + x := a.(0); + k, !v (** Add content of the iterator to the list *) let of_gen l gen = diff --git a/tests/helpers.ml b/tests/helpers.ml index 28f71b3a..d5eb690a 100644 --- a/tests/helpers.ml +++ b/tests/helpers.ml @@ -7,3 +7,11 @@ let print_int_list l = (Sequence.pp_seq ~sep:", " Format.pp_print_int) (Sequence.of_list l); Buffer.contents b + +let print_int_int_list l = + let printer fmt (i,j) = Format.fprintf fmt "%d, %d" i j in + let b = Buffer.create 20 in + Format.bprintf b "@[[%a]@]" + (Sequence.pp_seq ~sep:", " printer) + (Sequence.of_list l); + Buffer.contents b diff --git a/tests/test_gen.ml b/tests/test_gen.ml index adf9feae..65fc98b5 100644 --- a/tests/test_gen.ml +++ b/tests/test_gen.ml @@ -2,6 +2,8 @@ open OUnit open Gen.Infix +module GR = Gen.Restart + let pint i = string_of_int i let plist l = Utils.sprintf "%a" (Sequence.pp_seq Format.pp_print_int) (Sequence.of_list l) @@ -9,18 +11,18 @@ let pstrlist l = Utils.sprintf "%a" (Sequence.pp_seq Format.pp_print_string) (Sequence.of_list l) let test_singleton () = - let e = Gen.singleton 42 in - let gen = Gen.start e in - OUnit.assert_equal 42 (Gen.Gen.next gen); - OUnit.assert_raises Gen.EOG (fun () -> Gen.Gen.next gen); - OUnit.assert_equal 1 (Gen.length e); + let gen = Gen.singleton 42 in + OUnit.assert_equal 42 (Gen.get gen); + OUnit.assert_raises Gen.EOG (fun () -> Gen.get gen); + let gen = Gen.singleton 42 in + OUnit.assert_equal 1 (Gen.length gen); () let test_iter () = - let e = 1 -- 10 in - OUnit.assert_equal ~printer:pint 10 (Gen.length e); - OUnit.assert_equal [1;2] (Gen.to_list (1 -- 2)); - OUnit.assert_equal [1;2;3;4;5] (Gen.to_list (Gen.take 5 e)); + let e = GR.(1 -- 10) in + OUnit.assert_equal ~printer:pint 10 (GR.length e); + OUnit.assert_equal [1;2] GR.(to_list (1 -- 2)); + OUnit.assert_equal [1;2;3;4;5] (GR.to_list (GR.take 5 e)); () let test_map () = @@ -30,7 +32,7 @@ let test_map () = () let test_append () = - let e = (1 -- 5) @@ (6 -- 10) in + let e = Gen.append (1 -- 5) (6 -- 10) in OUnit.assert_equal [10;9;8;7;6;5;4;3;2;1] (Gen.to_rev_list e); () @@ -64,26 +66,23 @@ let test_persistent () = if j > 5 then raise Gen.EOG else (incr i; j) in let e = Gen.persistent gen in - OUnit.assert_equal [0;1;2;3;4;5] (Gen.to_list e); - OUnit.assert_equal [0;1;2;3;4;5] (Gen.to_list e); - OUnit.assert_equal [0;1;2;3;4;5] (Gen.to_list e); + OUnit.assert_equal [0;1;2;3;4;5] (GR.to_list e); + OUnit.assert_equal [0;1;2;3;4;5] (GR.to_list e); + OUnit.assert_equal [0;1;2;3;4;5] (GR.to_list e); () let test_round_robin () = - let e = Gen.round_robin ~n:2 (1--10) in - let e = Gen.map Gen.persistent e in - let l = Gen.to_list e in - match l with + let e = GR.round_robin ~n:2 GR.(1--10) in + match e with | [a;b] -> OUnit.assert_equal [1;3;5;7;9] (Gen.to_list a); OUnit.assert_equal [2;4;6;8;10] (Gen.to_list b) | _ -> OUnit.assert_failure "wrong list lenght" let test_big_rr () = - let e = Gen.round_robin ~n:3 (1 -- 999) in - let l = Gen.to_list e in - let l' = List.map Gen.Gen.length l in - OUnit.assert_equal [333;333;333] l'; + let e = GR.round_robin ~n:3 GR.(1 -- 999) in + let l = List.map Gen.length e in + OUnit.assert_equal [333;333;333] l; () let test_merge_sorted () = @@ -106,13 +105,9 @@ let test_intersperse () = () let test_product () = + let printer = Helpers.print_int_int_list in let e = Gen.product (1--3) (4--5) in - OUnit.assert_equal [1,4; 1,5; 2,4; 2,5; 3,4; 3,5] (Gen.to_list e); - () - -let test_fair_product () = - let e = Gen.fair_product (Gen.repeat ()) (1--3) in - let _ = Gen.take 10 e in (* succeeds -> ok *) + OUnit.assert_equal ~printer [1,4; 1,5; 2,4; 2,5; 3,4; 3,5] (List.sort compare (Gen.to_list e)); () let suite = @@ -132,5 +127,4 @@ let suite = "test_interleave" >:: test_interleave; "test_intersperse" >:: test_intersperse; "test_product" >:: test_product; - "test_fair_product" >:: test_fair_product; ]