(*
** Course: Concepts of Programming Languages (BU CAS CS 320)
** Semester: Summer I, 2009
** Instructor: Hongwei Xi (hwxi AT cs DOT bu DOT edu)
*)

//
// Author: Hongwei Xi (hwxi AT cs DOT bu DOT edu)
// Time: Wednesday, June 10, 2009
//

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

#include "BUCASCS320.hats"

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

// Part I: computing primes based on lazy evaluation

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

// exception StreamSubscript of () // defined in BUCASCS320.hats

val theNaturalNumberStream : stream int = from (0) where {
  fun from (n:int):<!laz> stream int = $delay (stream_cons (n, from (n+1)))
} // end of [val]

// stream_car
fun{a:t@ype} stream_car
  (s: stream a):<!laz> a = case+ !s of
  | stream_cons (x, _) => x
  | stream_nil () => $raise StreamSubscript ()
// end of [stream_car]

// stream_cdr
fun{a:t@ype} stream_cdr
  (s: stream a):<!laz> stream a = case+ !s of
  | stream_cons (_, s1) => s1
  | stream_nil () => $raise StreamSubscript ()
// end of [stream_cdr]

#define car stream_car
#define cdr stream_cdr

fun{a:t@ype} stream_nth
  (s: stream a, n: int):<!laz> a = case+ !s of
  | stream_cons (x, s1) => if n = 0 then x else stream_nth (s1, n-1)
  | stream_nil () => $raise StreamSubscript ()
// end of [stream_nth]

val n1000 = stream_nth (theNaturalNumberStream, 1000)
val () = printf ("n1000 = %i\n", @(n1000))

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

fun sieve (s: stream int):<!laz> stream int = $delay (let
  val- stream_cons (p1, s1) = !s
  val s1 = stream_filter_cloref<int> (s1, lam x => x mod p1 <> 0)
in    
  stream_cons (p1, sieve (s1))
end) // end of [sieve]

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

val thePrimeNumberStream = sieve (cdr (cdr theNaturalNumberStream))

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

fn prime_nth_get (n: int) = stream_nth (thePrimeNumberStream, n-1)

val p5 = stream_nth (thePrimeNumberStream, 5-1)
val () = printf ("p5 = %i\n", @(prime_nth_get 5))
val () = printf ("p100 = %i\n", @(prime_nth_get 100))
val () = printf ("p1000 = %i\n", @(prime_nth_get 1000))
val () = printf ("p5000 = %i", @(prime_nth_get 5000))
val () = print_newline ()
val () = print ("Testing memoization: no pause shoule be experienced!")
val () = print_newline ()
val () = printf ("p5000 = %i", @(prime_nth_get 5000))
val () = print_newline ()
// val () = printf ("p10000 = %i\n", @(prime_nth_get 10000))

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

// Part II: line count based on lazy evaluation 

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

fun file_line_count (inp: FILEref): int = let
  val cs = char_stream_make_file (inp)
  fun loop (cs: stream char, cnt: int): int =
    case !cs of
    | stream_cons (c, cs) =>
        (if c = '\n' then loop (cs, cnt+1) else loop (cs, cnt))
    | stream_nil () => cnt
  // end of [loop]
in
  loop (cs, 0)
end // end of [file_line_count]

(*

implement main (argc, argv) = let
  val inp = (
    if argc >= 2 then open_file (argv.[1], file_mode_r) else stdin_ref
  ) : FILEref
  val cnt = file_line_count (inp)
in
  printf ("The number of lines is [%i]\n", @(cnt)) 
end (* end of [main] *) 

*)

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

// Part III: computer Pi based on lazy evaluation and Euler's transform

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

// pi/4 = 1/1 - 1/3 + 1/5 - 1/7 + ...

val thePiStream =
  helper (0.0(*sum*), 1(*sign*), 1.0(*denom*)) where {
  fun helper
    (sum: double, sgn: int, denom: double)
    :<!laz> stream double = $delay (
    if sgn > 0 then let
      val sum1 = sum + 4.0 / denom in
      stream_cons {double} (sum1, helper (sum1, ~1, denom + 2.0))
    end else let // sgn = ~1 
      val sum1 = sum - 4.0 / denom in
      stream_cons {double} (sum1, helper (sum1,  1, denom + 2.0))
    end // end of [if]
  ) // end of [helper]
} (* end of [val] *)

local

#define :: stream_cons
#define nil stream_nil

in // in of [local]

fun euler_trans
  (ss0: stream double):<!laz> stream double = $delay (let
  val- s0 :: ss1 = !ss0
  val- s1 :: ss2 = !ss1
  val- s2 :: ss3 = !ss2
  val s01 = s0 - s1 and s21 = s2 - s1
in
  (s2 - s21 * s21 / (s01 + s21)) :: euler_trans ss1
end : stream_con double
) // end of [euler_trans]

end // end of [local]

val thePiStream1 = euler_trans (thePiStream)

val sum = stream_nth (thePiStream, 100)
val () = printf ("thePiStream_100 = %.8f\n", @(sum))

val sum = stream_nth (thePiStream1, 100)
val () = printf ("thePiStream1_100 = %.8f\n", @(sum))

(*
fun euler_trans_many
  (ss: stream double, n: int):<!laz> stream double =
  if n > 0 then let
    val ss = euler_trans (ss) in euler_trans_many (ss, n-1)
  end else ss
// end of [euler_trans_many]    
*)

fun euler_trans_tableau
  (ss: stream double):<!laz> stream (stream double) =
  $delay (stream_cons (ss, euler_trans_tableau (euler_trans ss)))
// end of [euler_trans_tableau]

val thePiStreamDiag =
  stream_map_fun<stream double><double>
    (euler_trans_tableau thePiStream, stream_car)
// end of [thePiStreamDiag]

val () = print "thePiStreamDiag:\n"
val sum = stream_nth (thePiStreamDiag, 0)
val () = printf ("thePiStreamDiag_0 = %.10f\n", @(sum))
val sum = stream_nth (thePiStreamDiag, 1)
val () = printf ("thePiStreamDiag_1 = %.10f\n", @(sum))
val sum = stream_nth (thePiStreamDiag, 2)
val () = printf ("thePiStreamDiag_2 = %.10f\n", @(sum))
val sum = stream_nth (thePiStreamDiag, 3)
val () = printf ("thePiStreamDiag_3 = %.10f\n", @(sum))
val sum = stream_nth (thePiStreamDiag, 4)
val () = printf ("thePiStreamDiag_4 = %.10f\n", @(sum))
val sum = stream_nth (thePiStreamDiag, 5)
val () = printf ("thePiStreamDiag_5 = %.10f\n", @(sum))
val sum = stream_nth (thePiStreamDiag, 6)
val () = printf ("thePiStreamDiag_6 = %.10f\n", @(sum))
val sum = stream_nth (thePiStreamDiag, 7)
val () = printf ("thePiStreamDiag_7 = %.10f\n", @(sum))
val sum = stream_nth (thePiStreamDiag, 8)
val () = printf ("thePiStreamDiag_8 = %.10f\n", @(sum))
(*
val sum = stream_nth (thePiStreamDiag, 9)
val () = printf ("thePiStreamDiag_9 = %.10f\n", @(sum))
*)

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

implement main () = ()

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

(* end of [code-2009-06-10.dats] *)