/* A sort of clone of the cddlib test program `lcdd'.
Copyright (C) 2001-2004 Roberto Bagnara <bagnara@cs.unipr.it>
This file is part of the Parma Polyhedra Library (PPL).
The PPL is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
The PPL is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
For the most up-to-date information see the Parma Polyhedra Library
site: http://www.cs.unipr.it/ppl/ . */
#include "config.h"
#include "ppl_install.hh"
#include <cstdarg>
#include <csignal>
#include <cerrno>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <stdexcept>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
namespace PPL = Parma_Polyhedra_Library;
#if PPL_VERSION_MAJOR == 0 && PPL_VERSION_MINOR < 6
# error "PPL version 0.6 or following is required"
#endif
namespace {
struct option long_options[] = {
{"max-cpu", required_argument, 0, 'C'},
{"max-memory", required_argument, 0, 'V'},
{"help", no_argument, 0, 'h'},
{"output", required_argument, 0, 'o'},
{"timings", no_argument, 0, 't'},
{"verbose", no_argument, 0, 'v'},
{"check", required_argument, 0, 'c'},
{0, 0, 0, 0}
};
static const char* usage_string
= "Usage: %s [OPTION]... [FILE]...\n\n"
" -CSECS, --max-cpu=SECS limits CPU usage to SECS seconds\n"
" -VMB, --max-memory=MB limits memory usage to MB megabytes\n"
" -h, --help prints this help text to stderr\n"
" -oPATH, --output=PATH appends output to PATH\n"
" -t, --timings prints timings to stderr\n"
" -v, --verbose produces lots of output\n"
" -cPATH, --check=PATH checks if the result is equal to what is in PATH\n";
#define OPTION_LETTERS "C:V:ho:tvc:"
const char* program_name = 0;
unsigned long max_seconds_of_cpu_time = 0;
unsigned long max_bytes_of_virtual_memory = 0;
bool print_timings = false;
bool verbose = false;
const char* check_file_name = 0;
void
fatal(const char* format, ...) {
va_list ap;
va_start(ap, format);
fprintf(stderr, "%s: ", program_name);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
namespace {
const char* input_file_name = 0;
std::istream* input_stream_p = 0;
void
set_input(const char* file_name) {
if (input_stream_p && *input_stream_p != std::cin)
delete input_stream_p;
if (file_name) {
input_stream_p = new std::ifstream(file_name, std::ios_base::in);
if (!*input_stream_p)
fatal("cannot open input file `%s'", file_name);
input_file_name = file_name;
}
else {
input_stream_p = &std::cin;
input_file_name = "<cin>";
}
}
std::istream&
input() {
assert(input_stream_p);
return *input_stream_p;
}
const char* output_file_name = 0;
std::ostream* output_stream_p = 0;
void
set_output(const char* file_name) {
if (output_stream_p && *output_stream_p != std::cout)
delete output_stream_p;
if (file_name) {
output_stream_p = new std::ofstream(file_name,
std::ios_base::out
| std::ios_base::app);
if (!*output_stream_p)
fatal("cannot open output file `%s'", file_name);
output_file_name = file_name;
}
else {
output_stream_p = &std::cout;
output_file_name = "<cout>";
}
}
std::ostream&
output() {
assert(output_stream_p);
return *output_stream_p;
}
} // namespace
void
error(const char* format, ...) {
va_list ap;
va_start(ap, format);
fprintf(stderr, "%s: in `%s': ", program_name, input_file_name);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
void
warning(const char* format, ...) {
va_list ap;
va_start(ap, format);
fprintf(stderr, "%s: Warning: in `%s': ", program_name, input_file_name);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
}
extern "C" typedef void (*sig_handler_type)(int);
void
set_alarm_on_cpu_time(const unsigned seconds, sig_handler_type handler) {
sigset_t mask;
sigemptyset(&mask);
struct sigaction s;
s.sa_handler = handler;
s.sa_mask = mask;
#if defined(SA_ONESHOT)
s.sa_flags = SA_ONESHOT;
#elif defined(SA_RESETHAND)
s.sa_flags = SA_RESETHAND;
#else
#error "Either SA_ONESHOT or SA_RESETHAND must be defined."
#endif
if (sigaction(SIGXCPU, &s, 0) != 0)
fatal("sigaction failed: %s", strerror(errno));
struct rlimit t;
if (getrlimit(RLIMIT_CPU, &t) != 0)
fatal("getrlimit failed: %s", strerror(errno));
if (seconds < t.rlim_cur) {
t.rlim_cur = seconds;
if (setrlimit(RLIMIT_CPU, &t) != 0)
fatal("setrlimit failed: %s", strerror(errno));
}
}
#if HAVE_DECL_RLIMIT_AS
void
limit_virtual_memory(const unsigned bytes) {
struct rlimit t;
if (getrlimit(RLIMIT_AS, &t) != 0)
fatal("getrlimit failed: %s", strerror(errno));
if (bytes < t.rlim_cur) {
t.rlim_cur = bytes;
if (setrlimit(RLIMIT_AS, &t) != 0)
fatal("setrlimit failed: %s", strerror(errno));
}
}
#else
void
limit_virtual_memory(unsigned) {
}
#endif // !HAVE_DECL_RLIMIT_AS
extern "C" void
timeout(int) {
try {
std::cerr << "TIMEOUT"
<< std::endl;
}
catch (...) {
}
try {
if (output_file_name)
output() << "TIMEOUT"
<< std::endl;
}
catch (...) {
}
exit(0);
}
void
process_options(int argc, char* argv[]) {
while (true) {
int option_index = 0;
int c = getopt_long(argc, argv, OPTION_LETTERS, long_options,
&option_index);
if (c == EOF)
break;
char* endptr;
long l;
switch (c) {
case 0:
break;
case '?':
case 'h':
fprintf(stderr, usage_string, argv[0]);
exit(0);
break;
case 'C':
l = strtol(optarg, &endptr, 10);
if (*endptr || l < 0)
fatal("a non-negative integer must follow `-C'");
else
max_seconds_of_cpu_time = l;
break;
case 'V':
l = strtol(optarg, &endptr, 10);
if (*endptr || l < 0)
fatal("a non-negative integer must follow `-V'");
else
max_bytes_of_virtual_memory = l*1024*1024;
break;
case 'o':
output_file_name = optarg;
break;
case 't':
print_timings = true;
break;
case 'v':
verbose = true;
break;
case 'c':
check_file_name = optarg;
break;
default:
abort();
}
}
if (argc - optind > 1)
// We have multiple input files.
fatal("at most one input file is accepted");
// We have one input files.
if (optind < argc)
input_file_name = argv[optind];
else
// If no input files have been specified: we will read from standard input.
assert(input_file_name == 0);
}
void
normalize(const std::vector<mpq_class>& source,
std::vector<mpz_class>& dest,
mpz_class& denominator) {
unsigned n = source.size();
denominator = 1;
for (unsigned i = 0; i < n; ++i)
mpz_lcm(denominator.get_mpz_t(),
denominator.get_mpz_t(),
source[i].get_den().get_mpz_t());
for (unsigned i = 0; i < n; ++i)
dest[i] = denominator*source[i];
}
template <typename T>
bool
guarded_read(std::istream& in, T& x) {
try {
return in >> x;
}
catch (...) {
return false;
}
}
template <typename T>
void
guarded_write(std::ostream& out, const T& x) {
bool succeeded = false;
try {
succeeded = out << x;
}
catch (...) {
}
if (!succeeded)
fatal("cannot write to output file `%s'", output_file_name);
}
enum Number_Type { INTEGER, RATIONAL, REAL };
void
read_coefficients(std::istream& in,
const Number_Type number_type,
std::vector<mpz_class>& coefficients,
mpz_class& denominator) {
unsigned num_coefficients = coefficients.size();
switch (number_type) {
case INTEGER:
{
for (unsigned i = 0; i < num_coefficients; ++i)
if (!guarded_read(in, coefficients[i]))
error("missing or invalid integer coefficient");
denominator = 1;
break;
}
case RATIONAL:
{
std::vector<mpq_class> rational_coefficients(num_coefficients);
for (unsigned i = 0; i < num_coefficients; ++i)
if (!guarded_read(in, rational_coefficients[i]))
error("missing or invalid rational coefficient");
normalize(rational_coefficients, coefficients, denominator);
break;
}
case REAL:
{
std::vector<mpq_class> rational_coefficients(num_coefficients);
for (unsigned i = 0; i < num_coefficients; ++i) {
double d;
if (!guarded_read(in, d))
error("missing or invalid real coefficient");
rational_coefficients[i] = mpq_class(d);
}
normalize(rational_coefficients, coefficients, denominator);
break;
}
}
}
void
read_indexes_set(std::istream& in,
std::set<unsigned>& dest,
const char* what) {
assert(dest.empty());
unsigned num_elements;
if (!guarded_read(in, num_elements))
error("missing or invalid number of set elements in `%s'", what);
while (num_elements--) {
unsigned i;
if (!guarded_read(in, i))
error("missing or invalid set element in `%s'", what);
dest.insert(i);
}
}
enum Representation { H, V };
Representation
read_polyhedron(std::istream& in, PPL::C_Polyhedron& ph) {
// By default we have an H-representation.
Representation rep = H;
std::string s;
std::set<unsigned> linearity;
while (true) {
if (!guarded_read(in, s))
error("premature end of file while seeking for `begin'");
if (s == "V-representation")
rep = V;
else if (s == "H-representation")
rep = H;
else if (s == "linearity" || s == "equality" || s == "partial_enum") {
read_indexes_set(in, linearity, "linearity");
if (verbose) {
std::cerr << "Linearity: ";
for (std::set<unsigned>::const_iterator j = linearity.begin(),
linearity_end = linearity.end(); j != linearity_end; ++j)
std::cerr << *j << " ";
std::cerr << std::endl;
}
}
else if (s == "begin")
break;
else
// A comment: skip to end of line.
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
unsigned num_rows;
if (!guarded_read(in, num_rows))
error("illegal or missing number of rows");
unsigned num_columns;
if (!guarded_read(in, num_columns))
error("illegal or missing number of columns");
if (!guarded_read(in, s))
error("missing number type");
Number_Type number_type = INTEGER;
if (s == "integer")
number_type = INTEGER;
else if (s == "rational")
number_type = RATIONAL;
else if (s == "real")
number_type = REAL;
else
error("illegal number type `%s'", s.c_str());
if (verbose)
std::cerr << "Problem dimension: " << num_rows << " x " << num_columns
<< "; number type: " << s
<< std::endl;
PPL::ConSys cs;
PPL::GenSys gs;
std::set<unsigned>::iterator linearity_end = linearity.end();
if (rep == V) {
std::vector<mpz_class> coefficients(num_columns-1);
mpz_class denominator;
bool has_a_point = false;
for (unsigned i = 0; i < num_rows; ++i) {
int vertex_marker;
if (!guarded_read(in, vertex_marker)
|| vertex_marker < 0 || vertex_marker > 1)
error("illegal or missing vertex marker");
read_coefficients(in, number_type, coefficients, denominator);
PPL::LinExpression e;
for (unsigned j = num_columns-1; j-- > 0; )
e += coefficients[j] * PPL::Variable(j);
if (vertex_marker == 1) {
assert(linearity.find(i+1) == linearity_end);
gs.insert(point(e, denominator));
has_a_point = true;
}
else if (linearity.find(i+1) != linearity_end)
gs.insert(line(e));
else
gs.insert(ray(e));
}
// Every non-empty generator system must have at least one point.
if (num_rows > 0 && !has_a_point)
gs.insert(PPL::point());
if (verbose) {
using namespace PPL::IO_Operators;
std::cerr << "Generator system:\n" << gs << std::endl;
}
}
else {
assert(rep == H);
std::vector<mpz_class> coefficients(num_columns);
mpz_class denominator;
for (unsigned i = 0; i < num_rows; ++i) {
read_coefficients(in, number_type, coefficients, denominator);
PPL::LinExpression e;
for (unsigned j = num_columns; j-- > 1; )
e += coefficients[j] * PPL::Variable(j-1);
e += coefficients[0];
if (linearity.find(i+1) != linearity_end)
cs.insert(e == 0);
else
cs.insert(e >= 0);
}
if (verbose) {
using namespace PPL::IO_Operators;
std::cerr << "Constraint system:\n" << cs << std::endl;
}
}
if (!guarded_read(in, s))
error("premature end of file while seeking for `end'");
if (s != "end")
error("`%s' found while seeking for `end'", s.c_str());
ph = rep == V ? PPL::C_Polyhedron(gs) : PPL::C_Polyhedron(cs);
return rep;
}
void
write_polyhedron(std::ostream& out,
const PPL::C_Polyhedron& ph,
const Representation rep) {
if (rep == H)
guarded_write(out, "H-representation\n");
else
guarded_write(out, "V-representation\n");
unsigned num_rows = 0;
std::set<unsigned> linearity;
if (rep == H) {
const PPL::ConSys& cs = ph.constraints();
for (PPL::ConSys::const_iterator i = cs.begin(),
cs_end = cs.end(); i != cs_end; ++i) {
++num_rows;
if (i->is_equality())
linearity.insert(linearity.end(), num_rows);
}
}
else {
assert(rep == V);
const PPL::GenSys& gs = ph.generators();
for (PPL::GenSys::const_iterator i = gs.begin(),
gs_end = gs.end(); i != gs_end; ++i) {
++num_rows;
if (i->is_line())
linearity.insert(linearity.end(), num_rows);
}
}
if (!linearity.empty()) {
guarded_write(out, "linearity ");
guarded_write(out, linearity.size());
for (std::set<unsigned>::const_iterator j = linearity.begin(),
linearity_end = linearity.end(); j != linearity_end; ++j) {
guarded_write(out, ' ');
guarded_write(out, *j);
}
guarded_write(out, '\n');
}
PPL::dimension_type space_dim = ph.space_dimension();
guarded_write(out, "begin\n");
guarded_write(out, num_rows);
guarded_write(out, ' ');
guarded_write(out, space_dim+1);
guarded_write(out, ' ');
if (rep == H) {
guarded_write(out, "integer\n");
const PPL::ConSys& cs = ph.constraints();
for (PPL::ConSys::const_iterator i = cs.begin(),
cs_end = cs.end(); i != cs_end; ++i) {
const PPL::Constraint& c = *i;
guarded_write(out, c.inhomogeneous_term());
for (PPL::dimension_type j = 0; j < space_dim; ++j) {
guarded_write(out, ' ');
guarded_write(out, c.coefficient(PPL::Variable(j)));
}
guarded_write(out, '\n');
}
}
else {
assert(rep == V);
guarded_write(out, "rational\n");
const PPL::GenSys& gs = ph.generators();
for (PPL::GenSys::const_iterator i = gs.begin(),
gs_end = gs.end(); i != gs_end; ++i) {
const PPL::Generator& g = *i;
if (g.is_point()) {
guarded_write(out, '1');
const PPL::Integer& divisor = g.divisor();
for (PPL::dimension_type j = 0; j < space_dim; ++j) {
guarded_write(out, ' ');
if (g.coefficient(PPL::Variable(j)) == 0)
guarded_write(out, '0');
else
guarded_write(out, mpq_class(g.coefficient(PPL::Variable(j)),
divisor));
}
}
else {
// `g' is a ray or a line.
guarded_write(out, '0');
for (PPL::dimension_type j = 0; j < space_dim; ++j) {
guarded_write(out, ' ');
guarded_write(out, g.coefficient(PPL::Variable(j)));
}
}
guarded_write(out, '\n');
}
}
guarded_write(out, "end\n");
// Flush `out'.
bool flush_succeeded = false;
try {
flush_succeeded = out.flush();
}
catch (...) {
}
if (!flush_succeeded)
fatal("cannot write to output file `%s'", output_file_name);
}
} // namespace
int
main(int argc, char* argv[]) try {
program_name = argv[0];
if (strcmp(PPL_VERSION, PPL::version()) != 0)
fatal("was compiled with PPL version %s, but linked with version %s",
PPL_VERSION, PPL::version());
if (verbose)
std::cerr << "Parma Polyhedra Library version:\n" << PPL::version()
<< "\n\nParma Polyhedra Library banner:\n" << PPL::banner()
<< std::endl;
// Process command line options.
process_options(argc, argv);
if (max_seconds_of_cpu_time > 0)
set_alarm_on_cpu_time(max_seconds_of_cpu_time, timeout);
if (max_bytes_of_virtual_memory > 0)
limit_virtual_memory(max_bytes_of_virtual_memory);
// Set up the input and output streams.
set_input(input_file_name);
set_output(output_file_name);
PPL::C_Polyhedron ph;
Representation rep = read_polyhedron(input(), ph);
//write_polyhedron(std::cout, ph, rep);
enum Command { None, H_to_V, V_to_H, Project };
Command command = None;
// Read commands, if any.
std::string s;
while (guarded_read(input(), s)) {
if (s == "linearity" || s == "equality" || s == "partial_enum")
error("the `linearity' command must occur before `begin'");
else if (s == "project") {
command = Project;
std::set<unsigned> indexes;
read_indexes_set(input(), indexes, "project");
if (verbose) {
std::cerr << "Project: ";
for (std::set<unsigned>::const_iterator j = indexes.begin(),
indexes_end = indexes.end(); j != indexes_end; ++j)
std::cerr << *j << " ";
std::cerr << std::endl;
}
PPL::Variables_Set vs;
for (std::set<unsigned>::const_iterator j = indexes.begin(),
indexes_end = indexes.end(); j != indexes_end; ++j)
vs.insert(PPL::Variable(*j - 1));
ph.remove_dimensions(vs);
write_polyhedron(output(), ph, H);
goto commands_done;
}
else
warning("ignoring command `%s'", s.c_str());
input().ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
// If we are still here, we just make a conversion.
if (rep == V) {
command = V_to_H;
ph.minimized_constraints();
write_polyhedron(output(), ph, H);
}
else {
command = H_to_V;
ph.minimized_generators();
write_polyhedron(output(), ph, V);
}
commands_done:
// Check the result, if requested to do so.
if (check_file_name) {
set_input(check_file_name);
// Read the polyhedron containing the expected result.
PPL::C_Polyhedron e_ph;
Representation e_rep = read_polyhedron(input(), e_ph);
switch (command) {
case Project:
{
if (ph != e_ph) {
if (verbose)
std::cerr << "Check failed: polyhedra differ"
<< std::endl;
return 1;
}
break;
}
case H_to_V:
{
if (e_rep == H)
warning("checking an H-to-V conversion with an H representation");
// Count the number of generators of `ph'.
unsigned ph_num_generators = 0;
const PPL::GenSys& ph_gs = ph.generators();
for (PPL::GenSys::const_iterator i = ph_gs.begin(),
ph_gs_end = ph_gs.end(); i != ph_gs_end; ++i)
++ph_num_generators;
// Count the number of generators of `e_ph'.
unsigned e_ph_num_generators = 0;
const PPL::GenSys& e_ph_gs = e_ph.generators();
for (PPL::GenSys::const_iterator i = e_ph_gs.begin(),
e_ph_gs_end = e_ph_gs.end(); i != e_ph_gs_end; ++i)
++e_ph_num_generators;
// If the polyhedra differ, that is the problem.
if (ph != e_ph) {
if (verbose)
std::cerr << "Check failed: polyhedra differ"
<< std::endl;
return 1;
}
else if (ph_num_generators != e_ph_num_generators)
// If we have different number of generators, we fail.
std::cerr << "Check failed: different number of generators:\n"
<< "expected " << e_ph_num_generators
<< ", obtained " << ph_num_generators
<< std::endl;
break;
}
case V_to_H:
{
if (e_rep == V)
warning("checking an V-to-H conversion with a V representation");
// Count the number of constraints of `ph'.
unsigned ph_num_constraints = 0;
const PPL::ConSys& ph_cs = ph.constraints();
for (PPL::ConSys::const_iterator i = ph_cs.begin(),
ph_cs_end = ph_cs.end(); i != ph_cs_end; ++i)
++ph_num_constraints;
// Count the number of constraints of `e_ph'.
unsigned e_ph_num_constraints = 0;
const PPL::ConSys& e_ph_cs = e_ph.constraints();
for (PPL::ConSys::const_iterator i = e_ph_cs.begin(),
e_ph_cs_end = e_ph_cs.end(); i != e_ph_cs_end; ++i)
++e_ph_num_constraints;
// If the polyhedra differ, that is the problem.
if (ph != e_ph) {
if (verbose)
std::cerr << "Check failed: polyhedra differ"
<< std::endl;
return 1;
}
else if (ph_num_constraints != e_ph_num_constraints)
// If we have different number of constraints, we fail.
std::cerr << "Check failed: different number of constraints:\n"
<< "expected " << e_ph_num_constraints
<< ", obtained " << ph_num_constraints
<< std::endl;
break;
}
case None:
break;
}
}
return 0;
}
catch(const std::bad_alloc&) {
fatal("out of memory");
exit(1);
}
catch(const std::overflow_error&) {
fatal("arithmetic overflow");
exit(1);
}
catch(...) {
fatal("internal error: please submit a bug report to ppl-devel@cs.unipr.it");
exit(1);
}
syntax highlighted by Code2HTML, v. 0.9.1