使用C中的递归进行堆栈溢出

时间:2016-06-01 19:47:29

标签: c recursion stack-overflow

我一直疯狂地寻找无济于事的答案;我创建了一个四叉树,它应该对结构声明的超过1000个对象的数组进行排序:

typedef struct node
{
    char     is_leaf;
    struct Particle *p;
    double    m;

    double center_x;
    double center_y;
    double width;

    struct node *sw;
    struct node *nw;
    struct node *se;
    struct node *ne;

} node;

进入具有函数的四叉树:

node* quadtree_insert(node *n, struct Particle *p, double center_x, double center_y, double width)
{
    if(n == NULL)
    {
        n = (node*)malloc(sizeof(node));
        n->is_leaf = 1;

        n->p = p;
        //n->m = 0.1;

        n->sw = NULL;
        n->se = NULL;
        n->nw = NULL;
        n->ne = NULL;
        if(width < 1e-300){
            n->width = 1e-300;
            }
        else
            n->width    = width;
        return n;
    }
    else{
        //n = (node*)malloc(sizeof(node));
        double x;
        double y;
        if(width < 1e-300){
            width = 1e-300;
            }
        if(n->is_leaf == 1) //! that is, if the node is not a branch
        {
                        x = (double)n->p->x_pos;
            y = (double)n->p->y_pos;

            if(x <= center_x && y <= center_y) //! first quadrant
            {
                n->sw = quadtree_insert(n->sw, n->p, center_x * 0.5, center_y * 0.5, width * 0.5);
            }
            else if(x <= center_x && y > center_y) //! second quadrant
            {
                n->nw = quadtree_insert(n->nw, n->p, center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
            }
            else if(x > center_x && y <= center_y) //! third quadrant
            {
                n->se = quadtree_insert(n->se, n->p, center_x + center_x * 0.5, center_y * 0.5, width * 0.5);
            }
            else //! fourth quadrant
            {
                n->ne = quadtree_insert(n->ne, n->p, center_x + center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
            }

            n->p = NULL; //! sets branch pointer to nothing...
            n->is_leaf = 0;
                    }
        //}

        x = (double)p->x_pos;
        y = (double)p->y_pos;

        if(x <= center_x && y <= center_y) //! first quadrant
        {
            n->sw = quadtree_insert(n->sw, p, center_x * 0.5, center_y * 0.5, width * 0.5);

        }
        else if(x <= center_x && y > center_y) //! second quadrant
        {
            n->nw = quadtree_insert(n->nw, p, center_x * 0.5, center_y + center_y * 0.5, width * 0.5);

        }
        else if(x > center_x && y <= center_y) //! third quadrant
        {
            n->se = quadtree_insert(n->se, p, center_x + center_x * 0.5, center_y * 0.5, width * 0.5);

        }
        else //! fourth quadrant
        {
            n->ne = quadtree_insert(n->ne, p, center_x + center_x * 0.5, center_y + center_y * 0.5, width * 0.5);            
        }
        return n;
    }
}

这一切都是通过以下方式完成的:

 node *root = NULL;
  root = quadtree_insert(root, &particles[0],0.500,0.5,1);

  for(i = 1; i < nParticles; i++)
  {
    quadtree_insert(root, &particles[i],0.5000,0.5,1);
  }

其中&#34;粒子&#34;在表单struct Particle *粒子上传递。并且定义粒子:

struct Particle {
    double mass;
    double x_pos;
    double y_pos;
    double x_vel;
    double y_vel;
};

typedef struct Particle * Particle_structure;

在每次迭代之后,在for循环之后,root是自由的,并且代码适用于小于~200的样本,而valgrind没有为这些提供错误。除此之外还有一个中间函数,它对粒子执行一些算术运算:

double quadtree_calculate_forcey(struct Particle *p, node *n, double theta_max, double delta_t, int numP, double epsilon, double G)
{
    if(n != NULL)
    {
            double d_x      = (n->center_x - p->x_pos);
                double d_y      = (n->center_y - p->y_pos);
        double r_2     = d_x * d_x + d_y * d_y;
        r_2 = sqrt(r_2) + epsilon;
        if(theta_max <= (n->width / r_2) && !n->is_leaf){
                double a = 0;
            if(n->sw != NULL)
                        a += quadtree_calculate_forcey(p, n->sw, theta_max, delta_t, numP, epsilon, G);
                    if(n->nw != NULL)
                        a += quadtree_calculate_forcey(p, n->nw, theta_max, delta_t, numP, epsilon, G);
                    if(n->se != NULL)
                        a += quadtree_calculate_forcey(p, n->se, theta_max, delta_t, numP, epsilon, G);
                    if(n->ne != NULL)
                        a += quadtree_calculate_forcey(p, n->ne, theta_max, delta_t, numP, epsilon, G);
                    return a;
        }
        else    
                {
                double fy;
            double mass;
           if(d_x == 0 && d_y == 0){ // could be comparing the same star
                 //printf("RÖÖÖVHATT\n");  
                  return 0;
            }
            else{
            //printf("MASS : %f\n", n->m);
                  mass = n->m;
                  //printf("MASS : %f\n", mass);
                  fy = G * (mass * p->mass/ pow(r_2,3)) * d_y;   
                            //printf("DY:%f   DX:%f   R_2:%f   MASSA:%f\n",d_y, d_x, r_2 - epsilon, mass);          
                     // printf("HIT SKA JAG: %f\n",d_y);
                  return fy;              
                 }
        }
    }
    return 0.0;
}

振铃器是它的滚动(清除根,并为新位置重做它),因此递归中的变量数量肯定不是问题(?)。我非常确定在指针声明的一些分配时我会和鱼一起游泳。任何想法/帮助都会让你站在奥丁身边!

编辑:我们如何找到质量中心等的一个例子。节点质量以相同的方式完成。

double set_centerx(node *n)
{
    if(n != NULL)
    {
        if(!(n->is_leaf))
        {
                    double a = set_centerx(n->ne);
                    double b = set_centerx(n->nw);
                    double c = set_centerx(n->se);
                    double d = set_centerx(n->sw);
                    double m1 = get_mass2(n->ne);
                    double m2 = get_mass2(n->nw);
                    double m3 = get_mass2(n->se);
                    double m4 = get_mass2(n->sw);
          n->center_x = (double)(m1*a + m2*b + m3*c + m4*d)/(m1+m2+m3+m4);
          return n->center_x;
        }
                n->center_x =n->p->x_pos;
        return n->p->x_pos;
    }

    return 0;
}

2 个答案:

答案 0 :(得分:3)

部分分析

我可以肯定地确定问题主要是由于else的主quadtree_insert()子句中的大量重复代码(接近重复代码)。我已将评论标记为Fragment 1AFragment 1B,并且片段1B现在还包含#ifdef DO_REPEAT#endif

    else
    {
        /* Fragment 1A */
        // n = (node*)malloc(sizeof(node));
        double x;
        double y;
        if (width < 1e-300)
        {
            width = 1e-300;
        }
        if (n->is_leaf == 1) // ! that is, if the node is not a branch
        {
            x = (double)n->p->x_pos;
            y = (double)n->p->y_pos;

            if (x <= center_x && y <= center_y) // ! first quadrant
            {
                n->sw = quadtree_insert(n->sw, n->p, center_x * 0.5, center_y * 0.5, width * 0.5);
            }
            else if (x <= center_x && y > center_y) // ! second quadrant
            {
                n->nw = quadtree_insert(n->nw, n->p, center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
            }
            else if (x > center_x && y <= center_y) // ! third quadrant
            {
                n->se = quadtree_insert(n->se, n->p, center_x + center_x * 0.5, center_y * 0.5, width * 0.5);
            }
            else // ! fourth quadrant
            {
                n->ne = quadtree_insert(n->ne, n->p, center_x + center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
            }

            n->p = NULL; // ! sets branch pointer to nothing...
            n->is_leaf = 0;
        }

#ifdef DO_REPEAT
        /* Fragment 1B */
        x = (double)p->x_pos;
        y = (double)p->y_pos;

        if (x <= center_x && y <= center_y) // ! first quadrant
        {
            n->sw = quadtree_insert(n->sw, p, center_x * 0.5, center_y * 0.5, width * 0.5);
        }
        else if (x <= center_x && y > center_y) // ! second quadrant
        {
            n->nw = quadtree_insert(n->nw, p, center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
        }
        else if (x > center_x && y <= center_y) // ! third quadrant
        {
            n->se = quadtree_insert(n->se, p, center_x + center_x * 0.5, center_y * 0.5, width * 0.5);
        }
        else // ! fourth quadrant
        {
            n->ne = quadtree_insert(n->ne, p, center_x + center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
        }
#endif /* DO_REPEAT */
        return n;
    }

我拿了你的代码并重新排序了一些片段,并使用了这个main()。请注意,我不应该这样做;你应该制作一个MCVE(How to create a Minimal, Complete, and Verifiable Example?)或SSCCE(Short, Self-Contained, Correct Example) - 两个名称和链接,用于相同的基本想法。

static struct Particle particles[] =
{
    {  19.99,  96.07,  62.79, -99.46,  19.70 },
    {  12.94,   1.43, -33.45,  31.80, -66.08 },
    {   6.49,  16.99, -20.83,  92.51,  35.98 },
    {  17.01, -28.85, -94.10,  42.82,  -1.30 },
    {  14.27,  85.07,  88.21,  11.22,  16.85 },
    {  15.73, -56.37,  46.85,  27.40, -15.15 },
    {   1.53, -49.44, -64.27, -29.45, -38.25 },
    {   8.03,  92.11, -47.50,  63.77, -29.99 },
    {   8.67, -99.81,  73.19,  18.75,  88.66 },
    {  16.36,  66.33,  14.23,  87.65,  40.01 },
};

enum { nParticles = sizeof(particles) / sizeof(particles[0]) };

int main(void)
{
    node *root = NULL;
    printf("Particle 0:\n");
    root = quadtree_insert(root, &particles[0], 0.500, 0.5, 1);

    for (int i = 1; i < nParticles; i++)
    {
        printf("Particle %d:\n", i);
        quadtree_insert(root, &particles[i], 0.5000, 0.5, 1);
    }
    return 0;
}

我使用随机数生成器作为值表:

random -n 10 -T '    { %6:2[1:20]f, %6:2[-100:100]f, %6:2[-100:100]f, %6:2[-100:100]f, %6:2[-100:100]f },'

它在粒子6上为我坠毁。概括地说,valgrind说:

…startup blurb omitted…
Particle 0:
Particle 1:
Particle 2:
Particle 3:
Particle 4:
Particle 5:
Particle 6:
==79528== 
==79528== Process terminating with default action of signal 11 (SIGSEGV)
==79528==  Access not within mapped region at address 0x104003FD0
==79528==    at 0x100008E39: malloc (vg_replace_malloc.c:302)
==79528==  If you believe this happened as a result of a stack
==79528==  overflow in your program's main thread (unlikely but
==79528==  possible), you can try to increase the size of the
==79528==  main thread stack using the --main-stacksize= flag.
==79528==  The main thread stack size used in this run was 8388608.
==79528== 
==79528== HEAP SUMMARY:
==79528==     in use at exit: 6,017,700 bytes in 75,080 blocks
==79528==   total heap usage: 75,162 allocs, 82 frees, 6,023,876 bytes allocated
==79528== 
==79528== LEAK SUMMARY:
==79528==    definitely lost: 4,120 bytes in 2 blocks
==79528==    indirectly lost: 2,288 bytes in 6 blocks
==79528==      possibly lost: 4,880 bytes in 45 blocks
==79528==    still reachable: 5,997,720 bytes in 74,904 blocks
==79528==         suppressed: 8,692 bytes in 123 blocks
==79528== Rerun with --leak-check=full to see details of leaked memory

即使在我运行它的Mac上,也表明存在问题;被压抑的东西是可以的,但其余的大部分都没有。

在没有-DDO_REPEAT(正常编译)的情况下编译时,示例程序将运行完成。当然,它会泄漏,因为没有代码可以释放内存。

Particle 0:
Particle 1:
Particle 2:
Particle 3:
Particle 4:
Particle 5:
Particle 6:
Particle 7:
Particle 8:
Particle 9:
==79683== 
==79683== HEAP SUMMARY:
==79683==     in use at exit: 26,580 bytes in 191 blocks
==79683==   total heap usage: 273 allocs, 82 frees, 32,756 bytes allocated
==79683== 
==79683== LEAK SUMMARY:
==79683==    definitely lost: 4,200 bytes in 3 blocks
==79683==    indirectly lost: 2,368 bytes in 7 blocks
==79683==      possibly lost: 4,880 bytes in 45 blocks
==79683==    still reachable: 6,440 bytes in 13 blocks
==79683==         suppressed: 8,692 bytes in 123 blocks
==79683== Rerun with --leak-check=full to see details of leaked memory

请注意,使用的内存比以前少得多。

如果由于缺少DO_REPEAT而消除的代码实际上是至关重要的,那么错误要么在于代码,要么在于片段1A中完成的设置工作。但是,使用递归调用将相同的粒子插入两次似乎可能是我遇到麻烦的原因。

我还注意到quadtree_calculate_forcey()函数未在代码中使用;它绝对不是MCVE的一部分。

需要片段1B

John Bollinger建议:

  

请注意&#34;重复&#34;代码在形式上是相同的,但不是详细的。也就是说,片段1B使用n->p,其中片段1A使用p。我认为这是有目的的:这个想法似乎是强制所有数据(粒子)出现在叶子节点上。

radnaskela肯定了:

  

与John说的完全一样,每个粒子都应该占据一个终端节点。我的怀疑说它与运动有关,并且行星对齐,因此必须进行荒谬的迭代次数才能打开两个自由方格。我有没有看到解决问题的方法,但有什么想法?

逻辑存在问题,但我并不完全确定该问题是什么。我要做的第一步是在quadtree_insert()中为代码添加一些工具,如下所示:

static node *quadtree_insert(node *n, struct Particle *p, double center_x, double center_y, double width)
{
    printf("Centre (X,Y) = (%6.2f,%6.2f)\n", center_x, center_y);
    if (n == NULL)
    {
        n = (node *)malloc(sizeof(node));
        n->is_leaf = 1;

        n->p = p;
        // n->m = 0.1;

        n->sw = NULL;
        n->se = NULL;
        n->nw = NULL;
        n->ne = NULL;
        if (width < 1e-300)
        {
            n->width = 1e-300;
        }
        else
            n->width    = width;
        return n;
    }
    else
    {
        // n = (node*)malloc(sizeof(node));
        double x;
        double y;
        if (width < 1e-300)
        {
            width = 1e-300;
        }
        if (n->is_leaf == 1) // ! that is, if the node is not a branch
        {
            x = (double)n->p->x_pos;
            y = (double)n->p->y_pos;

            if (x <= center_x && y <= center_y) // ! first quadrant
            {
                printf("Recurse SW 1: ");
                n->sw = quadtree_insert(n->sw, n->p, center_x * 0.5, center_y * 0.5, width * 0.5);
            }
            else if (x <= center_x && y > center_y) // ! second quadrant
            {
                printf("Recurse NW 1: ");
                n->nw = quadtree_insert(n->nw, n->p, center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
            }
            else if (x > center_x && y <= center_y) // ! third quadrant
            {
                printf("Recurse SE 1: ");
                n->se = quadtree_insert(n->se, n->p, center_x + center_x * 0.5, center_y * 0.5, width * 0.5);
            }
            else // ! fourth quadrant
            {
                printf("Recurse NE 1: ");
                n->ne = quadtree_insert(n->ne, n->p, center_x + center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
            }

            n->p = NULL; // ! sets branch pointer to nothing...
            n->is_leaf = 0;
        }

        x = (double)p->x_pos;
        y = (double)p->y_pos;

        if (x <= center_x && y <= center_y) // ! first quadrant
        {
            printf("Recurse SW 2: ");
            n->sw = quadtree_insert(n->sw, p, center_x * 0.5, center_y * 0.5, width * 0.5);
        }
        else if (x <= center_x && y > center_y) // ! second quadrant
        {
            printf("Recurse NW 2: ");
            n->nw = quadtree_insert(n->nw, p, center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
        }
        else if (x > center_x && y <= center_y) // ! third quadrant
        {
            printf("Recurse SE 2: ");
            n->se = quadtree_insert(n->se, p, center_x + center_x * 0.5, center_y * 0.5, width * 0.5);
        }
        else // ! fourth quadrant
        {
            printf("Recurse NE 2: ");
            n->ne = quadtree_insert(n->ne, p, center_x + center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
        }
        return n;
    }
}

使用10点数据集运行时,粒子5的输出为:

Particle 0: Centre (X,Y) = (  0.50,  0.50)
Particle 1: Centre (X,Y) = (  0.50,  0.50)
Recurse NE 1: Centre (X,Y) = (  0.75,  0.75)
Recurse SE 2: Centre (X,Y) = (  0.75,  0.25)
Particle 2: Centre (X,Y) = (  0.50,  0.50)
Recurse SE 2: Centre (X,Y) = (  0.75,  0.25)
Recurse SE 1: Centre (X,Y) = (  1.12,  0.12)
Recurse SE 2: Centre (X,Y) = (  1.12,  0.12)
Recurse SE 1: Centre (X,Y) = (  1.69,  0.06)
Recurse SE 2: Centre (X,Y) = (  1.69,  0.06)
Recurse SW 1: Centre (X,Y) = (  0.84,  0.03)
Recurse SE 2: Centre (X,Y) = (  2.53,  0.03)
Particle 3: Centre (X,Y) = (  0.50,  0.50)
Recurse SW 2: Centre (X,Y) = (  0.25,  0.25)
Particle 4: Centre (X,Y) = (  0.50,  0.50)
Recurse NE 2: Centre (X,Y) = (  0.75,  0.75)
Recurse NE 1: Centre (X,Y) = (  1.12,  1.12)
Recurse NE 2: Centre (X,Y) = (  1.12,  1.12)
Recurse NE 1: Centre (X,Y) = (  1.69,  1.69)
Recurse NE 2: Centre (X,Y) = (  1.69,  1.69)
Recurse NE 1: Centre (X,Y) = (  2.53,  2.53)
Recurse NE 2: Centre (X,Y) = (  2.53,  2.53)
Recurse NE 1: Centre (X,Y) = (  3.80,  3.80)
Recurse NE 2: Centre (X,Y) = (  3.80,  3.80)
Recurse NE 1: Centre (X,Y) = (  5.70,  5.70)
Recurse NE 2: Centre (X,Y) = (  5.70,  5.70)
Recurse NE 1: Centre (X,Y) = (  8.54,  8.54)
Recurse NE 2: Centre (X,Y) = (  8.54,  8.54)
Recurse NE 1: Centre (X,Y) = ( 12.81, 12.81)
Recurse NE 2: Centre (X,Y) = ( 12.81, 12.81)
Recurse NE 1: Centre (X,Y) = ( 19.22, 19.22)
Recurse NE 2: Centre (X,Y) = ( 19.22, 19.22)
Recurse NE 1: Centre (X,Y) = ( 28.83, 28.83)
Recurse NE 2: Centre (X,Y) = ( 28.83, 28.83)
Recurse NE 1: Centre (X,Y) = ( 43.25, 43.25)
Recurse NE 2: Centre (X,Y) = ( 43.25, 43.25)
Recurse NE 1: Centre (X,Y) = ( 64.87, 64.87)
Recurse NE 2: Centre (X,Y) = ( 64.87, 64.87)
Recurse SE 1: Centre (X,Y) = ( 97.31, 32.44)
Recurse NE 2: Centre (X,Y) = ( 97.31, 97.31)
Particle 5: Centre (X,Y) = (  0.50,  0.50)
Recurse NW 2: Centre (X,Y) = (  0.25,  0.75)

我对那里的一些处理以及粒子4的条目数感到有点惊讶。这可能表明了问题。

然后它处理粒子6:

Particle 6: Centre (X,Y) = (  0.50,  0.50)
Recurse SW 2: Centre (X,Y) = (  0.25,  0.25)
Recurse SW 1: Centre (X,Y) = (  0.12,  0.12)
Recurse SW 2: Centre (X,Y) = (  0.12,  0.12)
Recurse SW 1: Centre (X,Y) = (  0.06,  0.06)
Recurse SW 2: Centre (X,Y) = (  0.06,  0.06)
Recurse SW 1: Centre (X,Y) = (  0.03,  0.03)
Recurse SW 2: Centre (X,Y) = (  0.03,  0.03)
Recurse SW 1: Centre (X,Y) = (  0.02,  0.02)
Recurse SW 2: Centre (X,Y) = (  0.02,  0.02)
Recurse SW 1: Centre (X,Y) = (  0.01,  0.01)
Recurse SW 2: Centre (X,Y) = (  0.01,  0.01)
Recurse SW 1: Centre (X,Y) = (  0.00,  0.00)
Recurse SW 2: Centre (X,Y) = (  0.00,  0.00)
Recurse SW 1: Centre (X,Y) = (  0.00,  0.00)
Recurse SW 2: Centre (X,Y) = (  0.00,  0.00)
Recurse SW 1: Centre (X,Y) = (  0.00,  0.00)
Recurse SW 2: Centre (X,Y) = (  0.00,  0.00)

从那以后它变得非常乏味。你需要问的问题是&#34;那一点应该发生什么&#34;?拥有一个转储四叉树结构的函数可能是一个好主意。

这基本上表明您尚未分析充分添加点的条件。我不清楚为什么在插入在SE或NE象限时将中心坐标乘以1.5,在SW或NW象限中乘以0.5(也不是为什么使用加倍乘而不是简单乘法)。

width小于1e-300的测试令人担忧。鉴于您使用值1.0调用函数,每次递归时将宽度减半时,需要一段时间才能得到那么小。

更好的跟踪可能会报告xy的值以及中心坐标。

答案 1 :(得分:2)

我发现您的代码存在一些问题。

首先,你有一个相当重复的大量非平凡代码。这两个块似乎只有它们引用的Particle不同;这需要考虑到辅助函数中。

第二,考虑这个片段:

else if (x <= center_x && y > center_y) // ! second quadrant
{
    n->nw = quadtree_insert(n->nw, n->p, center_x * 0.5, center_y + center_y * 0.5, width * 0.5);
}

我将输入center_xcenter_y作为整个区域的方形补丁的中心,width作为该补丁的边长。在这种情况下,补丁的西北象限的中心是(center_x - width * 0.25center_y + width * 0.25)。只有在某些特殊情况下才会与递归调用中的计算一致。实际上,您可以从中心的坐标确定贴片的宽度,但不能直接与您尝试做的一样。

类似适用于所有其他七个递归调用。

第三,考虑如果最终得到两个具有非常相似或更差的相同坐标的粒子会发生什么。它们可能是模拟中唯一的两个。

第一个粒子最初位于代表整个模拟区域的节点中。插入第二个时,四叉树遍历到同一节点。因此,原始粒子被移动到原始节点区域的新创建的象限,并且第二节点的插入过程重新开始。但插入然后再次到达原始粒子占据的同一节点,并重复该过程。也许它会再次重复。再一次。

因此,您的递归没有明确的约束。如果碰巧最终得到两个具有相同坐标的粒子 - 例如,它们可能固定在模拟区域的角落 - 那么你肯定会无休止地递归,最终会产生堆栈溢出。然而,即使没有两个粒子具有相同的坐标,也可以想到两个粒子将足够接近,以使递归足够深,以便堆栈溢出。

我怀疑前一期已经加剧了这个问题,但并不依赖于这个问题。

解决这个问题的一种方法是合并足够接近的粒子。这将允许您根据补丁宽度在递归深度上设置一个固定界限。或者,如果递归过深,您可以中止。或者,你可以让它在这种情况下崩溃,就像它已经发生的那样。

对于它的价值,我认为你quadtree_insert()应该看起来如何:

// adjust as needed:
#define MIN_PARTICLE_SEPARATION 1e-300

void quadtree_insert_helper(node *n, Particle *p, double center_x,
        double center_y, double width) {
    width *= 0.5;

    double new_center_x = center_x
            + width * ((p->x_pos <= center_x) ? -0.5 : 0.5);
    double new_center_y = center_y
            + width * ((p->y_pos <= center_y) ? -0.5 : 0.5);
    node **quad;

    if (new_center_x <= center_x && new_center_y <= center_y) {
        //! first quadrant
        quad = &n->sw;
    } else if (new_center_x <= center_x && new_center_y > center_y) {
        //! second quadrant
        quad = &n->nw;
    } else if (new_center_x > center_x && new_center_y <= center_y) {
        //! third quadrant
        quad = &n->se;
    } else {
        //! fourth quadrant
        quad = &n->ne;
    }
    *quad = quadtree_insert(*quad, p, new_center_x, new_center_y, width);
}

node* quadtree_insert(node *n, struct Particle *p, double center_x,
        double center_y, double width) {
    if (n == NULL) {
        n = malloc(sizeof(*n));
        n->is_leaf = 1;
        n->p = p;
        //n->m = 0.1;
        n->sw = NULL;
        n->se = NULL;
        n->nw = NULL;
        n->ne = NULL;
        n->center_x = center_x;
        n->center_y = center_y;
        n->width = width;
    } else if (n->is_leaf && (with <= MIN_PARTICLE_SEPARATION)) {
        // ... merge particles ...
    } else {
        if (n->is_leaf) { //! that is, if the node is not a branch
            quadtree_insert_helper(n, n->p, center_x, center_y, width);
            //! the node is now a branch
            n->p = NULL;
            n->is_leaf = 0;
        }

        quadtree_insert_helper(n, p, center_x, center_y, width);
    }

    return n;
}