open OUnit2

let rot13 b = if (b >= 'a' && b <= 'z') then
    let res = int_of_char b+13 in
    char_of_int (res - (int_of_char 'z' - int_of_char 'a' + 1)
		 * (if char_of_int res <= 'z' then 0 else 1))
  else if (b >= 'A' && b <= 'Z') then
    let res = int_of_char b+13 in
    char_of_int (res - (int_of_char 'Z' - int_of_char 'A' + 1)
		 * (if char_of_int res <= 'Z' then 0 else 1))
  else b;;

let id x = x;;

let norm (x,y) = sqrt(x *. x +. y *. y);;

let mod_float (x,y) = x -. float_of_int(int_of_float (x /. y)) *. y

let tests = "Misc" >:::
  [
    "f" >:: ( fun test_ctxt ->
      assert_equal ~msg:"Test Misc.f.1 failed" "Hello!" (Misc.f id "Hello!");
      assert_equal ~msg:"Test Misc.f.2 failed" "Uryyb!" (Misc.f rot13 "Hello!")
    );

    "g" >:: ( fun test_ctxt ->
      Misc.x := 'b';
      assert_equal ~msg:"Test Misc.g.1 failed" 'b' !Misc.x;
      Misc.g Misc.x;
      assert_equal ~msg:"Test Misc.g.2 failed" 'a' !Misc.x;
      Misc.g Misc.x;
      assert_equal ~msg:"Test Misc.g.3 failed" 'a' !Misc.x;
    );

    "re" >:: ( fun test_ctxt ->
      assert_equal ~msg:"Test Misc.re.1 failed" 0.2
	(Misc.re { Misc.x=0.2; Misc.y=0.8 })
    );

    "diag" >:: ( fun test_ctxt ->
      let res : Misc.complex = { Misc.x=3.14; Misc.y=3.14 } in
      assert_equal ~msg:"Test Misc.diag.1 failed" res (Misc.diag 3.14)
    );

    "map_complex" >:: ( fun test_ctxt ->
      let res : Misc.complex = { Misc.x=0.7; Misc.y=0.7 } in
      assert_equal ~msg:"Test Misc.map_complex.1 failed"
	(hypot res.Misc.x res.Misc.y) (Misc.map_complex norm res);
      let res2 : Misc.complex = { Misc.x=18.6; Misc.y=4.2 } in
      assert_equal ~msg:"Test Misc.map_complex.2 failed"
	(mod_float (res2.Misc.x, res2.Misc.y)) (Misc.map_complex mod_float res2)
    );

    "pp" >:: ( fun test_ctxt ->
      assert_equal ~msg:"Test Misc.pp.1 failed" "(`C,3)" (Misc.pp (Misc.C(3)));
      assert_equal ~msg:"Test Misc.pp.2 failed"
	"(`A,(`C,3))" (Misc.pp (Misc.A(Misc.C(3))));
      assert_equal ~msg:"Test Misc.pp.3 failed"
	"(`B,((`C,3),(`A,(`C,2))))" (Misc.pp (Misc.B(Misc.C(3),Misc.A(Misc.C(2)))));
    );

    "find" >:: ( fun test_ctxt ->
      let exp = Misc.A(Misc.B(Misc.C(3),Misc.A(Misc.C(7)))) in
      assert_equal ~msg:"Test Misc.find.1 failed" true (Misc.find exp 3);
      assert_equal ~msg:"Test Misc.find.2 failed" false (Misc.find exp 5);
    );

    "exists" >:: ( fun test_ctxt ->
      assert_equal ~msg:"Test Misc.exists.1 failed" false
	(Misc.exists "toto.tmp");
      let file = bracket_tmpfile test_ctxt in match file with
	| (name, _) -> assert_equal
	  ~msg:"Test Misc.exists.2 failed" true (Misc.exists name);
    );

    "str_len" >:: ( fun test_ctxt ->
      let str = "This is an example." in
      assert_equal ~msg:"Test Misc.str_len.1 failed" 19 (Misc.str_len str);
      assert_equal ~msg:"Test Misc.str_len.2 failed" 23
	(Misc.str_len (str^"hack"));
    );

    "unix_write" >:: ( fun test_ctxt ->
      let h = Sys.getenv "HOME" in
      assert_equal ~msg:"Test Misc.unix_write.easy failed"
	(String.length h) (Misc.unix_write Unix.stderr h 0 (String.length h));
      assert_equal ~msg:"Test Misc.unix_write.hard failed"
	(Misc.str_len Misc.home) (Misc.unix_write Misc.stdin Misc.home 0
				    (Misc.str_len Misc.home))
    );

    "listmap" >:: ( fun test_ctxt ->
      let bioi = Big_int.big_int_of_int in
      let lst = Misc.listmap (fun x -> Big_int.mult_int_big_int 2 x)
	[ bioi 10; bioi 20; bioi 30 ] in
      let rec test_each lst refs = match lst,refs with
	| (el :: rest), (el2 :: rest2) -> assert_equal
	  ~msg:"Test Misc.listmap.1 failed" 0 (Big_int.compare_big_int el el2);
	  test_each rest rest2
	| [], [] -> assert true
	| _ -> assert false
      in
      test_each lst [ bioi 20; bioi 40; bioi 60 ];
    );

    "visit_tree_infix" >:: ( fun test_ctxt ->
      let tree = Misc.Tree("first", Misc.Empty(),
			   Misc.Tree(" second", Misc.Empty(), Misc.Empty())) in
      assert_equal ~msg:"Test Misc.visit_tree_infix.1 failed" "first second"
			  (Misc.visit_tree_infix (fun x -> x) tree);
      assert_equal ~msg:"Test Misc.visit_tree_infix.2 failed" "5~7~"
			  (Misc.visit_tree_infix (fun x -> string_of_int(Misc.str_len x)^"~") tree);
    );

    "stack" >:: ( fun test_ctxt ->
      let p1 = { Misc.a=(float_of_string "1.2"); Misc.b=(float_of_string "0.3");
		 Misc.c=(float_of_string "0.5") } in
      let p2 = { Misc.a=(float_of_string "-0.2"); Misc.b=(float_of_string "0.4");
		 Misc.c=(float_of_string "0.7") } in
      let stack = Misc.Novalue() in
      let stack = Misc.push stack p1 in
      assert_equal ~msg:"Test Misc.stack.push1 failed"
	"{ a=1.200000 b=0.300000 c=0.500000 }" (Misc.print_stack stack);
      let stack = Misc.push stack p2 in
      assert_equal ~msg:"Test Misc.stack.push2 failed"
	"{ a=-0.200000 b=0.400000 c=0.700000 }{ a=1.200000 b=0.300000 c=0.500000 }"
	(Misc.print_stack stack);
      let stack = Misc.pop stack in
      assert_equal ~msg:"Test Misc.stack.pop1 failed"
	"{ a=1.200000 b=0.300000 c=0.500000 }" (Misc.print_stack stack);
      let stack = Misc.pop stack in
      assert_equal ~msg:"Test Misc.stack.pop2 failed" ""
	(Misc.print_stack stack);
      let stack = Misc.pop stack in
      assert_equal ~msg:"Test Misc.stack.pop3 failed" ""
	(Misc.print_stack stack);
    );

    "misc" >:: ( fun test_ctxt ->
      assert_equal ~msg:"Test Misc.misc.1 failed" (Sys.getenv "HOME")
	Misc.home;
      assert_equal ~msg:"Test Misc.misc.2 failed" 2 Misc.i;
    );

  ]

let _ = run_test_tt_main tests
