#if !defined(ON_COMPILING_OPENNURBS_HSORT_FUNCTIONS)
/*
See opennurbs_sort.cpp for examples of using openurbs_hsort_template.c
to define type specific heap sort functions.
*/
#error Do not compile openurbs_hsort_template.c directly.
#endif

// ON_SORT_TEMPLATE_TYPE -> double, int, ....
#if !defined(ON_SORT_TEMPLATE_TYPE)
#error Define ON_SORT_TEMPLATE_TYPE macro before including opennurbs_qsort_template.c
#endif

#if !defined(ON_HSORT_FNAME)
#error Define ON_HSORT_FNAME macro before including opennurbs_qsort_template.c
#endif

#if defined(ON_SORT_TEMPLATE_COMPARE)
// use a compare function like strcmp for char* strings
#define ON_HSORT_GT(A,B) ON_SORT_TEMPLATE_COMPARE(A,B) > 0
#define ON_HSORT_GT_TMP(A) ON_SORT_TEMPLATE_COMPARE(A,&tmp) > 0
#else
// use type compares
#define ON_HSORT_GT(A,B) *A > *B
#define ON_HSORT_GT_TMP(A) *A > tmp
#endif

#if defined(ON_SORT_TEMPLATE_USE_MEMCPY)
#define ON_HSORT_TO_TMP(A) memcpy(&tmp,A,sizeof(tmp))
#define ON_HSORT_FROM_TMP(A) memcpy(A,&tmp,sizeof(tmp))
#define ON_HSORT_COPY(dst,src) memcpy(dst,src,sizeof(tmp))
#else
#define ON_HSORT_TO_TMP(A) tmp = *A
#define ON_HSORT_FROM_TMP(A) *A = tmp
#define ON_HSORT_COPY(dst,src) *dst = *src
#endif

#if defined(ON_SORT_TEMPLATE_STATIC_FUNCTION)
static
#endif
void
ON_HSORT_FNAME( ON_SORT_TEMPLATE_TYPE* base, size_t nel )
{
  size_t i_end,k,i,j;
  ON_SORT_TEMPLATE_TYPE* e_end;
  ON_SORT_TEMPLATE_TYPE* e_i;
  ON_SORT_TEMPLATE_TYPE* e_j;
  ON_SORT_TEMPLATE_TYPE tmp;

  if (0 == base || nel < 2)
    return;

  k = nel >> 1;
  i_end = nel-1;
  e_end = base + i_end;
  for (;;) 
  {
    if (k)
    {
      --k;
      ON_HSORT_TO_TMP((base+k));  /* e_tmp = e[k]; */
    } 
    else
    {      
      ON_HSORT_TO_TMP(e_end);     /* e_tmp = e[i_end]; */
      ON_HSORT_COPY(e_end,base);  /* e[i_end] = e[0];  */
      if (!(--i_end))
      {
        ON_HSORT_FROM_TMP(base);  /* e[0] = e_tmp;     */
        break;
      }
      e_end--;
    }

    i = k;
    j = (k<<1) + 1;
    e_i = base + i;
    while (j <= i_end) 
    {
      e_j = base + j;
      if (j < i_end && ON_HSORT_GT((e_j+1),e_j) /*e[j] < e[j + 1] */)
      {
        j++;
        e_j++;
      }
      if (ON_HSORT_GT_TMP(e_j) /* tmp < e[j] */) 
      {
        ON_HSORT_COPY(e_i,e_j);  /* e[i] = e[j]; */
        i = j;
        e_i = e_j;
        j = (j<<1) + 1;
      }
      else
        j = i_end + 1;
    }

    ON_HSORT_FROM_TMP(e_i); /* e[i] = e_tmp; */
  }
}

#undef ON_HSORT_GT
#undef ON_HSORT_GT_TMP
#undef ON_HSORT_TO_TMP
#undef ON_HSORT_FROM_TMP
#undef ON_HSORT_COPY
#undef ON_HSORT_FROM_TMP
