11#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
2- #include "utils.h"
32#include <Python.h>
43#include <math.h>
54#include <numpy/arrayobject.h>
65#include <numpy/npy_math.h>
76
7+ #include "utils.h"
8+
89
910static PyObject * largest_triangle_three_buckets (PyObject * self ,
1011 PyObject * args ) {
@@ -31,8 +32,11 @@ static PyObject *largest_triangle_three_buckets(PyObject *self,
3132 NPY_ARRAY_IN_ARRAY );
3233 y_array = (PyArrayObject * )PyArray_FROM_OTF (y_obj , NPY_DOUBLE ,
3334 NPY_ARRAY_IN_ARRAY );
34- if (!x_array || !y_array )
35+ if (!x_array || !y_array ) {
36+ PyErr_SetString (PyExc_ValueError ,
37+ "Failed to convert inputs to NumPy arrays." );
3538 goto fail ;
39+ }
3640
3741 if (PyArray_NDIM (x_array ) != 1 || PyArray_NDIM (y_array ) != 1 ) {
3842 PyErr_SetString (PyExc_ValueError , "x and y must be 1-dimensional." );
@@ -52,37 +56,33 @@ static PyObject *largest_triangle_three_buckets(PyObject *self,
5256 Py_DECREF (y_array );
5357 return result ;
5458 }
59+
5560 double * x = (double * )PyArray_DATA (x_array );
5661 double * y = (double * )PyArray_DATA (y_array );
57- // Create an empty output array with shape and dim for the output!
58- npy_intp dims [1 ];
59- dims [0 ] = threshold ;
60- PyArrayObject * result_x = (PyArrayObject * )PyArray_Empty (
61- 1 , dims , PyArray_DescrFromType (NPY_DOUBLE ), 0 );
62- PyArrayObject * result_y = (PyArrayObject * )PyArray_Empty (
63- 1 , dims , PyArray_DescrFromType (NPY_DOUBLE ), 0 );
64-
65- // Get a pointer to its data
66- double * result_x_data = (double * )PyArray_DATA (result_x );
67- double * result_y_data = (double * )PyArray_DATA (result_y );
68-
69- // The main loop here!
70- const double every = (double )(len_points - 2 ) / (threshold - 2 );
71-
72- npy_intp a = 0 ;
73- npy_intp next_a = 0 ;
7462
75- double max_area_point_x = 0.0 ;
76- double max_area_point_y = 0.0 ;
63+ double * result_x = (double * )malloc (threshold * sizeof (double ));
64+ double * result_y = (double * )malloc (threshold * sizeof (double ));
65+ if (!result_x || !result_y ) {
66+ PyErr_SetString (PyExc_MemoryError ,
67+ "Failed to allocate memory for result arrays." );
68+ free (result_x );
69+ free (result_y );
70+ goto fail ;
71+ }
7772
73+ const double every = (double )(len_points - 2 ) / (threshold - 2 );
7874 // Always add the first point!
79- result_x_data [0 ] = npy_isfinite (x [a ]) ? x [a ] : 0.0 ;
80- result_y_data [0 ] = npy_isfinite (y [a ]) ? y [a ] : 0.0 ;
75+ result_x [0 ] = npy_isfinite (x [0 ]) ? x [0 ] : 0.0 ;
76+ result_y [0 ] = npy_isfinite (y [0 ]) ? y [0 ] : 0.0 ;
8177
78+ npy_intp a = 0 , next_a = 0 ;
79+
80+ Py_BEGIN_ALLOW_THREADS ;
8281 for (npy_intp i = 0 ; i < threshold - 2 ; ++ i ) {
83- // Calculate point average for next bucket (containing c)
84- double avg_x = 0 ;
85- double avg_y = 0 ;
82+ double avg_x = 0 , avg_y = 0 ;
83+ // Careful, thread local variables
84+ double max_area_point_x = 0.0 ;
85+ double max_area_point_y = 0.0 ;
8686 npy_intp avg_start = (npy_intp )(floor ((i + 1 ) * every ) + 1 );
8787 npy_intp avg_end = (npy_intp )(floor ((i + 2 ) * every ) + 1 );
8888 if (avg_end >= len_points ) {
@@ -98,49 +98,49 @@ static PyObject *largest_triangle_three_buckets(PyObject *self,
9898 avg_y /= avg_length ;
9999
100100 // Get the range for this bucket
101- npy_intp k = (npy_intp )(floor ((i + 0 ) * every ) + 1 );
102- npy_intp range_to = (npy_intp )(floor ((i + 1 ) * every ) + 1 );
101+ npy_intp range_start = (npy_intp )(floor ((i + 0 ) * every ) + 1 );
102+ npy_intp range_end = (npy_intp )(floor ((i + 1 ) * every ) + 1 );
103103
104104 // Point a
105- double point_a_x = x [a ];
106- double point_a_y = y [a ];
107-
105+ double point_a [2 ] = {x [a ], y [a ]};
108106 double max_area = -1.0 ;
109- for (; k < range_to ; k ++ ) {
110- // Calculate triangle area over three buckets
111- double point_data [2 ] = {point_a_x , point_a_y };
112- double avg_data [2 ] = {avg_y , avg_y };
113- double next_data [2 ] = {x [k ], y [k ]};
114- double area =
115- calculate_triangle_area (point_data , avg_data , next_data );
107+
108+ for (npy_intp k = range_start ; k < range_end ; k ++ ) {
109+ double point_k [2 ] = {x [k ], y [k ]};
110+ double avg_point [2 ] = {avg_x , avg_y };
111+ double area = calculate_triangle_area (point_a , avg_point , point_k );
116112 if (area > max_area ) {
117113 max_area = area ;
118114 max_area_point_x = x [k ];
119115 max_area_point_y = y [k ];
120- next_a = k ; // Next a is this b
116+ next_a = k ;
121117 }
122118 }
123- // Pick this point from the bucket
124- result_x_data [(npy_intp )i + 1 ] = max_area_point_x ;
125- result_y_data [(npy_intp )i + 1 ] = max_area_point_y ;
126- // Current a becomes the next_a (chosen b)
119+
120+ result_x [i + 1 ] = max_area_point_x ;
121+ result_y [i + 1 ] = max_area_point_y ;
127122 a = next_a ;
128123 }
129-
130- // Always add last! Check for finite values!
131- double last_a_x = x [len_points - 1 ];
132- double last_a_y = y [len_points - 1 ];
133- result_x_data [threshold - 1 ] = npy_isfinite (last_a_x ) ? last_a_x : 0.0 ;
134- result_y_data [threshold - 1 ] = npy_isfinite (last_a_y ) ? last_a_y : 0.0 ;
135-
136- PyObject * result = PyTuple_Pack (2 , result_x , result_y );
137-
138- // And remove the references!
124+ Py_END_ALLOW_THREADS ;
125+
126+ result_x [threshold - 1 ] =
127+ npy_isfinite (x [len_points - 1 ]) ? x [len_points - 1 ] : 0.0 ;
128+ result_y [threshold - 1 ] =
129+ npy_isfinite (y [len_points - 1 ]) ? y [len_points - 1 ] : 0.0 ;
130+
131+ npy_intp dims [1 ] = {threshold };
132+ PyObject * npx =
133+ PyArray_SimpleNewFromData (1 , dims , NPY_DOUBLE , (void * )result_x );
134+ PyObject * npy =
135+ PyArray_SimpleNewFromData (1 , dims , NPY_DOUBLE , (void * )result_y );
136+ PyArray_ENABLEFLAGS ((PyArrayObject * )npx , NPY_ARRAY_OWNDATA );
137+ PyArray_ENABLEFLAGS ((PyArrayObject * )npy , NPY_ARRAY_OWNDATA );
138+
139+ PyObject * result = PyTuple_Pack (2 , npx , npy );
139140 Py_DECREF (x_array );
140141 Py_DECREF (y_array );
141- Py_XDECREF (result_x );
142- Py_XDECREF (result_y );
143-
142+ Py_DECREF (npx );
143+ Py_DECREF (npy );
144144 return result ;
145145
146146fail :
@@ -149,7 +149,6 @@ static PyObject *largest_triangle_three_buckets(PyObject *self,
149149 return NULL ;
150150}
151151
152-
153152static PyMethodDef LTTBMethods [] = {
154153 {"largest_triangle_three_buckets" , largest_triangle_three_buckets ,
155154 METH_VARARGS ,
@@ -163,9 +162,7 @@ static struct PyModuleDef LTTBModule = {
163162 "algorithm (LTTB) using C code." ,
164163 -1 , LTTBMethods };
165164
166-
167165PyMODINIT_FUNC PyInit__lttb (void ) {
168- Py_Initialize ();
169166 import_array ();
170167 return PyModule_Create (& LTTBModule );
171168}
0 commit comments