+
+/* Construct a piecewise quasipolynomial that is constant on the given
+ * domain. In particular, it is
+ * 0 if cst == 0
+ * 1 if cst == 1
+ * infinity if cst == -1
+ */
+static __isl_give isl_pw_qpolynomial *constant_on_domain(
+ __isl_take isl_basic_set *bset, int cst)
+{
+ isl_space *dim;
+ isl_qpolynomial *qp;
+
+ if (!bset)
+ return NULL;
+
+ bset = isl_basic_set_params(bset);
+ dim = isl_basic_set_get_space(bset);
+ if (cst < 0)
+ qp = isl_qpolynomial_infty_on_domain(dim);
+ else if (cst == 0)
+ qp = isl_qpolynomial_zero_on_domain(dim);
+ else
+ qp = isl_qpolynomial_one_on_domain(dim);
+ return isl_pw_qpolynomial_alloc(isl_set_from_basic_set(bset), qp);
+}
+
+/* Factor bset, call fn on each of the factors and return the product.
+ *
+ * If no factors can be found, simply call fn on the input.
+ * Otherwise, construct the factors based on the factorizer,
+ * call fn on each factor and compute the product.
+ */
+static __isl_give isl_pw_qpolynomial *compressed_multiplicative_call(
+ __isl_take isl_basic_set *bset,
+ __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset))
+{
+ int i, n;
+ isl_space *dim;
+ isl_set *set;
+ isl_factorizer *f;
+ isl_qpolynomial *qp;
+ isl_pw_qpolynomial *pwqp;
+ unsigned nparam;
+ unsigned nvar;
+
+ f = isl_basic_set_factorizer(bset);
+ if (!f)
+ goto error;
+ if (f->n_group == 0) {
+ isl_factorizer_free(f);
+ return fn(bset);
+ }
+
+ nparam = isl_basic_set_dim(bset, isl_dim_param);
+ nvar = isl_basic_set_dim(bset, isl_dim_set);
+
+ dim = isl_basic_set_get_space(bset);
+ dim = isl_space_domain(dim);
+ set = isl_set_universe(isl_space_copy(dim));
+ qp = isl_qpolynomial_one_on_domain(dim);
+ pwqp = isl_pw_qpolynomial_alloc(set, qp);
+
+ bset = isl_morph_basic_set(isl_morph_copy(f->morph), bset);
+
+ for (i = 0, n = 0; i < f->n_group; ++i) {
+ isl_basic_set *bset_i;
+ isl_pw_qpolynomial *pwqp_i;
+
+ bset_i = isl_basic_set_copy(bset);
+ bset_i = isl_basic_set_drop_constraints_involving(bset_i,
+ nparam + n + f->len[i], nvar - n - f->len[i]);
+ bset_i = isl_basic_set_drop_constraints_involving(bset_i,
+ nparam, n);
+ bset_i = isl_basic_set_drop(bset_i, isl_dim_set,
+ n + f->len[i], nvar - n - f->len[i]);
+ bset_i = isl_basic_set_drop(bset_i, isl_dim_set, 0, n);
+
+ pwqp_i = fn(bset_i);
+ pwqp = isl_pw_qpolynomial_mul(pwqp, pwqp_i);
+
+ n += f->len[i];
+ }
+
+ isl_basic_set_free(bset);
+ isl_factorizer_free(f);
+
+ return pwqp;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Factor bset, call fn on each of the factors and return the product.
+ * The function is assumed to evaluate to zero on empty domains,
+ * to one on zero-dimensional domains and to infinity on unbounded domains
+ * and will not be called explicitly on zero-dimensional or unbounded domains.
+ *
+ * We first check for some special cases and remove all equalities.
+ * Then we hand over control to compressed_multiplicative_call.
+ */
+__isl_give isl_pw_qpolynomial *isl_basic_set_multiplicative_call(
+ __isl_take isl_basic_set *bset,
+ __isl_give isl_pw_qpolynomial *(*fn)(__isl_take isl_basic_set *bset))
+{
+ int bounded;
+ isl_morph *morph;
+ isl_pw_qpolynomial *pwqp;
+
+ if (!bset)
+ return NULL;
+
+ if (isl_basic_set_plain_is_empty(bset))
+ return constant_on_domain(bset, 0);
+
+ if (isl_basic_set_dim(bset, isl_dim_set) == 0)
+ return constant_on_domain(bset, 1);
+
+ bounded = isl_basic_set_is_bounded(bset);
+ if (bounded < 0)
+ goto error;
+ if (!bounded)
+ return constant_on_domain(bset, -1);
+
+ if (bset->n_eq == 0)
+ return compressed_multiplicative_call(bset, fn);
+
+ morph = isl_basic_set_full_compression(bset);
+ bset = isl_morph_basic_set(isl_morph_copy(morph), bset);
+
+ pwqp = compressed_multiplicative_call(bset, fn);
+
+ morph = isl_morph_dom_params(morph);
+ morph = isl_morph_ran_params(morph);
+ morph = isl_morph_inverse(morph);
+
+ pwqp = isl_pw_qpolynomial_morph_domain(pwqp, morph);
+
+ return pwqp;
+error:
+ isl_basic_set_free(bset);
+ return NULL;
+}
+
+/* Drop all floors in "qp", turning each integer division [a/m] into
+ * a rational division a/m. If "down" is set, then the integer division
+ * is replaces by (a-(m-1))/m instead.
+ */
+static __isl_give isl_qpolynomial *qp_drop_floors(
+ __isl_take isl_qpolynomial *qp, int down)
+{
+ int i;
+ struct isl_upoly *s;
+
+ if (!qp)
+ return NULL;
+ if (qp->div->n_row == 0)
+ return qp;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+
+ for (i = qp->div->n_row - 1; i >= 0; --i) {
+ if (down) {
+ isl_int_sub(qp->div->row[i][1],
+ qp->div->row[i][1], qp->div->row[i][0]);
+ isl_int_add_ui(qp->div->row[i][1],
+ qp->div->row[i][1], 1);
+ }
+ s = isl_upoly_from_affine(qp->dim->ctx, qp->div->row[i] + 1,
+ qp->div->row[i][0], qp->div->n_col - 1);
+ qp = substitute_div(qp, i, s);
+ if (!qp)
+ return NULL;
+ }
+
+ return qp;
+}
+
+/* Drop all floors in "pwqp", turning each integer division [a/m] into
+ * a rational division a/m.
+ */
+static __isl_give isl_pw_qpolynomial *pwqp_drop_floors(
+ __isl_take isl_pw_qpolynomial *pwqp)
+{
+ int i;
+
+ if (!pwqp)
+ return NULL;
+
+ if (isl_pw_qpolynomial_is_zero(pwqp))
+ return pwqp;
+
+ pwqp = isl_pw_qpolynomial_cow(pwqp);
+ if (!pwqp)
+ return NULL;
+
+ for (i = 0; i < pwqp->n; ++i) {
+ pwqp->p[i].qp = qp_drop_floors(pwqp->p[i].qp, 0);
+ if (!pwqp->p[i].qp)
+ goto error;
+ }
+
+ return pwqp;
+error:
+ isl_pw_qpolynomial_free(pwqp);
+ return NULL;
+}
+
+/* Adjust all the integer divisions in "qp" such that they are at least
+ * one over the given orthant (identified by "signs"). This ensures
+ * that they will still be non-negative even after subtracting (m-1)/m.
+ *
+ * In particular, f is replaced by f' + v, changing f = [a/m]
+ * to f' = [(a - m v)/m].
+ * If the constant term k in a is smaller than m,
+ * the constant term of v is set to floor(k/m) - 1.
+ * For any other term, if the coefficient c and the variable x have
+ * the same sign, then no changes are needed.
+ * Otherwise, if the variable is positive (and c is negative),
+ * then the coefficient of x in v is set to floor(c/m).
+ * If the variable is negative (and c is positive),
+ * then the coefficient of x in v is set to ceil(c/m).
+ */
+static __isl_give isl_qpolynomial *make_divs_pos(__isl_take isl_qpolynomial *qp,
+ int *signs)
+{
+ int i, j;
+ int total;
+ isl_vec *v = NULL;
+ struct isl_upoly *s;
+
+ qp = isl_qpolynomial_cow(qp);
+ if (!qp)
+ return NULL;
+ qp->div = isl_mat_cow(qp->div);
+ if (!qp->div)
+ goto error;
+
+ total = isl_space_dim(qp->dim, isl_dim_all);
+ v = isl_vec_alloc(qp->div->ctx, qp->div->n_col - 1);
+
+ for (i = 0; i < qp->div->n_row; ++i) {
+ isl_int *row = qp->div->row[i];
+ v = isl_vec_clr(v);
+ if (!v)
+ goto error;
+ if (isl_int_lt(row[1], row[0])) {
+ isl_int_fdiv_q(v->el[0], row[1], row[0]);
+ isl_int_sub_ui(v->el[0], v->el[0], 1);
+ isl_int_submul(row[1], row[0], v->el[0]);
+ }
+ for (j = 0; j < total; ++j) {
+ if (isl_int_sgn(row[2 + j]) * signs[j] >= 0)
+ continue;
+ if (signs[j] < 0)
+ isl_int_cdiv_q(v->el[1 + j], row[2 + j], row[0]);
+ else
+ isl_int_fdiv_q(v->el[1 + j], row[2 + j], row[0]);
+ isl_int_submul(row[2 + j], row[0], v->el[1 + j]);
+ }
+ for (j = 0; j < i; ++j) {
+ if (isl_int_sgn(row[2 + total + j]) >= 0)
+ continue;
+ isl_int_fdiv_q(v->el[1 + total + j],
+ row[2 + total + j], row[0]);
+ isl_int_submul(row[2 + total + j],
+ row[0], v->el[1 + total + j]);
+ }
+ for (j = i + 1; j < qp->div->n_row; ++j) {
+ if (isl_int_is_zero(qp->div->row[j][2 + total + i]))
+ continue;
+ isl_seq_combine(qp->div->row[j] + 1,
+ qp->div->ctx->one, qp->div->row[j] + 1,
+ qp->div->row[j][2 + total + i], v->el, v->size);
+ }
+ isl_int_set_si(v->el[1 + total + i], 1);
+ s = isl_upoly_from_affine(qp->dim->ctx, v->el,
+ qp->div->ctx->one, v->size);
+ qp->upoly = isl_upoly_subs(qp->upoly, total + i, 1, &s);
+ isl_upoly_free(s);
+ if (!qp->upoly)
+ goto error;
+ }
+
+ isl_vec_free(v);
+ return qp;
+error:
+ isl_vec_free(v);
+ isl_qpolynomial_free(qp);
+ return NULL;
+}
+
+struct isl_to_poly_data {
+ int sign;
+ isl_pw_qpolynomial *res;
+ isl_qpolynomial *qp;
+};
+
+/* Appoximate data->qp by a polynomial on the orthant identified by "signs".
+ * We first make all integer divisions positive and then split the
+ * quasipolynomials into terms with sign data->sign (the direction
+ * of the requested approximation) and terms with the opposite sign.
+ * In the first set of terms, each integer division [a/m] is
+ * overapproximated by a/m, while in the second it is underapproximated
+ * by (a-(m-1))/m.
+ */
+static int to_polynomial_on_orthant(__isl_take isl_set *orthant, int *signs,
+ void *user)
+{
+ struct isl_to_poly_data *data = user;
+ isl_pw_qpolynomial *t;
+ isl_qpolynomial *qp, *up, *down;
+
+ qp = isl_qpolynomial_copy(data->qp);
+ qp = make_divs_pos(qp, signs);
+
+ up = isl_qpolynomial_terms_of_sign(qp, signs, data->sign);
+ up = qp_drop_floors(up, 0);
+ down = isl_qpolynomial_terms_of_sign(qp, signs, -data->sign);
+ down = qp_drop_floors(down, 1);
+
+ isl_qpolynomial_free(qp);
+ qp = isl_qpolynomial_add(up, down);
+
+ t = isl_pw_qpolynomial_alloc(orthant, qp);
+ data->res = isl_pw_qpolynomial_add_disjoint(data->res, t);
+
+ return 0;
+}
+
+/* Approximate each quasipolynomial by a polynomial. If "sign" is positive,
+ * the polynomial will be an overapproximation. If "sign" is negative,
+ * it will be an underapproximation. If "sign" is zero, the approximation
+ * will lie somewhere in between.
+ *
+ * In particular, is sign == 0, we simply drop the floors, turning
+ * the integer divisions into rational divisions.
+ * Otherwise, we split the domains into orthants, make all integer divisions
+ * positive and then approximate each [a/m] by either a/m or (a-(m-1))/m,
+ * depending on the requested sign and the sign of the term in which
+ * the integer division appears.
+ */
+__isl_give isl_pw_qpolynomial *isl_pw_qpolynomial_to_polynomial(
+ __isl_take isl_pw_qpolynomial *pwqp, int sign)
+{
+ int i;
+ struct isl_to_poly_data data;
+
+ if (sign == 0)
+ return pwqp_drop_floors(pwqp);
+
+ if (!pwqp)
+ return NULL;
+
+ data.sign = sign;
+ data.res = isl_pw_qpolynomial_zero(isl_pw_qpolynomial_get_space(pwqp));
+
+ for (i = 0; i < pwqp->n; ++i) {
+ if (pwqp->p[i].qp->div->n_row == 0) {
+ isl_pw_qpolynomial *t;
+ t = isl_pw_qpolynomial_alloc(
+ isl_set_copy(pwqp->p[i].set),
+ isl_qpolynomial_copy(pwqp->p[i].qp));
+ data.res = isl_pw_qpolynomial_add_disjoint(data.res, t);
+ continue;
+ }
+ data.qp = pwqp->p[i].qp;
+ if (isl_set_foreach_orthant(pwqp->p[i].set,
+ &to_polynomial_on_orthant, &data) < 0)
+ goto error;
+ }
+
+ isl_pw_qpolynomial_free(pwqp);
+
+ return data.res;
+error:
+ isl_pw_qpolynomial_free(pwqp);
+ isl_pw_qpolynomial_free(data.res);
+ return NULL;
+}
+
+static int poly_entry(void **entry, void *user)
+{
+ int *sign = user;
+ isl_pw_qpolynomial **pwqp = (isl_pw_qpolynomial **)entry;
+
+ *pwqp = isl_pw_qpolynomial_to_polynomial(*pwqp, *sign);
+
+ return *pwqp ? 0 : -1;
+}
+
+__isl_give isl_union_pw_qpolynomial *isl_union_pw_qpolynomial_to_polynomial(
+ __isl_take isl_union_pw_qpolynomial *upwqp, int sign)
+{
+ upwqp = isl_union_pw_qpolynomial_cow(upwqp);
+ if (!upwqp)
+ return NULL;
+
+ if (isl_hash_table_foreach(upwqp->dim->ctx, &upwqp->table,
+ &poly_entry, &sign) < 0)
+ goto error;
+
+ return upwqp;
+error:
+ isl_union_pw_qpolynomial_free(upwqp);
+ return NULL;
+}
+
+__isl_give isl_basic_map *isl_basic_map_from_qpolynomial(
+ __isl_take isl_qpolynomial *qp)
+{
+ int i, k;
+ isl_space *dim;
+ isl_vec *aff = NULL;
+ isl_basic_map *bmap = NULL;
+ unsigned pos;
+ unsigned n_div;
+
+ if (!qp)
+ return NULL;
+ if (!isl_upoly_is_affine(qp->upoly))
+ isl_die(qp->dim->ctx, isl_error_invalid,
+ "input quasi-polynomial not affine", goto error);
+ aff = isl_qpolynomial_extract_affine(qp);
+ if (!aff)
+ goto error;
+ dim = isl_qpolynomial_get_space(qp);
+ pos = 1 + isl_space_offset(dim, isl_dim_out);
+ n_div = qp->div->n_row;
+ bmap = isl_basic_map_alloc_space(dim, n_div, 1, 2 * n_div);
+
+ for (i = 0; i < n_div; ++i) {
+ k = isl_basic_map_alloc_div(bmap);
+ if (k < 0)
+ goto error;
+ isl_seq_cpy(bmap->div[k], qp->div->row[i], qp->div->n_col);
+ isl_int_set_si(bmap->div[k][qp->div->n_col], 0);
+ if (isl_basic_map_add_div_constraints(bmap, k) < 0)
+ goto error;
+ }
+ k = isl_basic_map_alloc_equality(bmap);
+ if (k < 0)
+ goto error;
+ isl_int_neg(bmap->eq[k][pos], aff->el[0]);
+ isl_seq_cpy(bmap->eq[k], aff->el + 1, pos);
+ isl_seq_cpy(bmap->eq[k] + pos + 1, aff->el + 1 + pos, n_div);
+
+ isl_vec_free(aff);
+ isl_qpolynomial_free(qp);
+ bmap = isl_basic_map_finalize(bmap);
+ return bmap;
+error:
+ isl_vec_free(aff);
+ isl_qpolynomial_free(qp);
+ isl_basic_map_free(bmap);
+ return NULL;
+}