open Core.Std
include Stream
let next_exn = next
let next s = try Some (next_exn s) with Stream.Failure -> None
let npeek s n = npeek n s
let is_empty s =
  match peek s with
  | None -> true
  | Some _ -> false
let empty () = from (const None)
let to_stream x = x
let of_stream x = x
exception Expected_streams_of_equal_length
let of_list l =
  let lr = ref l in
  let f _ = match !lr with
    | h :: t -> lr := t ; Some h
    | [] -> None
  in
  from f
let rec iteri xs ~f =
  match peek xs with
  | Some x -> f (count xs) x ; junk xs ; iteri xs ~f
  | None -> ()
let iter xs ~f = iteri xs ~f:(const f)
let rec iter2i_exn xs ys ~f =
  match peek xs, peek ys with
  | Some x, Some y -> (
    f (count xs) (count ys) x y;
    junk xs ;
    junk ys ;
    iter2i_exn xs ys ~f
  )
  | None, None -> ()
  | _, _ -> raise Expected_streams_of_equal_length
let iter2_exn xs ys ~f = iter2i_exn xs ys ~f:(const (const f))
let iter2i a b ~f =
  try iter2i_exn a b ~f
  with Expected_streams_of_equal_length -> ()
let iter2 a b ~f =
  try iter2_exn a b ~f
  with Expected_streams_of_equal_length -> ()
let rec find_map xs ~f =
  match next xs with
  | Some x -> (
    match f x with
    | Some x as y -> y
    | None -> find_map xs ~f
  )
  | None -> None
let find xs ~f = find_map xs ~f:(fun x -> if f x then Some x else None)
let find_exn xs ~f = match find xs ~f with
| Some x -> x
| None -> raise Not_found
let exists xs ~f = match find xs ~f with
| Some _ -> true
| None -> false
let rec for_all xs ~f =
  match next xs with
  | Some x when not (f x) -> false
  | Some _ -> for_all xs ~f
  | None -> true
let rec foldi xs ~init ~f =
  match next xs with
  | None -> init
  | Some x -> foldi xs ~init:(f (count xs - 1) init x) ~f
  
let fold xs ~init ~f = foldi xs ~init ~f:(const f)
let reduce xs ~f =
  match next xs with
  | Some init -> fold xs ~init ~f
  | None -> invalid_arg "Stream.reduce: stream should contain at least one element"
let sum = reduce ~f:( + )
let fsum = reduce ~f:( +. )
let rec fold2i_exn xs ys ~init ~f =
  match next xs, next ys with
  | Some x, Some y ->
      let init = f (count xs - 1) (count ys - 1) init x y in
        
      fold2i_exn xs ys ~init ~f
  | None, None -> init
  | _ -> raise Expected_streams_of_equal_length
let fold2_exn xs ys ~init ~f = fold2i_exn xs ys ~init ~f:(const (const f))
let rec fold2i xs ys ~init ~f =
  match next xs, next ys with
  | Some x, Some y ->
      let init = f (count xs - 1) (count ys - 1) init x y in
      fold2i xs ys ~init ~f
  | _ -> init
let fold2 xs ys ~init ~f = fold2i xs ys ~init ~f:(const (const f))
let scanl xs ~init ~f =
  let current = ref init in
  let f i =
    if i = 0 then Some init
    else (
      match next xs with
      | Some x ->
          current := f !current x ;
          Some !current
      | None -> None
    )
  in
  from f
let scan xs ~f =
  match next xs with
  | Some init -> scanl xs ~init ~f
  | None -> empty ()
let take_whilei xs ~f =
  let aux i =
    match peek xs with
    | Some x when f i x -> junk xs ; Some x
    | _ -> None
  in
  from aux
let take_while xs ~f = take_whilei xs ~f:(const f)
let take xs ~n =
  take_whilei xs ~f:(fun j _ -> j < n)
let rec drop_whilei xs ~f =
  match peek xs with
  | Some x when f (count xs) x -> junk xs ; drop_whilei xs ~f
  | _ -> ()
let drop_while xs ~f = drop_whilei xs ~f:(const f)
let drop xs ~n =
  drop_whilei xs ~f:(fun j _ -> j < n)
let skip_whilei xs ~f =
  drop_whilei xs ~f ;
  from (fun _ -> next xs)
let skip_while xs ~f = skip_whilei xs ~f:(const f)
let skip xs ~n =
  drop xs ~n ;
  from (fun _ -> next xs)
let span xs ~f =
    
  let queue           = Queue.create ()
  and read_from_queue = ref false in
  let head _ =
    if !read_from_queue then 
      Queue.dequeue queue    
    else
      match peek xs with
      | Some x as e when f x -> junk xs ; e
      | _ -> None
  and tail _ =
    if not !read_from_queue then 
      begin
        read_from_queue := true;
        let rec aux () =
          match peek xs with
          | Some x when f x -> Queue.enqueue queue x ; aux ()
          | e -> e
        in aux ()
      end
    else next xs
  in
  (from head, from tail)
let group_aux xs map eq =
  let prev_group_force = ref ignore in
  let for_each_group _ =
    !prev_group_force () ;
    match next xs with
    | None -> None
    | Some x ->
        let queue = Queue.create ()
        and forced = ref false
        and mapped_x = map x in
        let aux i =
          if i = 0 then Some x
          else (
            if !forced then
              Queue.dequeue queue
            else (
              match peek xs with
              | Some y as e when eq (map y) mapped_x -> junk xs ; e
              | _ -> None
            )
          ) in
        let force () =
          forced := true ;
          let rec loop () =
            match peek xs with
            | Some y when eq (map y) mapped_x ->
                junk xs ;
                Queue.enqueue queue y ;
                loop ()
            | _ -> ()
          in
          loop ()
        in
        prev_group_force := force ;
        Some (from aux)
  in
  from for_each_group
let group xs ~f = group_aux xs f ( = )
let group_by xs ~eq = group_aux xs ident eq
let mapi xs ~f =
  let aux i = Option.map (next xs) ~f:(f i) in
  from aux
let map xs ~f =
  let aux _ = Option.map (next xs) ~f in
  from aux
let filter xs ~f =
  let rec aux i =
    match next xs with
    | Some x when not (f x) -> aux i
    | x -> x
  in
  from aux
let filter_map xs ~f =
  let rec aux i =
    match next xs with
    | Some x -> (
      match f x with
      | None -> aux i
      | x -> x
    )
    | None -> None
  in
  from aux
let append xs ys =
  let aux _ =
    match next xs with
    | None -> next ys
    | e -> e
  in
  from aux
let concat xs =
  let rec find_next_non_empty_stream xs =
    match peek xs with
    | Some stream when is_empty stream ->
        junk xs ;
        find_next_non_empty_stream xs
    | x -> x
  in
  let current = ref (empty ()) in
  let aux _ =
    match next !current with
    | None -> (
        match find_next_non_empty_stream xs with
        | None -> None
        | Some stream ->
          current := stream ;
          next stream
    )
    | x -> x
  in
  from aux
let combine (xs, ys) =
  let aux _ =
    match peek xs, peek ys with
    | Some x, Some y ->
        junk xs ;
        junk ys ;
        Some (x,y)
    | _ -> None
  in
  from aux
let uncombine xs =
  let whosfirst = ref `left
  and lq = Queue.create ()
  and rq = Queue.create () in
  let rec left i =
    match !whosfirst with
    | `left -> (
      match next xs with
      | None -> None
      | Some (l,r) ->
          Queue.enqueue rq r ;
          Some l
    )
    | `right -> (
      match Queue.dequeue lq with
      | None ->
          whosfirst := `left ;
          left i
      | x -> x
    )
  and right i =
    match !whosfirst with
    | `right -> (
      match next xs with
      | None -> None
      | Some (l,r) ->
          Queue.enqueue lq l ;
          Some r
    )
    | `left -> (
      match Queue.dequeue rq with
      | None ->
          whosfirst := `right ;
          right i
      | x -> x
    )
  in
  from left, from right
let merge xs ys ~cmp =
  let aux _ =
    match peek xs, peek ys with
    | Some x as ex, Some y when cmp x y <= 0 -> junk xs ; ex
    | Some _, (Some _ as ey) -> junk ys ; ey
    | Some _ as ex, None -> junk xs ; ex
    | None, (Some _ as ey) -> junk ys ; ey
    | None, None -> None
  in
  from aux
let partition xs ~f =
  let pos_queue = Queue.create ()
  and neg_queue = Queue.create () in
  let rec pos i =
    match Queue.dequeue pos_queue with
    | None -> (
      match next xs with
      | Some x when not (f x) -> Queue.enqueue neg_queue x ; pos i
      | e -> e
    )
    | e -> e
  and neg i =
    match Queue.dequeue neg_queue with
    | None -> (
      match next xs with
      | Some x when f x -> Queue.enqueue pos_queue x ; neg i
      | e -> e
    )
    | e -> e
  in
  from pos, from neg
let uniq xs =
  match peek xs with
  | None -> empty ()
  | Some first ->
      let prev = ref first in
      let rec aux i =
        if i = 0 then Some first
        else (
          match next xs with
          | None -> None
          | Some x ->
              if x = !prev then
                aux i
              else (
                prev := x ;
                Some x
              )
        )
      in
      from aux
let init n ~f =
  if n < 0 then empty ()
  else (
    let aux i =
      if i < n then Some (f i)
      else None
    in
    from aux
  )
let singleton x = init 1 (const x)
let to_list t =
  List.rev (fold ~init:[] ~f:(fun l b -> b::l) t)
let result_to_exn s ~error_to_exn =
  from (fun _ ->
    match next s with
    | None -> None
    | Some result -> match result with
      | Ok x -> Some x
      | Result.Error x -> raise (error_to_exn x)
  )
let unfoldi init ~f =
  let a = ref init in
  from (fun i -> match f i !a with
    | Some (b, a_next) -> (a := a_next; Some b)
    | None -> None
  )
let unfold init ~f = unfoldi init ~f:(const f)
let range ?until n =
  let stop = Option.value_map until ~default:(fun _ -> false) ~f:( < ) in
  unfold n (fun i -> if stop i then None else Some (i, i + 1))
let of_lazy s =
  let next i = next (Lazy.force s) in
  from next
let strings_of_channel ?(buffer_size=65536) inp =
  let buf = String.create buffer_size in
  from (fun _ ->
    match In_channel.input inp ~buf ~pos:0 ~len:buffer_size with
    | 0 -> None
    | len -> Some (String.sub buf ~pos:0 ~len)
  )
module Infix = struct
  let ( -- ) x y = range x ~until:y
  let ( --. ) (a, step) b =
    let n = Int.of_float ((b -. a) /. step) + 1 in
    if n < 0 then
      empty ()
    else
      init n ~f:(fun i -> Float.of_int i *. step +. a)
  let ( --^ ) x y = range x ~until:(y-1)
  let ( --- ) x y =
    if x <= y then x -- y
    else unfold x ~f:(fun prev -> if prev >= y then Some (prev, prev - 1) else None)
  let ( /@ ) x f = map x ~f
  let ( // ) x f = filter x ~f
  let ( //@ ) x f = filter_map x ~f
end