summaryrefslogtreecommitdiff
path: root/lib/convex.c
blob: 7255ad483f954d2bb3099ffdea43ca86054feb71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

#include "convex.h"

double slope(point_t *P, point_t *Q) {
  return (Q->y - P->y) / ((double)(Q->x) - (double)(P->x));
}

double *get_convex_minorant(count_t n, double *Gammas) {
  if (n < 2) {
    return Gammas;
  }

  list_t *L = (list_t *)calloc(1, sizeof(list_t));
  L->p = (point_t *)calloc(1, sizeof(point_t));
  L->p->x = 0;
  L->p->y = Gammas[0];

  list_t *pos = L;

  for (count_t i = 1; i < n; i++) {
    pos->next = (list_t *)calloc(1, sizeof(list_t));
    pos->next->p = (point_t *)calloc(1, sizeof(point_t));
    pos->next->p->x = i;
    pos->next->p->y = Gammas[i];
    pos->next->prev = pos;
    pos = pos->next;
  }

  pos->next = (list_t *)calloc(1, sizeof(list_t));
  pos->next->p = (point_t *)calloc(1, sizeof(point_t));
  pos->next->p->x = n;
  pos->next->p->y = 0;
  pos->next->prev = pos;

  list_t *X = L;
  list_t *Y = L->next;
  list_t *Z = Y->next;

  while (true) {
    if (slope(X->p, Y->p) <= slope(Y->p, Z->p)) {
      X = Y;
      Y = Z;
      if (Z->next == NULL) {
        break;
      } else {
        Z = Z->next;
      }
    } else {
      Y->prev->next = Y->next;
      Y->next->prev = Y->prev;
      free(Y->p);
      free(Y);
      if (X->prev != NULL) {
        Y = X;
        X = X->prev;
      } else {
        if (Z->next != NULL) {
          Y = Z;
          Z = Z->next;
        } else {
          break;
        }
      }
    }
  }

  pos = L;

  double *g = (double *)calloc(n + 1, sizeof(double));
  double rho = 0;

  for (count_t i = 0; i < n + 1; i++) {
    if (i > pos->next->p->x) {
      pos = pos->next;
    }

    g[i] = pos->p->y + ((double)i - (double)(pos->p->x)) *  (pos->next->p->y - pos->p->y) / ((double)(pos->next->p->x) - (double)(pos->p->x));

    if (i <n) {
      if (Gammas[i] - g[i] > rho) {
        rho = Gammas[i] - g[i];
      }
    } else {
      if (0 - g[i] > rho) {
        rho = 0 - g[i];
      }
    }
  }

  for (count_t i = 0; i < n + 1; i++) {
    g[i] += rho / 2;
  }

  while (L != NULL) {
    free(L->p);
    L = L->next;
    free(L);
  }

  return g;
}