() = evalfile ("inc.sl");

testing_feature ("sorting");

private define test_simple_sort (n, dir)
{
   variable cmp_op = (dir > 0) ? &_op_lt : &_op_gt;
   variable ishift = [[1:n-1], 0];
   variable x, dx, bad;

   foreach ([Int_Type, Float_Type, Double_Type,
	     UShort_Type, Short_Type, Char_Type, Long_Type,
	     UInt_Type, UChar_Type, ULong_Type,
#ifexists LLong_Type
	     LLong_Type, ULLong_Type
#endif
	    ])
     {
	variable type = ();
	loop (3)
	  {
	     x = typecast (1000*(urand (n)), type);
	     x = x[array_sort (x; dir=dir)];
	     dx = x[ishift] - 1.0*x;
	     dx[-1] = 0;
	     if (any((@cmp_op)(dx, 0)))
	       failed ("simple sort: %S", type);

	     % Already sorted
	     x = x[array_sort (x; dir=dir)];
	     dx = x[ishift] - 1.0*x;
	     dx[-1] = 0;
	     if (any((@cmp_op)(dx, 0)))
	       failed ("simple sort already sorted: %S", type);

	     % Reversed
	     array_reverse (x);
	     x = x[array_sort (x; dir=dir)];
	     dx = x[ishift] - 1.0*x;
	     dx[-1] = 0;
	     if (any((@cmp_op)(dx, 0)))
	       failed ("simple sort reversed: %S", type);
	  }
     }
}

define run_test_simple_sorts (method)
{
   set_default_sort_method (method);
   if (method != get_default_sort_method ())
     failed ("get/set_default_sort_method");

   test_simple_sort (1, 1);
   test_simple_sort (2, 1);
   test_simple_sort (3, 1);
   test_simple_sort (4, 1);
   test_simple_sort (5, 1);
   test_simple_sort (14, 1);
   test_simple_sort (15, 1);
   test_simple_sort (16, 1);
   test_simple_sort (17, 1);
   test_simple_sort (1023, 1);
   test_simple_sort (1024, 1);
   test_simple_sort (1025, 1);
   test_simple_sort (2, -1);
   test_simple_sort (3, -1);
   test_simple_sort (15, -1);
   test_simple_sort (1023, -1);
   test_simple_sort (1024, -1);
   test_simple_sort (1025, -1);
}
run_test_simple_sorts ("qsort");
run_test_simple_sorts ("msort");

private define opaque_sort_func (s, i, j)
{
   return sign (s.a[i] - s.a[j]);
}

private define cmp_fun (a, b)
{
   return sign (a-b);
}

private define test_sort (x, n, ans)
{
   variable i, a;

   a = x[array_sort (x;; __qualifiers)];
   ifnot (_eqs (a, ans))
     failed ("array_sort(int x)");

   a = 1.0f*x;
   a = a[array_sort (a;; __qualifiers)];
   ifnot (_eqs (a, ans))
     failed ("array_sort(float x)");

   a = 1.0*x;
   a = a[array_sort (a;; __qualifiers)];
   ifnot (_eqs (a, ans))
     failed ("array_sort(double x)");

   a = x[array_sort (x, &cmp_fun;; __qualifiers)];
   ifnot (_eqs (a, ans))
     failed ("array_sort(int x, &cmp_fun)");

   a = 1.0f*x;
   a = a[array_sort (a, &cmp_fun;; __qualifiers)];
   ifnot (_eqs (a, ans))
     failed ("array_sort(float x, &cmp_fun)");

   a = 1.0*x;
   a = a[array_sort (a, &cmp_fun;; __qualifiers)];
   ifnot (_eqs (a, ans))
     failed ("array_sort(double x, &cmp_fun)");

   variable s = struct {a};
   s.a = 1.0*x;
   i = array_sort(s, &opaque_sort_func, n;; __qualifiers);
   a = s.a[i];
   ifnot (_eqs (a, ans))
     failed ("array_sort(struct, &opaque_sort_func, n)");
}

private define run_test_sort (method)
{
   set_default_sort_method (method);
   if (method != get_default_sort_method ())
     failed ("get/set_default_sort_method");

   variable A = [2, 3, 7, 9, 4, 1, 0, 8, 6, 5];
   test_sort (A, length(A), [0:length(A)-1]);
   test_sort ([2], 1, [2]);
   test_sort (A[[0:-1]], 0, A[[0:-1]]);

   test_sort (A, length(A), [length(A)-1:0:-1] ;dir=-1);
   test_sort ([2], 1, [2]; dir=-1);
   test_sort (A[[0:-1]], 0, A[[0:-1]]; dir=-1);
}
run_test_sort ("qsort");
run_test_sort ("msort");

private define test_stability (method)
{
   variable a = [1, 2, 3, 3, 4, 5, 5, 6];
   variable i = [7, 5, 6, 4, 2, 3, 1, 0];

   variable j = array_sort (a; dir=-1, method=method);
   ifnot (_eqs (j, i))
     failed ("[%s]: descend sort was not stable", method);

   a = [6, 6, 5, 4, 4, 3, 2, 1, 1];
   i = [7, 8, 6, 5, 3, 4, 2, 0, 1];

   j = array_sort (a; dir=1, method=method);
   ifnot (_eqs (j, i))
     failed ("[%s]: ascend sort was not stable", method);

}
test_stability ("qsort");
test_stability ("msort");

print ("Ok\n");

exit (0);

