//
// Course: BU CAS CS 520
// Instructor: Hongwei Xi (hwxi AT cs DOT bu DOT edu)
//

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

//
// How to compile:
//   atscc -o isqrt -O3 isqrt.dats
//
// How to test:
//   ./isqrt [integer]
//

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

(*
// this one is buggy
fn isqrt (x: int): int = let
  fun search (x: int, l: int, r: int): int = let
    val diff = r - l
  in
    case+ 0 of
    | _ when diff > 0 => let
        val m = l + (diff / 2)
      in
        if x / m < m then search (x, l, m) else search (x, m, r)
      end // end of [if]
    | _ => l
  end // end of [search]
in
  search (x, 0, x)
end // end of [isqrt]
*)

fn isqrt {x:nat} (x: int x): int = let
  fun search {x,l,r:nat | l <= r} .<r-l>. // invariant: x < r * r!!!
    (x: int x, l: int l, r: int r): int = let
    val diff = r - l
  in
    case+ 0 of
    | _ when diff >= 2 => let
        val m = l + (diff / 2)
      in
        if div_int_int (x, m) < m then search (x, l, m) else search (x, m, r)
      end // end of [if]
    | _ => l
  end (* end of [search] *)
in
  if x >= 2 then search (x, 0, x) else x
end // end of [isqrt]

implement main (argc, argv) = let
  val () = assert (argc >= 2)
  val n = int1_of argv.[1]
  val () = assert (n >= 0)
in
  printf ("isqrt (%i) = %i\n", @(n, isqrt n))
end // end of [main]

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

(* end of [isqrt.dats] *)