diff --git a/README.adoc b/README.adoc index 7f69a87f..04738814 100644 --- a/README.adoc +++ b/README.adoc @@ -119,7 +119,8 @@ Documentation http://cedeela.fr/~simon/software/containers[here]. - `CCHeap`, a purely functional heap structure - `CCVector`, a growable array (pure OCaml, no C) with mutability annotations - `CCList`, functions on lists, including tail-recursive implementations of `map` and `append` and many other things -- `CCArray`, utilities on arrays and slices +- `CCArray`, utilities on arrays +- `CCArray_slice`, array slices - `CCHashtbl`, `CCMap` extensions of the standard modules `Hashtbl` and `Map` - `CCInt` - `CCString` (basic string operations) diff --git a/_oasis b/_oasis index 31c2f04e..259f985b 100644 --- a/_oasis +++ b/_oasis @@ -41,7 +41,8 @@ Library "containers" Modules: CCVector, CCHeap, CCList, CCOpt, CCPair, CCFun, CCHash, CCInt, CCBool, CCFloat, CCArray, CCRef, CCSet, CCOrd, CCRandom, CCString, CCHashtbl, CCMap, CCFormat, CCIO, - CCInt64, CCChar, CCResult, CCParse, Containers + CCInt64, CCChar, CCResult, CCParse, CCArray_slice, + Containers BuildDepends: bytes, result # BuildDepends: bytes, bisect_ppx diff --git a/doc/intro.txt b/doc/intro.txt index a03d509d..2a973e9f 100644 --- a/doc/intro.txt +++ b/doc/intro.txt @@ -26,6 +26,7 @@ by ocamlfind). {!modules: CCArray +CCArray_slice CCBool CCChar CCFloat diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index c46fa8f6..728dbb36 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -11,314 +11,11 @@ type 'a ord = 'a -> 'a -> int type 'a random_gen = Random.State.t -> 'a type 'a printer = Format.formatter -> 'a -> unit -module type S = sig - type 'a t - (** Array, or sub-array, containing elements of type ['a] *) - - val empty : 'a t - - val equal : 'a equal -> 'a t equal - - val compare : 'a ord -> 'a t ord - - val get : 'a t -> int -> 'a - - val get_safe : 'a t -> int -> 'a option - (** [get_safe a i] returns [Some a.(i)] if [i] is a valid index - @since 0.18 *) - - val set : 'a t -> int -> 'a -> unit - - val length : _ t -> int - - val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a - - val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a - (** Fold left on array, with index *) - - val fold_while : ('a -> 'b -> 'a * [`Stop | `Continue]) -> 'a -> 'b t -> 'a - (** Fold left on array until a stop condition via [('a, `Stop)] is - indicated by the accumulator - @since 0.8 *) - - val iter : ('a -> unit) -> 'a t -> unit - - val iteri : (int -> 'a -> unit) -> 'a t -> unit - - val blit : 'a t -> int -> 'a t -> int -> int -> unit - (** [blit from i into j len] copies [len] elements from the first array - to the second. See {!Array.blit}. *) - - val reverse_in_place : 'a t -> unit - (** Reverse the array in place *) - - val sorted : ('a -> 'a -> int) -> 'a t -> 'a array - (** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. - @since NEXT_RELEASE *) - - val sort_indices : ('a -> 'a -> int) -> 'a t -> int array - (** [sort_indices cmp a] returns a new array [b], with the same length as [a], - such that [b.(i)] is the index of the [i]-th element of [a] in [sort cmp a]. - In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. - [a] is not modified. - @since NEXT_RELEASE *) - - val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array - (** [sort_ranking cmp a] returns a new array [b], with the same length as [a], - such that [b.(i)] is the position in [sorted cmp a] of the [i]-th - element of [a]. - [a] is not modified. - - In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. - - Without duplicates, we also have - [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] - @since NEXT_RELEASE *) - - val find : ('a -> 'b option) -> 'a t -> 'b option - (** [find f a] returns [Some y] if there is an element [x] such - that [f x = Some y], else it returns [None] *) - - val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option - (** Like {!find}, but also pass the index to the predicate function. - @since 0.3.4 *) - - val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option - (** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l], - and [p x] holds. Otherwise returns [None] - @since 0.3.4 *) - - val lookup : ?cmp:'a ord -> 'a -> 'a t -> int option - (** Lookup the index of some value in a sorted array. - @return [None] if the key is not present, or - [Some i] ([i] the index of the key) otherwise *) - - val lookup_exn : ?cmp:'a ord -> 'a -> 'a t -> int - (** Same as {!lookup_exn}, but - @raise Not_found if the key is not present *) - - val bsearch : ?cmp:('a -> 'a -> int) -> 'a -> 'a t -> - [ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ] - (** [bsearch ?cmp x arr] finds the index of the object [x] in the array [arr], - provided [arr] is {b sorted} using [cmp]. If the array is not sorted, - the result is not specified (may raise Invalid_argument). - - Complexity: O(log n) where n is the length of the array - (dichotomic search). - - @return - - [`At i] if [cmp arr.(i) x = 0] (for some i) - - [`All_lower] if all elements of [arr] are lower than [x] - - [`All_bigger] if all elements of [arr] are bigger than [x] - - [`Just_after i] if [arr.(i) < x < arr.(i+1)] - - [`Empty] if the array is empty - - @raise Invalid_argument if the array is found to be unsorted w.r.t [cmp] - @since 0.13 *) - - val for_all : ('a -> bool) -> 'a t -> bool - - val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool - (** Forall on pairs of arrays. - @raise Invalid_argument if they have distinct lengths - allow different types @since 0.20 *) - - val exists : ('a -> bool) -> 'a t -> bool - - val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool - (** Exists on pairs of arrays. - @raise Invalid_argument if they have distinct lengths - allow different types @since 0.20 *) - - val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc - (** Fold on two arrays stepwise. - @raise Invalid_argument if they have distinct lengths - @since 0.20 *) - - val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit - (** Iterate on two arrays stepwise. - @raise Invalid_argument if they have distinct lengths - @since 0.20 *) - - val shuffle : 'a t -> unit - (** Shuffle randomly the array, in place *) - - val shuffle_with : Random.State.t -> 'a t -> unit - (** Like shuffle but using a specialized random state *) - - val random_choose : 'a t -> 'a random_gen - (** Choose an element randomly. - @raise Not_found if the array/slice is empty *) - - val to_seq : 'a t -> 'a sequence - val to_gen : 'a t -> 'a gen - val to_klist : 'a t -> 'a klist - - (** {2 IO} *) - - val pp: ?sep:string -> 'a printer -> 'a t printer - (** Print an array of items with printing function *) - - val pp_i: ?sep:string -> (int -> 'a printer) -> 'a t printer - (** Print an array, giving the printing function both index and item *) -end - -(** {2 General Implementation} -Most of those functions use a range [(i,j)] with -[i] included and [j] excluded *) - -let rec _foldi f acc a i j = - if i = j then acc else _foldi f (f acc i a.(i)) a (i+1) j - -let _reverse_in_place a i ~len = - if len=0 then () - else - for k = 0 to (len-1)/2 do - let t = a.(i+k) in - a.(i+k) <- a.(i+len-1-k); - a.(i+len-1-k) <- t; - done - -let rec _equal eq a1 i1 j1 a2 i2 j2 = - if i1 = j1 - then (assert (i1=j1 && i2=j2); true) - else - eq a1.(i1) a2.(i2) && _equal eq a1 (i1+1) j1 a2 (i2+1) j2 - -let rec _compare cmp a1 i1 j1 a2 i2 j2 = - if i1 = j1 - then if i2=j2 then 0 else -1 - else if i2=j2 - then 1 - else - let c = cmp a1.(i1) a2.(i2) in - if c = 0 - then _compare cmp a1 (i1+1) j1 a2 (i2+1) j2 - else c - -(*$T - compare CCOrd.compare [| 1; 2; 3 |] [| 1; 2; 3 |] = 0 - compare CCOrd.compare [| 1; 2; 3 |] [| 2; 2; 3 |] < 0 - compare CCOrd.compare [| 1; 2; |] [| 1; 2; 3 |] < 0 - compare CCOrd.compare [| 1; 2; 3 |] [| 1; 2; |] > 0 -*) - -let rec _find f a i j = - if i = j then None - else match f i a.(i) with - | Some _ as res -> res - | None -> _find f a (i+1) j - -let rec _lookup_rec ~cmp k a i j = - if i>j then raise Not_found - else if i=j - then if cmp k a.(i) = 0 - then i - else raise Not_found - else - let middle = (j+i)/2 in - match cmp k a.(middle) with - | 0 -> middle - | n when n<0 -> _lookup_rec ~cmp k a i (middle-1) - | _ -> _lookup_rec ~cmp k a (middle+1) j - -let _lookup_exn ~cmp k a i j = - if i>j then raise Not_found; - match cmp k a.(i) with - | 0 -> i - | n when n<0 -> raise Not_found (* too low *) - | _ when i=j -> raise Not_found (* too high *) - | _ -> - match cmp k a.(j) with - | 0 -> j - | n when n<0 -> _lookup_rec ~cmp k a (i+1) (j-1) - | _ -> raise Not_found (* too high *) - -let bsearch_ ~cmp x arr i j = - let rec aux i j = - if i > j - then `Just_after j - else - let middle = i + (j - i) / 2 in (* avoid overflow *) - match cmp x arr.(middle) with - | 0 -> `At middle - | n when n<0 -> aux i (middle - 1) - | _ -> aux (middle + 1) j - in - if i>=j then `Empty - else match cmp arr.(i) x, cmp arr.(j) x with - | n, _ when n>0 -> `All_bigger - | _, n when n<0 -> `All_lower - | _ -> aux i j - -let rec _for_all p a i j = - i = j || (p a.(i) && _for_all p a (i+1) j) - -let rec _exists p a i j = - i <> j && (p a.(i) || _exists p a (i+1) j) - -let rec _for_all2 p a1 a2 i1 i2 ~len = - len=0 || (p a1.(i1) a2.(i2) && _for_all2 p a1 a2 (i1+1) (i2+1) ~len:(len-1)) - -let rec _exists2 p a1 a2 i1 i2 ~len = - len>0 && (p a1.(i1) a2.(i2) || _exists2 p a1 a2 (i1+1) (i2+1) ~len:(len-1)) - -(* shuffle a[i...j[ using the given int random generator - See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle *) -let _shuffle _rand_int a i j = - for k = j-1 downto i+1 do - let l = _rand_int (k+1) in - let tmp = a.(l) in - a.(l) <- a.(k); - a.(k) <- tmp; - done - (*$T let st = Random.State.make [||] in let a = 0--10000 in \ let b = Array.copy a in shuffle_with st a; a <> b *) -let _sort_indices cmp a i j = - let len = j-i in - let b = Array.init len (fun k->k) in - Array.sort (fun k1 k2 -> cmp a.(k1+i) a.(k2+i)) b; - b - -let _sorted cmp a i j = - let len = j-i in - let b = Array.sub a i len in - Array.sort cmp b; - b - -let _choose a i j st = - if i>=j then raise Not_found; - a.(i+Random.State.int st (j-i)) - -let _pp ~sep pp_item out a i j = - for k = i to j - 1 do - if k > i then (Format.pp_print_string out sep; Format.pp_print_cut out ()); - pp_item out a.(k) - done - -let _pp_i ~sep pp_item out a i j = - for k = i to j - 1 do - if k > i then (Format.pp_print_string out sep; Format.pp_print_cut out ()); - pp_item k out a.(k) - done - -let _to_gen a i j = - let k = ref i in - fun () -> - if !k < j - then ( - let x = a.(!k) in - incr k; - Some x - ) else None - -let rec _to_klist a i j () = - if i=j then `Nil else `Cons (a.(i), _to_klist a (i+1) j) - (** {2 Arrays} *) type 'a t = 'a array @@ -354,7 +51,11 @@ let set = Array.set let fold = Array.fold_left -let foldi f acc a = _foldi f acc a 0 (Array.length a) +let foldi f acc a = + let rec aux acc i = + if i = Array.length a then acc else aux (f acc i a.(i)) (i+1) + in + aux acc 0 let fold_while f acc a = let rec fold_while_i f acc i = @@ -377,7 +78,14 @@ let iteri = Array.iteri let blit = Array.blit let reverse_in_place a = - _reverse_in_place a 0 ~len:(Array.length a) + let len = Array.length a in + if len>0 then ( + for k = 0 to (len-1)/2 do + let t = a.(k) in + a.(k) <- a.(len-1-k); + a.(len-1-k) <- t; + done + ) (*$T reverse_in_place [| |]; true @@ -390,7 +98,10 @@ let reverse_in_place a = a = [| 6;5;4;3;2;1 |] *) -let sorted cmp a = _sorted cmp a 0 (Array.length a) +let sorted cmp a = + let b = Array.copy a in + Array.sort cmp b; + b (*$= & ~cmp:(=) ~printer:Q.Print.(array int) [||] (sorted Pervasives.compare [||]) @@ -403,7 +114,11 @@ let sorted cmp a = _sorted cmp a 0 (Array.length a) Array.sort Pervasives.compare b; b = sorted Pervasives.compare a) *) -let sort_indices cmp a = _sort_indices cmp a 0 (Array.length a) +let sort_indices cmp a = + let len = Array.length a in + let b = Array.init len (fun k->k) in + Array.sort (fun k1 k2 -> cmp a.(k1) a.(k2)) b; + b (*$= & ~cmp:(=) ~printer:Q.Print.(array int) [||] (sort_indices Pervasives.compare [||]) @@ -447,14 +162,20 @@ let rev a = rev [| |] = [| |] *) +let rec find_aux f a i = + if i = Array.length a then None + else match f i a.(i) with + | Some _ as res -> res + | None -> find_aux f a (i+1) + let find f a = - _find (fun _ -> f ) a 0 (Array.length a) + find_aux (fun _ -> f ) a 0 let findi f a = - _find f a 0 (Array.length a) + find_aux f a 0 let find_idx p a = - _find (fun i x -> if p x then Some (i,x) else None) a 0 (Array.length a) + find_aux (fun i x -> if p x then Some (i,x) else None) a 0 let filter_map f a = let rec aux acc i = @@ -505,6 +226,31 @@ let flat_map f a = a' = [| 1; 2; 3; 4; 5; 6 |] *) +let rec _lookup_rec ~cmp k a i j = + if i>j then raise Not_found + else if i=j + then if cmp k a.(i) = 0 + then i + else raise Not_found + else + let middle = (j+i)/2 in + match cmp k a.(middle) with + | 0 -> middle + | n when n<0 -> _lookup_rec ~cmp k a i (middle-1) + | _ -> _lookup_rec ~cmp k a (middle+1) j + +let _lookup_exn ~cmp k a i j = + if i>j then raise Not_found; + match cmp k a.(i) with + | 0 -> i + | n when n<0 -> raise Not_found (* too low *) + | _ when i=j -> raise Not_found (* too high *) + | _ -> + match cmp k a.(j) with + | 0 -> j + | n when n<0 -> _lookup_rec ~cmp k a (i+1) (j-1) + | _ -> raise Not_found (* too high *) + let lookup_exn ?(cmp=Pervasives.compare) k a = _lookup_exn ~cmp k a 0 (Array.length a-1) @@ -522,7 +268,23 @@ let lookup ?(cmp=Pervasives.compare) k a = lookup 2 [| 1 |] = None *) -let bsearch ?(cmp=Pervasives.compare) k a = bsearch_ ~cmp k a 0 (Array.length a-1) +let bsearch ?(cmp=Pervasives.compare) k a = + let rec aux i j = + if i > j + then `Just_after j + else + let middle = i + (j - i) / 2 in (* avoid overflow *) + match cmp k a.(middle) with + | 0 -> `At middle + | n when n<0 -> aux i (middle - 1) + | _ -> aux (middle + 1) j + in + let n = Array.length a in + if n=0 then `Empty + else match cmp a.(0) k, cmp a.(n-1) k with + | c, _ when c>0 -> `All_bigger + | _, c when c<0 -> `All_lower + | _ -> aux 0 (n-1) (*$T bsearch bsearch 3 [|1; 2; 2; 3; 4; 10|] = `At 3 @@ -540,15 +302,29 @@ let (>>|) a f = map f a let (>|=) a f = map f a -let for_all p a = _for_all p a 0 (Array.length a) +let for_all p a = + let rec aux i = + i = Array.length a || (p a.(i) && aux (i+1)) + in + aux 0 -let exists p a = _exists p a 0 (Array.length a) +let exists p a = + let rec aux i = + i <> Array.length a && (p a.(i) || aux (i+1)) + in + aux 0 + +let rec _for_all2 p a1 a2 i1 i2 ~len = + len=0 || (p a1.(i1) a2.(i2) && _for_all2 p a1 a2 (i1+1) (i2+1) ~len:(len-1)) let for_all2 p a b = Array.length a = Array.length b && _for_all2 p a b 0 0 ~len:(Array.length a) +let rec _exists2 p a1 a2 i1 i2 ~len = + len>0 && (p a1.(i1) a2.(i2) || _exists2 p a1 a2 (i1+1) (i2+1) ~len:(len-1)) + let exists2 p a b = _exists2 p a b 0 0 ~len:(min (Array.length a) (Array.length b)) @@ -610,9 +386,13 @@ let except_idx a i = [] a let equal eq a b = + let rec aux i = + if i = Array.length a then true + else eq a.(i) b.(i) && aux (i+1) + in Array.length a = Array.length b && - _equal eq a 0 (Array.length a) b 0 (Array.length b) + aux 0 (*$Q Q.(pair (array small_int)(array small_int)) (fun (a,b) -> \ @@ -624,13 +404,47 @@ let equal eq a b = *) let compare cmp a b = - _compare cmp a 0 (Array.length a) b 0 (Array.length b) + let rec aux i = + if i = Array.length a + then if i = Array.length b then 0 else -1 + else if i = Array.length b + then 1 + else + let c = cmp a.(i) b.(i) in + if c = 0 then aux (i+1) else c + in + aux 0 -let shuffle a = _shuffle Random.int a 0 (Array.length a) +(*$T + compare CCOrd.compare [| 1; 2; 3 |] [| 1; 2; 3 |] = 0 + compare CCOrd.compare [| 1; 2; 3 |] [| 2; 2; 3 |] < 0 + compare CCOrd.compare [| 1; 2; |] [| 1; 2; 3 |] < 0 + compare CCOrd.compare [| 1; 2; 3 |] [| 1; 2; |] > 0 +*) -let shuffle_with st a = _shuffle (Random.State.int st) a 0 (Array.length a) +(* shuffle a[i...j[ using the given int random generator + See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle *) +let _shuffle _rand_int a i j = + for k = j-1 downto i+1 do + let l = _rand_int (k+1) in + let tmp = a.(l) in + a.(l) <- a.(k); + a.(k) <- tmp; + done -let random_choose a st = _choose a 0 (Array.length a) st +let shuffle a = + _shuffle Random.int a 0 (Array.length a) + +let shuffle_with st a = + _shuffle (Random.State.int st) a 0 (Array.length a) + +let rec _to_klist a i j () = + if i=j then `Nil else `Cons (a.(i), _to_klist a (i+1) j) + +let random_choose a st = + let n = Array.length a in + if n = 0 then raise Not_found; + a.(Random.State.int st n) let random_len n g st = Array.init n (fun _ -> g st) @@ -643,260 +457,32 @@ let random_non_empty g st = let n = 1 + Random.State.int st 1_000 in random_len n g st -let pp ?(sep=", ") pp_item out a = _pp ~sep pp_item out a 0 (Array.length a) +let pp ?(sep=", ") pp_item out a = + for k = 0 to Array.length a-1 do + if k > 0 then (Format.pp_print_string out sep; Format.pp_print_cut out ()); + pp_item out a.(k) + done -let pp_i ?(sep=", ") pp_item out a = _pp_i ~sep pp_item out a 0 (Array.length a) +let pp_i ?(sep=", ") pp_item out a = + for k = 0 to Array.length a - 1 do + if k > 0 then (Format.pp_print_string out sep; Format.pp_print_cut out ()); + pp_item k out a.(k) + done let to_seq a k = iter k a -let to_gen a = _to_gen a 0 (Array.length a) +let to_gen a = + let k = ref 0 in + fun () -> + if !k < Array.length a + then ( + let x = a.(!k) in + incr k; + Some x + ) else None let to_klist a = _to_klist a 0 (Array.length a) -module Sub = struct - type 'a t = { - arr : 'a array; - i : int; (** Start index (included) *) - j : int; (** Stop index (excluded) *) - } - - let empty = { - arr = [||]; - i = 0; - j = 0; - } - - let make arr i ~len = - if i<0||i+len > Array.length arr then invalid_arg "Array.Sub.make"; - { arr; i; j=i+len; } - - let of_slice (arr,i,len) = make arr i ~len - - let to_slice a = a.arr, a.i, a.j-a.i - - let full arr = { arr; i=0; j=Array.length arr; } - - let underlying a = a.arr - - let length a = a.j - a.i - - let copy a = Array.sub a.arr a.i (length a) - - let sub a i len = make a.arr (a.i + i) ~len - (*$= - [ 3;4 ] \ - (let a = Sub.make (0--10) 2 5 in Sub.sub a 1 2 |> Sub.to_list) - [ ] \ - (let a = Sub.make (0--10) 2 5 in Sub.sub a 1 0 |> Sub.to_list) - [ 5 ] \ - (let a = Sub.make (0--10) 1 9 in Sub.sub a 4 1 |> Sub.to_list) - *) - - let equal eq a b = - length a = length b && _equal eq a.arr a.i a.j b.arr b.i b.j - - let compare cmp a b = - _compare cmp a.arr a.i a.j b.arr b.i b.j - - let fold f acc a = - let rec _fold acc i j = - if i=j then acc - else _fold (f acc a.arr.(i)) (i+1) j - in _fold acc a.i a.j - - let to_list a = - let l = fold (fun l x -> x::l) [] a in - List.rev l - - let foldi f acc a = _foldi f acc a.arr a.i a.j - - let fold_while f acc a = - let rec fold_while_i f acc i = - if i < Array.length a.arr && i < a.j then - let acc, cont = f acc a.arr.(i) in - match cont with - | `Stop -> acc - | `Continue -> fold_while_i f acc (i+1) - else acc - in fold_while_i f acc a.i - - let get a i = - let j = a.i + i in - if i<0 || j>=a.j then invalid_arg "Array.Sub.get"; - a.arr.(j) - - let get_safe a i = - try Some (get a i) - with Invalid_argument _ -> None - - (*$inject - let sub_a = Sub.make [|1;2;3;4;5|] 1 ~len:3 - *) - - (*$= - (Some 2) (Sub.get_safe sub_a 0) - (Some 3) (Sub.get_safe sub_a 1) - (Some 4) (Sub.get_safe sub_a 2) - None (Sub.get_safe sub_a 4) - None (Sub.get_safe sub_a max_int) - None (Sub.get_safe sub_a ~-1) - None (Sub.get_safe sub_a ~-42) - *) - - let set a i x = - let j = a.i + i in - if i<0 || j>=a.j then invalid_arg "Array.Sub.set"; - a.arr.(j) <- x - - let iter f a = - for k=a.i to a.j-1 do f a.arr.(k) done - - let iteri f a = - for k=0 to length a-1 do f k a.arr.(a.i + k) done - - let blit a i b j len = - if i+len>length a || j+len>length b then invalid_arg "Array.Sub.blit"; - Array.blit a.arr (a.i+i) b.arr (b.i+j) len - - let reverse_in_place a = _reverse_in_place a.arr a.i ~len:(length a) - - (*$T - let a = 1--6 in let s = Sub.make a 2 ~len:3 in \ - Sub.reverse_in_place s; a = [| 1; 2; 5; 4; 3; 6 |] - *) - - let sorted cmp a = _sorted cmp a.arr a.i a.j - - (*$= & ~cmp:(=) ~printer:Q.Print.(array int) - [||] \ - (let a = 1--6 in let s = Sub.make a 2 ~len:0 in \ - Sub.sorted Pervasives.compare s) - [|2;3;4|] \ - (let a = [|6;5;4;3;2;1|] in let s = Sub.make a 2 ~len:3 in \ - Sub.sorted Pervasives.compare s) - *) - - (*$Q - Q.(array int) (fun a -> \ - Array.length a > 10 ==> ( Array.length a > 10 && \ - let s = Sub.make a 5 ~len:5 in \ - let b = Array.sub a 5 5 in \ - Array.sort Pervasives.compare b; b = Sub.sorted Pervasives.compare s)) - *) - - let sort_ranking cmp a = - let idx = _sort_indices cmp a.arr a.i a.j in - let cmp_int : int -> int -> int = Pervasives.compare in - sort_indices cmp_int idx - - (*$= & ~cmp:(=) ~printer:Q.Print.(array int) - [||] \ - (let a = 1--6 in let s = Sub.make a 2 ~len:0 in \ - Sub.sort_ranking Pervasives.compare s) - [|2;1;3;0|] \ - (let a = [|"d";"c";"b";"e";"a"|] in let s = Sub.make a 1 ~len:4 in \ - Sub.sort_ranking Pervasives.compare s) - *) - - (*$Q - Q.(array printable_string) (fun a -> \ - Array.length a > 10 ==> ( Array.length a > 10 && \ - let s = Sub.make a 5 ~len:5 in \ - let b = Sub.sort_indices String.compare s in \ - Sub.sorted String.compare s = Array.map (Sub.get s) b)) - *) - - let sort_indices cmp a = _sort_indices cmp a.arr a.i a.j - - (*$= & ~cmp:(=) ~printer:Q.Print.(array int) - [||] \ - (let a = 1--6 in let s = Sub.make a 2 ~len:0 in \ - Sub.sort_indices Pervasives.compare s) - [|3;1;0;2|] \ - (let a = [|"d";"c";"b";"e";"a"|] in let s = Sub.make a 1 ~len:4 in \ - Sub.sort_indices Pervasives.compare s) - *) - - (*$Q - Q.(array printable_string) (fun a -> \ - Array.length a > 10 ==> ( Array.length a > 10 && \ - let s = Sub.make a 5 ~len:5 in \ - let b = Sub.sort_ranking String.compare s in \ - let a_sorted = Sub.sorted String.compare s in \ - Sub.copy s = Array.map (Array.get a_sorted) b)) - *) - - - let find f a = _find (fun _ -> f) a.arr a.i a.j - - let findi f a = _find (fun i -> f (i-a.i)) a.arr a.i a.j - - let find_idx p a = - _find (fun i x -> if p x then Some (i-a.i,x) else None) a.arr a.i a.j - - (*$= - (Some (1,"c")) (Sub.find_idx ((=) "c") (Sub.make [| "a"; "b"; "c" |] 1 2)) - *) - - let lookup_exn ?(cmp=Pervasives.compare) k a = - _lookup_exn ~cmp k a.arr a.i (a.j-1) - a.i - - let lookup ?(cmp=Pervasives.compare) k a = - try Some (_lookup_exn ~cmp k a.arr a.i (a.j-1) - a.i) - with Not_found -> None - - (*$= - (Some 1) (Sub.lookup "c" (Sub.make [| "a"; "b"; "c" |] 1 2)) - *) - - let bsearch ?(cmp=Pervasives.compare) k a = - match bsearch_ ~cmp k a.arr a.i (a.j - 1) with - | `At m -> `At (m - a.i) - | `Just_after m -> `Just_after (m - a.i) - | res -> res - - let for_all p a = _for_all p a.arr a.i a.j - - let exists p a = _exists p a.arr a.i a.j - - let for_all2 p a b = - length a = length b && _for_all2 p a.arr b.arr a.i b.i ~len:(length a) - - let exists2 p a b = - _exists2 p a.arr b.arr a.i b.i ~len:(min (length a) (length b)) - - (*$T - Sub.exists2 (=) (Sub.make [| 1;2;3;4 |] 1 ~len:2) (Sub.make [| 0;1;3;4 |] 1 ~len:3) - *) - - let iter2 f a b = - if length a <> length b then invalid_arg "iter2"; - _iter2 f a.arr b.arr a.i b.i ~len:(length a) - - let fold2 f acc a b = - if length a <> length b then invalid_arg "fold2"; - _fold2 f acc a.arr b.arr a.i b.i ~len:(length a) - - let shuffle a = - _shuffle Random.int a.arr a.i a.j - - let shuffle_with st a = - _shuffle (Random.State.int st) a.arr a.i a.j - - let random_choose a st = _choose a.arr a.i a.j st - - let pp ?(sep=", ") pp_item buf a = _pp ~sep pp_item buf a.arr a.i a.j - - let pp_i ?(sep=", ") pp_item out a = - _pp_i ~sep (fun k out x -> pp_item (k-a.i) out x) out a.arr a.i a.j - - let to_seq a k = iter k a - - let to_gen a = _to_gen a.arr a.i a.j - - let to_klist a = _to_klist a.arr a.i a.j -end - (** {2 Generic Functions} *) module type MONO_ARRAY = sig diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index 6774c862..39de56e6 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -11,165 +11,156 @@ type 'a ord = 'a -> 'a -> int type 'a random_gen = Random.State.t -> 'a type 'a printer = Format.formatter -> 'a -> unit -(** {2 Abstract Signature} *) - -module type S = sig - type 'a t - (** Array, or sub-array, containing elements of type ['a] *) - - val empty : 'a t - - val equal : 'a equal -> 'a t equal - - val compare : 'a ord -> 'a t ord - - val get : 'a t -> int -> 'a - - val get_safe : 'a t -> int -> 'a option - (** [get_safe a i] returns [Some a.(i)] if [i] is a valid index - @since 0.18 *) - - val set : 'a t -> int -> 'a -> unit - - val length : _ t -> int - - val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a - - val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a - (** Fold left on array, with index *) - - val fold_while : ('a -> 'b -> 'a * [`Stop | `Continue]) -> 'a -> 'b t -> 'a - (** Fold left on array until a stop condition via [('a, `Stop)] is - indicated by the accumulator - @since 0.8 *) - - val iter : ('a -> unit) -> 'a t -> unit - - val iteri : (int -> 'a -> unit) -> 'a t -> unit - - val blit : 'a t -> int -> 'a t -> int -> int -> unit - (** [blit from i into j len] copies [len] elements from the first array - to the second. See {!Array.blit}. *) - - val reverse_in_place : 'a t -> unit - (** Reverse the array in place *) - - val sorted : ('a -> 'a -> int) -> 'a t -> 'a array - (** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. - @since NEXT_RELEASE *) - - val sort_indices : ('a -> 'a -> int) -> 'a t -> int array - (** [sort_indices cmp a] returns a new array [b], with the same length as [a], - such that [b.(i)] is the index of the [i]-th element of [a] in [sort cmp a]. - In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. - [a] is not modified. - @since NEXT_RELEASE *) - - val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array - (** [sort_ranking cmp a] returns a new array [b], with the same length as [a], - such that [b.(i)] is the position in [sorted cmp a] of the [i]-th - element of [a]. - [a] is not modified. - - In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. - - Without duplicates, we also have - [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] - @since NEXT_RELEASE *) - - val find : ('a -> 'b option) -> 'a t -> 'b option - (** [find f a] returns [Some y] if there is an element [x] such - that [f x = Some y], else it returns [None] *) - - val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option - (** Like {!find}, but also pass the index to the predicate function. - @since 0.3.4 *) - - val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option - (** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l], - and [p x] holds. Otherwise returns [None] - @since 0.3.4 *) - - val lookup : ?cmp:'a ord -> 'a -> 'a t -> int option - (** Lookup the index of some value in a sorted array. - @return [None] if the key is not present, or - [Some i] ([i] the index of the key) otherwise *) - - val lookup_exn : ?cmp:'a ord -> 'a -> 'a t -> int - (** Same as {!lookup_exn}, but - @raise Not_found if the key is not present *) - - val bsearch : ?cmp:('a -> 'a -> int) -> 'a -> 'a t -> - [ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ] - (** [bsearch ?cmp x arr] finds the index of the object [x] in the array [arr], - provided [arr] is {b sorted} using [cmp]. If the array is not sorted, - the result is not specified (may raise Invalid_argument). - - Complexity: O(log n) where n is the length of the array - (dichotomic search). - - @return - - [`At i] if [cmp arr.(i) x = 0] (for some i) - - [`All_lower] if all elements of [arr] are lower than [x] - - [`All_bigger] if all elements of [arr] are bigger than [x] - - [`Just_after i] if [arr.(i) < x < arr.(i+1)] - - [`Empty] if the array is empty - - @raise Invalid_argument if the array is found to be unsorted w.r.t [cmp] - @since 0.13 *) - - val for_all : ('a -> bool) -> 'a t -> bool - - val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool - (** Forall on pairs of arrays. - @raise Invalid_argument if they have distinct lengths - allow different types @since 0.20 *) - - val exists : ('a -> bool) -> 'a t -> bool - - val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool - (** Exists on pairs of arrays. - @raise Invalid_argument if they have distinct lengths - allow different types @since 0.20 *) - - val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc - (** Fold on two arrays stepwise. - @raise Invalid_argument if they have distinct lengths - @since 0.20 *) - - val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit - (** Iterate on two arrays stepwise. - @raise Invalid_argument if they have distinct lengths - @since 0.20 *) - - val shuffle : 'a t -> unit - (** Shuffle randomly the array, in place *) - - val shuffle_with : Random.State.t -> 'a t -> unit - (** Like shuffle but using a specialized random state *) - - val random_choose : 'a t -> 'a random_gen - (** Choose an element randomly. - @raise Not_found if the array/slice is empty *) - - val to_seq : 'a t -> 'a sequence - val to_gen : 'a t -> 'a gen - val to_klist : 'a t -> 'a klist - - (** {2 IO} *) - - val pp: ?sep:string -> 'a printer -> 'a t printer - (** Print an array of items with printing function *) - - val pp_i: ?sep:string -> (int -> 'a printer) -> 'a t printer - (** Print an array, giving the printing function both index and item *) -end - (** {2 Arrays} *) type 'a t = 'a array -include S with type 'a t := 'a t +val empty : 'a t + +val equal : 'a equal -> 'a t equal + +val compare : 'a ord -> 'a t ord + +val get : 'a t -> int -> 'a + +val get_safe : 'a t -> int -> 'a option +(** [get_safe a i] returns [Some a.(i)] if [i] is a valid index + @since 0.18 *) + +val set : 'a t -> int -> 'a -> unit + +val length : _ t -> int + +val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a + +val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a +(** Fold left on array, with index *) + +val fold_while : ('a -> 'b -> 'a * [`Stop | `Continue]) -> 'a -> 'b t -> 'a +(** Fold left on array until a stop condition via [('a, `Stop)] is + indicated by the accumulator + @since 0.8 *) + +val iter : ('a -> unit) -> 'a t -> unit + +val iteri : (int -> 'a -> unit) -> 'a t -> unit + +val blit : 'a t -> int -> 'a t -> int -> int -> unit +(** [blit from i into j len] copies [len] elements from the first array + to the second. See {!Array.blit}. *) + +val reverse_in_place : 'a t -> unit +(** Reverse the array in place *) + +val sorted : ('a -> 'a -> int) -> 'a t -> 'a array +(** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. + @since NEXT_RELEASE *) + +val sort_indices : ('a -> 'a -> int) -> 'a t -> int array +(** [sort_indices cmp a] returns a new array [b], with the same length as [a], + such that [b.(i)] is the index of the [i]-th element of [a] in [sort cmp a]. + In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. + [a] is not modified. + @since NEXT_RELEASE *) + +val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array +(** [sort_ranking cmp a] returns a new array [b], with the same length as [a], + such that [b.(i)] is the position in [sorted cmp a] of the [i]-th + element of [a]. + [a] is not modified. + + In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. + + Without duplicates, we also have + [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] + @since NEXT_RELEASE *) + +val find : ('a -> 'b option) -> 'a t -> 'b option +(** [find f a] returns [Some y] if there is an element [x] such + that [f x = Some y], else it returns [None] *) + +val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option +(** Like {!find}, but also pass the index to the predicate function. + @since 0.3.4 *) + +val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option +(** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l], + and [p x] holds. Otherwise returns [None] + @since 0.3.4 *) + +val lookup : ?cmp:'a ord -> 'a -> 'a t -> int option +(** Lookup the index of some value in a sorted array. + @return [None] if the key is not present, or + [Some i] ([i] the index of the key) otherwise *) + +val lookup_exn : ?cmp:'a ord -> 'a -> 'a t -> int +(** Same as {!lookup_exn}, but + @raise Not_found if the key is not present *) + +val bsearch : ?cmp:('a -> 'a -> int) -> 'a -> 'a t -> + [ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ] +(** [bsearch ?cmp x arr] finds the index of the object [x] in the array [arr], + provided [arr] is {b sorted} using [cmp]. If the array is not sorted, + the result is not specified (may raise Invalid_argument). + + Complexity: O(log n) where n is the length of the array + (dichotomic search). + + @return + - [`At i] if [cmp arr.(i) x = 0] (for some i) + - [`All_lower] if all elements of [arr] are lower than [x] + - [`All_bigger] if all elements of [arr] are bigger than [x] + - [`Just_after i] if [arr.(i) < x < arr.(i+1)] + - [`Empty] if the array is empty + + @raise Invalid_argument if the array is found to be unsorted w.r.t [cmp] + @since 0.13 *) + +val for_all : ('a -> bool) -> 'a t -> bool + +val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool +(** Forall on pairs of arrays. + @raise Invalid_argument if they have distinct lengths + allow different types @since 0.20 *) + +val exists : ('a -> bool) -> 'a t -> bool + +val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool +(** Exists on pairs of arrays. + @raise Invalid_argument if they have distinct lengths + allow different types @since 0.20 *) + +val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc +(** Fold on two arrays stepwise. + @raise Invalid_argument if they have distinct lengths + @since 0.20 *) + +val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit +(** Iterate on two arrays stepwise. + @raise Invalid_argument if they have distinct lengths + @since 0.20 *) + +val shuffle : 'a t -> unit +(** Shuffle randomly the array, in place *) + +val shuffle_with : Random.State.t -> 'a t -> unit +(** Like shuffle but using a specialized random state *) + +val random_choose : 'a t -> 'a random_gen +(** Choose an element randomly. + @raise Not_found if the array/slice is empty *) + +val to_seq : 'a t -> 'a sequence +val to_gen : 'a t -> 'a gen +val to_klist : 'a t -> 'a klist + +(** {2 IO} *) + +val pp: ?sep:string -> 'a printer -> 'a t printer +(** Print an array of items with printing function *) + +val pp_i: ?sep:string -> (int -> 'a printer) -> 'a t printer +(** Print an array, giving the printing function both index and item *) val map : ('a -> 'b) -> 'a t -> 'b t @@ -217,50 +208,6 @@ val random : 'a random_gen -> 'a t random_gen val random_non_empty : 'a random_gen -> 'a t random_gen val random_len : int -> 'a random_gen -> 'a t random_gen -(** {2 Slices} -A slice is a part of an array, that requires no copying and shares -its storage with the original array. - -All indexing in a slice is relative to the beginning of a slice, not -to the underlying array (meaning a slice is effectively like -a regular array) *) - -module Sub : sig - type 'a t - (** A slice is an array, an offset, and a length *) - - val make : 'a array -> int -> len:int -> 'a t - (** Create a slice. - @raise Invalid_argument if the slice isn't valid *) - - val of_slice : ('a array * int * int) -> 'a t - (** Make a sub-array from a triple [(arr, i, len)] where [arr] is the array, - [i] the offset in [arr], and [len] the number of elements of the slice. - @raise Invalid_argument if the slice isn't valid (See {!make}) *) - - val to_slice : 'a t -> ('a array * int * int) - (** Convert into a triple [(arr, i, len)] where [len] is the length of - the subarray of [arr] starting at offset [i] *) - - val to_list : 'a t -> 'a list - (** Convert directly to a list - @since NEXT_RELEASE *) - - val full : 'a array -> 'a t - (** Slice that covers the full array *) - - val underlying : 'a t -> 'a array - (** Underlying array (shared). Modifying this array will modify the slice *) - - val copy : 'a t -> 'a array - (** Copy into a new array *) - - val sub : 'a t -> int -> int -> 'a t - (** Sub-slice *) - - include S with type 'a t := 'a t -end - (** {2 Generic Functions} *) module type MONO_ARRAY = sig diff --git a/src/core/CCArray_slice.ml b/src/core/CCArray_slice.ml new file mode 100644 index 00000000..6759c9a2 --- /dev/null +++ b/src/core/CCArray_slice.ml @@ -0,0 +1,418 @@ + +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Array Slice} *) + +type 'a sequence = ('a -> unit) -> unit +type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist] +type 'a gen = unit -> 'a option +type 'a equal = 'a -> 'a -> bool +type 'a ord = 'a -> 'a -> int +type 'a random_gen = Random.State.t -> 'a +type 'a printer = Format.formatter -> 'a -> unit + +(*$inject + let (--) = CCArray.(--) +*) + +type 'a t = { + arr : 'a array; + i : int; (** Start index (included) *) + j : int; (** Stop index (excluded) *) +} + +let empty = { + arr = [||]; + i = 0; + j = 0; +} + +let make arr i ~len = + if i<0||i+len > Array.length arr then invalid_arg "Array_slice.make"; + { arr; i; j=i+len; } + +let of_slice (arr,i,len) = make arr i ~len + +let to_slice a = a.arr, a.i, a.j-a.i + +let full arr = { arr; i=0; j=Array.length arr; } + +let underlying a = a.arr + +let length a = a.j - a.i + +let copy a = Array.sub a.arr a.i (length a) + +let sub a i len = make a.arr (a.i + i) ~len +(*$= + [ 3;4 ] \ + (let a = make (0--10) 2 5 in sub a 1 2 |> to_list) + [ ] \ + (let a = make (0--10) 2 5 in sub a 1 0 |> to_list) + [ 5 ] \ + (let a = make (0--10) 1 9 in sub a 4 1 |> to_list) +*) + +let rec _foldi f acc a i j = + if i = j then acc else _foldi f (f acc i a.(i)) a (i+1) j + +let _reverse_in_place a i ~len = + if len=0 then () + else + for k = 0 to (len-1)/2 do + let t = a.(i+k) in + a.(i+k) <- a.(i+len-1-k); + a.(i+len-1-k) <- t; + done + +let rec _equal eq a1 i1 j1 a2 i2 j2 = + if i1 = j1 + then (assert (i1=j1 && i2=j2); true) + else + eq a1.(i1) a2.(i2) && _equal eq a1 (i1+1) j1 a2 (i2+1) j2 + +let rec _compare cmp a1 i1 j1 a2 i2 j2 = + if i1 = j1 + then if i2=j2 then 0 else -1 + else if i2=j2 + then 1 + else + let c = cmp a1.(i1) a2.(i2) in + if c = 0 + then _compare cmp a1 (i1+1) j1 a2 (i2+1) j2 + else c + +let equal eq a b = + length a = length b && _equal eq a.arr a.i a.j b.arr b.i b.j + +let compare cmp a b = + _compare cmp a.arr a.i a.j b.arr b.i b.j + +let fold f acc a = + let rec _fold acc i j = + if i=j then acc + else _fold (f acc a.arr.(i)) (i+1) j + in _fold acc a.i a.j + +let to_list a = + let l = fold (fun l x -> x::l) [] a in + List.rev l + +let foldi f acc a = _foldi f acc a.arr a.i a.j + +let fold_while f acc a = + let rec fold_while_i f acc i = + if i < Array.length a.arr && i < a.j then + let acc, cont = f acc a.arr.(i) in + match cont with + | `Stop -> acc + | `Continue -> fold_while_i f acc (i+1) + else acc + in fold_while_i f acc a.i + +let get a i = + let j = a.i + i in + if i<0 || j>=a.j then invalid_arg "Array_slice.get"; + a.arr.(j) + +let get_safe a i = + try Some (get a i) + with Invalid_argument _ -> None + +(*$inject + let sub_a = make [|1;2;3;4;5|] 1 ~len:3 +*) + +(*$= + (Some 2) (get_safe sub_a 0) + (Some 3) (get_safe sub_a 1) + (Some 4) (get_safe sub_a 2) + None (get_safe sub_a 4) + None (get_safe sub_a max_int) + None (get_safe sub_a ~-1) + None (get_safe sub_a ~-42) +*) + +let set a i x = + let j = a.i + i in + if i<0 || j>=a.j then invalid_arg "Array_slice.set"; + a.arr.(j) <- x + +let iter f a = + for k=a.i to a.j-1 do f a.arr.(k) done + +let iteri f a = + for k=0 to length a-1 do f k a.arr.(a.i + k) done + +let blit a i b j len = + if i+len>length a || j+len>length b then invalid_arg "Array_slice.blit"; + Array.blit a.arr (a.i+i) b.arr (b.i+j) len + +let rec _find f a i j = + if i = j then None + else match f i a.(i) with + | Some _ as res -> res + | None -> _find f a (i+1) j + +let rec _lookup_rec ~cmp k a i j = + if i>j then raise Not_found + else if i=j + then if cmp k a.(i) = 0 + then i + else raise Not_found + else + let middle = (j+i)/2 in + match cmp k a.(middle) with + | 0 -> middle + | n when n<0 -> _lookup_rec ~cmp k a i (middle-1) + | _ -> _lookup_rec ~cmp k a (middle+1) j + +let _lookup_exn ~cmp k a i j = + if i>j then raise Not_found; + match cmp k a.(i) with + | 0 -> i + | n when n<0 -> raise Not_found (* too low *) + | _ when i=j -> raise Not_found (* too high *) + | _ -> + match cmp k a.(j) with + | 0 -> j + | n when n<0 -> _lookup_rec ~cmp k a (i+1) (j-1) + | _ -> raise Not_found (* too high *) + +let bsearch_ ~cmp x arr i j = + let rec aux i j = + if i > j + then `Just_after j + else + let middle = i + (j - i) / 2 in (* avoid overflow *) + match cmp x arr.(middle) with + | 0 -> `At middle + | n when n<0 -> aux i (middle - 1) + | _ -> aux (middle + 1) j + in + if i>=j then `Empty + else match cmp arr.(i) x, cmp arr.(j) x with + | n, _ when n>0 -> `All_bigger + | _, n when n<0 -> `All_lower + | _ -> aux i j + +let rec _for_all p a i j = + i = j || (p a.(i) && _for_all p a (i+1) j) + +let rec _exists p a i j = + i <> j && (p a.(i) || _exists p a (i+1) j) + +let rec _for_all2 p a1 a2 i1 i2 ~len = + len=0 || (p a1.(i1) a2.(i2) && _for_all2 p a1 a2 (i1+1) (i2+1) ~len:(len-1)) + +let rec _exists2 p a1 a2 i1 i2 ~len = + len>0 && (p a1.(i1) a2.(i2) || _exists2 p a1 a2 (i1+1) (i2+1) ~len:(len-1)) + +(* shuffle a[i...j[ using the given int random generator + See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle *) +let _shuffle _rand_int a i j = + for k = j-1 downto i+1 do + let l = _rand_int (k+1) in + let tmp = a.(l) in + a.(l) <- a.(k); + a.(k) <- tmp; + done + +(*$T + let st = Random.State.make [||] in let a = 0--10000 in \ + let b = Array.copy a in CCArray.shuffle_with st a; a <> b +*) + +let _sort_indices cmp a i j = + let len = j-i in + let b = Array.init len (fun k->k) in + Array.sort (fun k1 k2 -> cmp a.(k1+i) a.(k2+i)) b; + b + +let _sorted cmp a i j = + let len = j-i in + let b = Array.sub a i len in + Array.sort cmp b; + b + +let _choose a i j st = + if i>=j then raise Not_found; + a.(i+Random.State.int st (j-i)) + +let _pp ~sep pp_item out a i j = + for k = i to j - 1 do + if k > i then (Format.pp_print_string out sep; Format.pp_print_cut out ()); + pp_item out a.(k) + done + +let _pp_i ~sep pp_item out a i j = + for k = i to j - 1 do + if k > i then (Format.pp_print_string out sep; Format.pp_print_cut out ()); + pp_item k out a.(k) + done + +let _to_gen a i j = + let k = ref i in + fun () -> + if !k < j + then ( + let x = a.(!k) in + incr k; + Some x + ) else None + +let rec _to_klist a i j () = + if i=j then `Nil else `Cons (a.(i), _to_klist a (i+1) j) + +let reverse_in_place a = _reverse_in_place a.arr a.i ~len:(length a) + +(*$T +let a = 1--6 in let s = make a 2 ~len:3 in \ + reverse_in_place s; a = [| 1; 2; 5; 4; 3; 6 |] +*) + +let sorted cmp a = _sorted cmp a.arr a.i a.j + +(*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] \ + (let a = 1--6 in let s = make a 2 ~len:0 in \ + sorted Pervasives.compare s) + [|2;3;4|] \ + (let a = [|6;5;4;3;2;1|] in let s = make a 2 ~len:3 in \ + sorted Pervasives.compare s) +*) + +(*$Q + Q.(array int) (fun a -> \ + Array.length a > 10 ==> ( Array.length a > 10 && \ + let s = make a 5 ~len:5 in \ + let b = Array.sub a 5 5 in \ + Array.sort Pervasives.compare b; b = sorted Pervasives.compare s)) +*) + +let sort_ranking cmp a = + let idx = _sort_indices cmp a.arr a.i a.j in + let cmp_int : int -> int -> int = Pervasives.compare in + let sort_indices cmp a = _sort_indices cmp a 0 (Array.length a) in + sort_indices cmp_int idx + +(*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] \ + (let a = 1--6 in let s = make a 2 ~len:0 in \ + sort_ranking Pervasives.compare s) + [|2;1;3;0|] \ + (let a = [|"d";"c";"b";"e";"a"|] in let s = make a 1 ~len:4 in \ + sort_ranking Pervasives.compare s) +*) + +(*$Q + Q.(array printable_string) (fun a -> \ + Array.length a > 10 ==> ( Array.length a > 10 && \ + let s = make a 5 ~len:5 in \ + let b = sort_indices String.compare s in \ + sorted String.compare s = Array.map (get s) b)) +*) + +let sort_indices cmp a = _sort_indices cmp a.arr a.i a.j + +(*$= & ~cmp:(=) ~printer:Q.Print.(array int) + [||] \ + (let a = 1--6 in let s = make a 2 ~len:0 in \ + sort_indices Pervasives.compare s) + [|3;1;0;2|] \ + (let a = [|"d";"c";"b";"e";"a"|] in let s = make a 1 ~len:4 in \ + sort_indices Pervasives.compare s) +*) + +(*$Q + Q.(array printable_string) (fun a -> \ + Array.length a > 10 ==> ( Array.length a > 10 && \ + let s = make a 5 ~len:5 in \ + let b = sort_ranking String.compare s in \ + let a_sorted = sorted String.compare s in \ + copy s = Array.map (Array.get a_sorted) b)) +*) + + +let find f a = _find (fun _ -> f) a.arr a.i a.j + +let findi f a = _find (fun i -> f (i-a.i)) a.arr a.i a.j + +let find_idx p a = + _find (fun i x -> if p x then Some (i-a.i,x) else None) a.arr a.i a.j + +(*$= + (Some (1,"c")) (find_idx ((=) "c") (make [| "a"; "b"; "c" |] 1 2)) + *) + +let lookup_exn ?(cmp=Pervasives.compare) k a = + _lookup_exn ~cmp k a.arr a.i (a.j-1) - a.i + +let lookup ?(cmp=Pervasives.compare) k a = + try Some (_lookup_exn ~cmp k a.arr a.i (a.j-1) - a.i) + with Not_found -> None + +(*$= + (Some 1) (lookup "c" (make [| "a"; "b"; "c" |] 1 2)) + *) + +let bsearch ?(cmp=Pervasives.compare) k a = + match bsearch_ ~cmp k a.arr a.i (a.j - 1) with + | `At m -> `At (m - a.i) + | `Just_after m -> `Just_after (m - a.i) + | res -> res + +let for_all p a = _for_all p a.arr a.i a.j + +let exists p a = _exists p a.arr a.i a.j + +let for_all2 p a b = + length a = length b && _for_all2 p a.arr b.arr a.i b.i ~len:(length a) + +let exists2 p a b = + _exists2 p a.arr b.arr a.i b.i ~len:(min (length a) (length b)) + +(*$T +exists2 (=) (make [| 1;2;3;4 |] 1 ~len:2) (make [| 0;1;3;4 |] 1 ~len:3) +*) + +let _iter2 f a b i j ~len = + for o = 0 to len-1 do + f (Array.get a (i+o)) (Array.get b (j+o)) + done + +let iter2 f a b = + if length a <> length b then invalid_arg "iter2"; + _iter2 f a.arr b.arr a.i b.i ~len:(length a) + +let _fold2 f acc a b i j ~len = + let rec aux acc o = + if o=len then acc + else + let acc = f acc (Array.get a (i+o)) (Array.get b (j+o)) in + aux acc (o+1) + in + aux acc 0 + +let fold2 f acc a b = + if length a <> length b then invalid_arg "fold2"; + _fold2 f acc a.arr b.arr a.i b.i ~len:(length a) + +let shuffle a = + _shuffle Random.int a.arr a.i a.j + +let shuffle_with st a = + _shuffle (Random.State.int st) a.arr a.i a.j + +let random_choose a st = _choose a.arr a.i a.j st + +let pp ?(sep=", ") pp_item buf a = _pp ~sep pp_item buf a.arr a.i a.j + +let pp_i ?(sep=", ") pp_item out a = + _pp_i ~sep (fun k out x -> pp_item (k-a.i) out x) out a.arr a.i a.j + +let to_seq a k = iter k a + +let to_gen a = _to_gen a.arr a.i a.j + +let to_klist a = _to_klist a.arr a.i a.j diff --git a/src/core/CCArray_slice.mli b/src/core/CCArray_slice.mli new file mode 100644 index 00000000..f0ecffd2 --- /dev/null +++ b/src/core/CCArray_slice.mli @@ -0,0 +1,191 @@ + +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Array Slice} *) + +type 'a sequence = ('a -> unit) -> unit +type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist] +type 'a gen = unit -> 'a option +type 'a equal = 'a -> 'a -> bool +type 'a ord = 'a -> 'a -> int +type 'a random_gen = Random.State.t -> 'a +type 'a printer = Format.formatter -> 'a -> unit + +type 'a t +(** Array slice, containing elements of type ['a] *) + +val empty : 'a t + +val equal : 'a equal -> 'a t equal + +val compare : 'a ord -> 'a t ord + +val get : 'a t -> int -> 'a + +val get_safe : 'a t -> int -> 'a option +(** [get_safe a i] returns [Some a.(i)] if [i] is a valid index + @since 0.18 *) + +val make : 'a array -> int -> len:int -> 'a t +(** Create a slice from given offset and length.. + @raise Invalid_argument if the slice isn't valid *) + +val of_slice : ('a array * int * int) -> 'a t +(** Make a sub-array from a triple [(arr, i, len)] where [arr] is the array, + [i] the offset in [arr], and [len] the number of elements of the slice. + @raise Invalid_argument if the slice isn't valid (See {!make}) *) + +val to_slice : 'a t -> ('a array * int * int) +(** Convert into a triple [(arr, i, len)] where [len] is the length of + the subarray of [arr] starting at offset [i] *) + +val to_list : 'a t -> 'a list +(** Convert directly to a list + @since NEXT_RELEASE *) + +val full : 'a array -> 'a t +(** Slice that covers the full array *) + +val underlying : 'a t -> 'a array +(** Underlying array (shared). Modifying this array will modify the slice *) + +val copy : 'a t -> 'a array +(** Copy into a new array *) + +val sub : 'a t -> int -> int -> 'a t +(** Sub-slice *) + +val set : 'a t -> int -> 'a -> unit + +val length : _ t -> int + +val fold : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a + +val foldi : ('a -> int -> 'b -> 'a) -> 'a -> 'b t -> 'a +(** Fold left on array, with index *) + +val fold_while : ('a -> 'b -> 'a * [`Stop | `Continue]) -> 'a -> 'b t -> 'a +(** Fold left on array until a stop condition via [('a, `Stop)] is + indicated by the accumulator + @since 0.8 *) + +val iter : ('a -> unit) -> 'a t -> unit + +val iteri : (int -> 'a -> unit) -> 'a t -> unit + +val blit : 'a t -> int -> 'a t -> int -> int -> unit +(** [blit from i into j len] copies [len] elements from the first array + to the second. See {!Array.blit}. *) + +val reverse_in_place : 'a t -> unit +(** Reverse the array in place *) + +val sorted : ('a -> 'a -> int) -> 'a t -> 'a array +(** [sorted cmp a] makes a copy of [a] and sorts it with [cmp]. + @since NEXT_RELEASE *) + +val sort_indices : ('a -> 'a -> int) -> 'a t -> int array +(** [sort_indices cmp a] returns a new array [b], with the same length as [a], + such that [b.(i)] is the index of the [i]-th element of [a] in [sort cmp a]. + In other words, [map (fun i -> a.(i)) (sort_indices a) = sorted cmp a]. + [a] is not modified. + @since NEXT_RELEASE *) + +val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array +(** [sort_ranking cmp a] returns a new array [b], with the same length as [a], + such that [b.(i)] is the position in [sorted cmp a] of the [i]-th + element of [a]. + [a] is not modified. + + In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. + + Without duplicates, we also have + [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)] + @since NEXT_RELEASE *) + +val find : ('a -> 'b option) -> 'a t -> 'b option +(** [find f a] returns [Some y] if there is an element [x] such + that [f x = Some y], else it returns [None] *) + +val findi : (int -> 'a -> 'b option) -> 'a t -> 'b option +(** Like {!find}, but also pass the index to the predicate function. + @since 0.3.4 *) + +val find_idx : ('a -> bool) -> 'a t -> (int * 'a) option +(** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l], + and [p x] holds. Otherwise returns [None] + @since 0.3.4 *) + +val lookup : ?cmp:'a ord -> 'a -> 'a t -> int option +(** Lookup the index of some value in a sorted array. + @return [None] if the key is not present, or + [Some i] ([i] the index of the key) otherwise *) + +val lookup_exn : ?cmp:'a ord -> 'a -> 'a t -> int +(** Same as {!lookup_exn}, but + @raise Not_found if the key is not present *) + +val bsearch : ?cmp:('a -> 'a -> int) -> 'a -> 'a t -> + [ `All_lower | `All_bigger | `Just_after of int | `Empty | `At of int ] +(** [bsearch ?cmp x arr] finds the index of the object [x] in the array [arr], + provided [arr] is {b sorted} using [cmp]. If the array is not sorted, + the result is not specified (may raise Invalid_argument). + + Complexity: O(log n) where n is the length of the array + (dichotomic search). + + @return + - [`At i] if [cmp arr.(i) x = 0] (for some i) + - [`All_lower] if all elements of [arr] are lower than [x] + - [`All_bigger] if all elements of [arr] are bigger than [x] + - [`Just_after i] if [arr.(i) < x < arr.(i+1)] + - [`Empty] if the array is empty + + @raise Invalid_argument if the array is found to be unsorted w.r.t [cmp] + @since 0.13 *) + +val for_all : ('a -> bool) -> 'a t -> bool + +val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool +(** Forall on pairs of arrays. + @raise Invalid_argument if they have distinct lengths + allow different types @since 0.20 *) + +val exists : ('a -> bool) -> 'a t -> bool + +val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool +(** Exists on pairs of arrays. + @raise Invalid_argument if they have distinct lengths + allow different types @since 0.20 *) + +val fold2 : ('acc -> 'a -> 'b -> 'acc) -> 'acc -> 'a t -> 'b t -> 'acc +(** Fold on two arrays stepwise. + @raise Invalid_argument if they have distinct lengths + @since 0.20 *) + +val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit +(** Iterate on two arrays stepwise. + @raise Invalid_argument if they have distinct lengths + @since 0.20 *) + +val shuffle : 'a t -> unit +(** Shuffle randomly the array, in place *) + +val shuffle_with : Random.State.t -> 'a t -> unit +(** Like shuffle but using a specialized random state *) + +val random_choose : 'a t -> 'a random_gen +(** Choose an element randomly. + @raise Not_found if the array/slice is empty *) + +val to_seq : 'a t -> 'a sequence +val to_gen : 'a t -> 'a gen +val to_klist : 'a t -> 'a klist + +(** {2 IO} *) + +val pp: ?sep:string -> 'a printer -> 'a t printer +(** Print an array of items with printing function *) + +val pp_i: ?sep:string -> (int -> 'a printer) -> 'a t printer +(** Print an array, giving the printing function both index and item *)