//
// K&R, 2nd edition, page 64
//

(*

/*
** itoa: convert [n] to characters
*/
void itoa (int n, char s[]) {
  int i, sgn ;
  if ((sgn = n) < 0) n = -n ;
  i = 0 ;
  do { /* generate digits in reverse order */
    s[i++] = n % 10 + '0' ;
  } while ((n /= 10) > 0) ;
  if (sgn < 0) s[i++] = '-' ;
  s[i] = '\0' ;
  reverse (s) ;
  return ;
} /* end of [iota] */

*)

(* ****** ****** *)

dataview
itoa_v (bsz:int, l:addr, int) =
  | itoa_v_succ (bsz, l, 0) of strbuf bsz @ l
  | itoa_v_fail (bsz, l, ~1) of b0ytes bsz @ l
// end of [itoa_v]

// [itoa_err] reports an error if the buffer is not long enough
extern fun itoa_err {bsz:pos} {l:addr}
  (pf_buf: b0ytes bsz @ l | n: int, p_buf: ptr l, bsz: int bsz)
  :<> [p:int] (itoa_v (bsz, l, p) | int p)
// end of [itoa_err]

(* ****** ****** *)

typedef chars (n:int) = @[char][n]

(* ****** ****** *)

%{^
ats_char_type
char_of_digit (ats_int_type i) { return (i + '0') ; }
%}
extern fun char_of_digit (d: natLt 10):<> char = "char_of_digit"

(* ****** ****** *)

%{^
ats_void_type
strbuf_reverse (ats_ptr_type s0) {
  char *s = (char*)s0 ; int c, i, j ;
  for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
    c = s[i]; s[i] = s[j]; s[j] = c;
  }
  return ;
} /* end of [strbuf_reverse] */
%}
extern
fun strbuf_reverse {m,n:nat | n < m}
  (s: &strbuf (m, n)):<> void = "strbuf_reverse"
// end of [strbuf_reverse]

(* ****** ****** *)
  
implement
itoa_err {bsz} {l}
  (pf_buf | n, p_buf, bsz) = let
  fun loop
    (n: Nat, buf: &chars bsz, bsz: int bsz, i: &Nat): void =
    if i < bsz then begin
      if n > 0 then let
        val d = n nmod 10
        val () = buf.[i] := char_of_digit (d)
        val () = i := i + 1
      in
        loop (n / 10, buf, bsz, i)
      end else begin
        // loop exits normally
      end // end of [if]
    end // end of [if]
  // end of [loop]
  var i: Nat = 0
  prval pf1_buf = chars_v_of_b0ytes_v (pf_buf)
  val n = int1_of_int n
  val n_abs = (if n >= 0 then n else ~n): Nat
  val () = $effmask_all (loop (n_abs, !p_buf, bsz, i))
  val () = if i = 0 then (p_buf->[0] := '0'; i := 1)
  val () = if n < 0 then begin
    if i < bsz then (p_buf->[i] := '-'; i := i + 1)
  end // end of [val]
in
  if i < bsz then let
    prval pf_buf = bytes_v_of_chars_v (pf1_buf)
    val () = bytes_strbuf_trans (pf_buf | p_buf, size1_of_int1 i)
    val () = strbuf_reverse (!p_buf)
  in
    (itoa_v_succ (pf_buf) | 0)
  end else let
    prval pf_buf = bytes_v_of_chars_v (pf1_buf)
  in
    (itoa_v_fail (pf_buf) | ~1)
  end // end of [if]
end // end of [itoa_err]

(* ****** ****** *)

implement main (argc, argv) = let
  val () = assert (argc >= 2)
  val n = int_of_string (argv.[1])
  #define bsz 16
  var !p_buf with pf_buf = @[byte][bsz]()
  val (pf_itoa | err) = itoa_err (pf_buf | n, p_buf, bsz)
in
  if :(pf_buf: b0ytes bsz @ p_buf) => err >= 0 then let
    prval itoa_v_succ pf1_buf = pf_itoa
    val () = print (__cast p_buf) where {
      extern castfn __cast (p: ptr): string 
    }
    val () = print_newline ()
    prval () = pf_buf := bytes_v_of_strbuf_v (pf1_buf)
  in
    // empty
  end else let // err < 0
    prval itoa_v_fail pf1_buf = pf_itoa
    val () = (print "?"; print_newline ())
    prval () = pf_buf := pf1_buf
  in
    // empty
  end // end of [if]
end // end of [main]

(* ****** ****** *)

(* end of [itoa.dats] *)