Calculate min, max, mean and median out of array Version 2 This question is a follow up of:
Calculate min, max, mean and median out of array

I took all the suggestions an reworked the code. Like the description of the excercise suggests i only use one function for all the computations. Also with the sorting the max and median values can now be calculated easier.

I wonder if its good to return the Macro types INT_MIN INT_MAX and NAN for indicating no valid result?

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;assert.h&gt;
#include &lt;limits.h&gt;
#include &lt;math.h&gt;

struct Summary_data {
    long long smallest;
    long long largest;
    double median;
    double mean;
};

static inline int cmp(void const *lhs, void const *rhs) {
    const int *left = (const int *)lhs; 
    const int *right = (const int *)rhs;
    return (*left &gt; *right) - (*left &lt; *right);
}

int* intdup(const int * source, const size_t len)
{
    assert(source);

    int * p = malloc(len * sizeof(*source));

    if (p == NULL)
        exit(1);

    memcpy(p, source, len * sizeof(*source));
    return p;
}

struct Summary_data calculate_values(const int* array, const size_t len)
{
    assert(array);

    struct Summary_data summary_data = { 0 };
    int* calc_array = intdup(array, len);
    size_t i;

    if (len &lt;= 0) {             // dont bother to calc if invalid len
        free(calc_array);
        calc_array = NULL;
        summary_data.smallest = INT_MAX;
        summary_data.largest = INT_MIN;
        summary_data.mean = NAN;
        summary_data.median = NAN;
        return summary_data;
    }

    qsort(calc_array, len, sizeof *array, cmp);

    summary_data.smallest = calc_array[0];
    summary_data.largest = calc_array[len - 1];

    for (i = 0; i &lt; len; ++i) {
        summary_data.mean += calc_array[i];
    }
    summary_data.mean /= len;

    if (len % 2 == 0) { // is even == return the arithmetic middle of the two middle values
        summary_data.median = (calc_array[(len - 1) / 2] + calc_array[len / 2]) / 2.0;
    }
    else {  // is odd  == retunr the middle
        summary_data.median = calc_array[len / 2];
    }

    free(calc_array);
    calc_array = NULL;

    return summary_data;
}


void print_result(const struct Summary_data* summary_data)
{
    assert(summary_data);

    printf("smallest: %i\n", summary_data-&gt;smallest);
    printf("largest: %i\n", summary_data-&gt;largest);
    printf("median: %g\n", summary_data-&gt;median);
    printf("mean: %g\n\n", summary_data-&gt;mean);
}

int main()
{
    int test_array[] = { 1,7,3,4,5,6,7,8,9 };       // 9 elements  
    //  int test_array[] = { 1,7,3,4,5,6,7,8 };         // 8 elements  

    int len = sizeof(test_array) / sizeof(test_array[0]);

    //len = 0;                                          // test when len is invalid
    struct Summary_data summary_data = calculate_values(test_array, len);

    print_result(&amp;summary_data);
    getchar();
    return 0;
}
To add to Peter's answer:

I can only think of one case where you couldn't compute a valid result (min, median, max, mean) for an array and that's when the array is empty (or infinite I suppose, but if that's a problem for you please let me know where you buy your RAM). Since this is trivial to check I would generally leave it up to the caller to do so (and document that expectation in the function's interface). It's just simpler and cleaner.

I have some minor style suggestions:


I advise against using assertions for error checking (except for during debugging). Assertion failures cause your program to immediately terminate. In general you would probably want to propagate errors back up the call stack so that the caller can handle them (hopefully) gracefully.
You check if len is less than zero, but this can't occur as len has an unsigned type.
It makes sense to check len before allocating calc_array. In general, if you are checking parameters you generally want to do it at the beginning of a function. It's more readable and may save you work.
I generally prefer to use the types defined in stdint.h when reasonable (uint32_t, uint64_t etc.).