/*
 * Last Change: Sun Jan 14 07:00 PM 2007 J
 * 
 * vim:syntax=c
 */


/* 
 * (npy_double version) Clip in-place an array with scalars min and max
 * (native endianness), contiguous array  or not.
 *
 */
static int numeric_native_scalar_dbl_clip(
        PyArrayObject* in, 
        npy_double min, 
        npy_double max) 
{
    int                     st;
    npy_intp                ni, i;
    npy_double curel, *curptr;

    PyArrayIterObject   *iter;

    if (PyArray_ISCONTIGUOUS(in) || PyArray_ISFORTRAN(in)) {
        /*
         * Special case where we can easily iterate over all
         * items of the input in a C-way (eg using standard array indexing)
         */
        ni      = PyArray_Size((PyObject*)in);
        curptr  = (npy_double *)in->data;
        for (i = 0; i < ni; ++i) {  
            if (curptr[i] < min) {
                curptr[i]   = min;
            } else if (curptr[i] > max) {
                curptr[i]   = max;
            }
        }
    } else {
        /*
         * Generic case.
         */
        iter    = (PyArrayIterObject*)PyArray_IterNew((PyObject*)in);
        if (iter == NULL) {
            st = -1;
            goto fail;
        }
        
        while(iter->index < iter->size) {
            curel   = ((npy_double*) iter->dataptr)[0];
            if (curel < min) {
                curel   = min;
            } else if (curel > max) {
                curel   = max;
            }
            ((npy_double*) iter->dataptr)[0] = curel;
            PyArray_ITER_NEXT(iter);
        }

        Py_DECREF(iter);
    }

    return 0;

fail:
    return st;
}

/* This function does the dispatching based on input type */
/*
 * Expect 
 *  - input, min and max to be same typenum
 *  - min and max are scalar arrays
 *  - input is writeable
 *  - input, min and max are native endianness
 *  - input, min and max are aligned
 *
 * Those requirements are checked in debug mode only
 */
static int numeric_native_scalar_generic_clip(PyArrayObject* input, 
    PyArrayObject* min, PyArrayObject *max)
{
    int         typenum, st;

    /* 
     * Check we have what we expect for type
     */
    typenum = input->descr->type_num;
    if(typenum != min->descr->type_num 
        || typenum != max->descr->type_num) {
        PyErr_SetString(PyExc_TypeError,
                        "BUG: expected all objs to be same type");
        goto fail;
    }

    /*
     * Checking requirements (IN DEBUG MODE ONLY)
     */
    assert (PyArray_ISALIGNED(input));
    assert (PyArray_ISWRITEABLE(input));
    assert (PyArray_ISNOTSWAPPED(input));

    assert (PyArray_ISALIGNED(min));
    assert (PyArray_ISNOTSWAPPED(min));
    assert (PyArray_CheckScalar(min));

    assert (PyArray_ISALIGNED(max));
    assert (PyArray_ISNOTSWAPPED(max));
    assert (PyArray_CheckScalar(max));

    /*
     * calling type specific implementation
     */
    switch (typenum) {
        /* 
         * Take care to convert data pointer to correct pointer type before
         * indexing ! 
         */
        case NPY_DOUBLE:
            st  = numeric_native_scalar_dbl_clip(
                    input, 
                    ((npy_double*)min->data)[0], 
                    ((npy_double*)max->data)[0]); 
            break;
        
        default:
            PyErr_SetString(PyExc_TypeError,
                            "type is not supported yet,"\
                            " sorry");
            goto fail;
            break;
    }
    return st;

fail:
    return -1;
}

