(* File: gnuplot_arr_ba.ml

   These are the functions that will be specialized for bigarray and
   array by the program "gnuplot_make.ml".

   Copyright (C) 2001-2004

     Christophe Troestler
     email: Christophe.Troestler@umh.ac.be
     WWW: http://math.umh.ac.be/an/software/

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public License
   version 2.1 as published by the Free Software Foundation, with the
   special exception on linking described in file LICENSE.

   This library is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the file
   LICENSE for more details.
*)
(* 	$Id: gnuplot_arr_ba.ml,v 1.6 2004-11-28 23:49:39 chris_77 Exp $	 *)

open Printf

(* Specialized for efficiency *)
let min (x:int) y = if x < y then x else y

let min4 (x:int) y z t = min (min x y) (min z t)

let data_of_float x =
  if is_finite x then string_of_float x else "?"

(*
 * 2D Plots
 **********************************************************************)

let x g ?(tag=system_tag) ?(style=Lines) ?(label="") ?(n0=LOWER)
  ?(ofsx=LOWER) ?(incx=1) (xvec:VEC) =
  if g.closed then failwith "Gnuplot.ARRAY.x: handle closed";
  if ofsx < LOWER || ofsx > UPPER(xvec) then
    invalid_arg "Gnuplot.x: ofsx invalid";
  if incx < 1 then invalid_arg "Gnuplot.x: incx < 1";
  let rec loop i n out =
    if i <= UPPER(xvec) then begin
      out (string_of_int n ^ " " ^ data_of_float(GET(xvec,i)) ^ "\n");
      loop (i + incx) (n + 1) out
    end in
  plot_data g ~inline_if:(1 + (UPPER(xvec) - ofsx) / incx <= g.max_inline)
    (loop ofsx n0) ~axes ~label ~style:(with_style g style) ~tag


let xy g ?(tag=system_tag) ?(style=Lines) ?(label="")
  ?(ofsx=LOWER) ?(incx=1) (xvec:VEC)
  ?(ofsy=LOWER) ?(incy=1) (yvec:VEC) =
  if g.closed then failwith "Gnuplot.ARRAY.xy: handle closed";
  if ofsx < LOWER || ofsx > UPPER(xvec) || incx < 1
    || ofsy < LOWER || ofsy > UPPER(yvec) || incy < 1 then
    invalid_arg "Gnuplot.xy: ofsx, incx, ofxy, or incy invalid";
  let rec loop i j out =
    if i <= UPPER(xvec) && j <= UPPER(yvec) then begin
      out (data_of_float(GET(xvec,i)) ^ " " ^
           data_of_float(GET(yvec,i)) ^ "\n");
      loop (i + incx) (j + incy) out
    end in
  let nlines =
    1 + min ((UPPER(xvec) - ofsx) / incx) ((UPPER(yvec) - ofsy) / incy) in
  plot_data g ~inline_if:(nlines <= g.max_inline)
    (loop ofsx ofsy) ~axes ~label ~style:(with_style g style) ~tag


let bin g ?(tag=system_tag) ?(label="") ?(center=true)
  ?(ofsx=LOWER) ?(incx=1) (xvec:VEC)
  ?(ofsy=LOWER) ?(incy=1) (yvec:VEC) =
  if g.closed then failwith "Gnuplot.ARRAY.bin";
  if ofsx < LOWER || ofsx + incx > UPPER(xvec) (* >= 2 points *) || incx < 1
    || ofsy < LOWER || ofsy > UPPER(yvec) || incy < 1 then
      invalid_arg "Gnuplot.bin";
  if center then begin
    let rec loop i j out =
      if i <= UPPER(xvec) && j <= UPPER(yvec) then begin
        let xlow =
          if i = ofsx then
            0.5 *. (3. *. GET(xvec,ofsx) -. GET(xvec,ofsx + incx))
          else 0.5 *. (GET(xvec,i - incx) +. GET(xvec,i))
        and xup =
          if i + incx > UPPER(xvec) then
            0.5 *. (3. *. GET(xvec,i) -. GET(xvec,i - incx))
          else 0.5 *. (GET(xvec,i) +. GET(xvec,i + incx))
        and yi = GET(yvec,j) in
        if is_finite xlow && is_finite xup && is_finite yi then
          let b0 = string_of_float xlow
          and b1 = string_of_float xup
          and y  = string_of_float yi in
          out(sprintf "%s 0.\n%s %s\n%s %s\n%s 0.\n" b0 b0 y b1 y b1)
        else out "? ?\n";
        loop (i + incx) (j + incy) out
      end in
    let nbin =
      1 + min ((UPPER(xvec) - ofsx) / incx) ((UPPER(yvec) - ofsy) / incy) in
    plot_data g ~inline_if:(4 * nbin <= g.max_inline)
      (loop ofsx ofsy) ~axes ~label ~style:(with_style g Lines) ~tag
  end
  else begin
    let rec loop is_finite_xi sxi i1 j out =
      if i1 <= UPPER(xvec) && j <= UPPER(yvec) then begin
        let xi1 = GET(xvec,i1)
        and yi = GET(yvec,j) in
        let is_finite_xi1 = is_finite xi1
        and sxi1 = string_of_float xi1 in
        if is_finite_xi && is_finite_xi1 && is_finite yi then
          let syi = string_of_float yi in
          out(sprintf "%s 0.\n%s %s\n%s %s\n%s 0.\n" sxi sxi syi sxi1 syi sxi1)
        else out "? ?\n";
        loop is_finite_xi1 sxi1 (i1 + incx) (j + incy) out
      end in
    let x0 = GET(xvec,ofsx) in
    let nbin = 1 + min ((UPPER(xvec) - ofsx - incx) / incx)
                 ((UPPER(yvec) - ofsy) / incy) in
    plot_data g ~inline_if:(4 * nbin <= g.max_inline)
      (loop (is_finite x0) (string_of_float x0) (ofsx + incx) ofsy)
      ~axes ~label ~style:(with_style g Lines) ~tag
  end


let vector g ?(tag=system_tag) ?(label="")
  ?(ofsx=LOWER) ?(incx=1) (xvec:VEC)
  ?(ofsy=LOWER) ?(incy=1) (yvec:VEC)
  ?(ofsdx=LOWER) ?(incdx=1) (dxvec:VEC)
  ?(ofsdy=LOWER) ?(incdy=1) (dyvec:VEC) =
  if g.closed then failwith "Gnuplot.ARRAY.vector";
  if ofsx < LOWER || ofsx > UPPER(xvec) || incx < 1
    || ofsy < LOWER || ofsy > UPPER(yvec) || incy < 1
    || ofsdx < LOWER || ofsdx > UPPER(dxvec) || incdx < 1
    || ofsdy < LOWER || ofsdy > UPPER(dyvec) || incdy < 1 then
    invalid_arg "Gnuplot.vector";
  let rec loop i j i' j' out =
    if i <= UPPER(xvec) && i <= UPPER(yvec)
      && i' <= UPPER(dxvec) && j' <= UPPER(dyvec) then begin
        let xi = GET(xvec,i)
        and yi = GET(yvec,j)
        and dxi = GET(dxvec,i')
        and dyi = GET(dyvec,j') in
        if is_finite xi && is_finite yi && is_finite dxi && is_finite dyi then
          out (sprintf "%F %F %F %F\n" xi yi dxi dyi)
        else out "? ? ? ?\n";
        loop (i + incx) (j + incy) (i' + incdx) (j' + incdy) out
      end in
  let nlines =
    1 + min4 ((UPPER(xvec) - ofsx) / incx) ((UPPER(yvec) - ofsy) / incy)
      ((UPPER(dxvec) - ofsdx) / incdx) ((UPPER(dyvec) - ofsdy) / incdy) in
  plot_data g ~inline_if:(nlines <= g.max_inline)
    (loop ofsx ofsy ofsdx ofsdy) ~axes ~label ~style:with_vector ~tag


let err g ?(tag=system_tag)
  ?(xerr:VEC option) (xvec:VEC) ?(yerr:VEC option) (yvec:VEC) =
  if g.closed then failwith "Gnuplot.ARRAY.err";
  failwith "Gnuplot.err not yet implemented"


(*
 * 3D Plots
 **********************************************************************)

let xyz g ?(tag=system_tag) ?(style=Lines) ?(label="")
  (xvec:VEC) (yvec:VEC) (zmat:MAT) =
  if g.closed then failwith "Gnuplot.ARRAY.xyz";
  let loop out =
    for i = LOWER to UPPER(xvec) do
      let xi = GET(xvec,i) in
      if is_finite xi then begin
        for j = LOWER to UPPER(yvec) do
          let yi = GET(yvec,j)  in
          let zij = GET2(zmat,i,j) in
          if is_finite yi && is_finite zij then
            out (sprintf "%F %F %F\n" xi yi zij)
          else out "? ? ?\n"
        done
      end
      else out "? ? ?\n"
    done in
  let nlines =
    (1 + UPPER(xvec) - LOWER) * (1 + UPPER(yvec) - LOWER) in
  plot_data g ~inline_if:(nlines <= g.max_inline)
    loop ~axes:no_axes ~label ~style:(with_style g style) ~tag
