////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 Leo Tenenbaum
// This file is part of GraphColoring.
//
// GraphColoring 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 3 of the License, or
// (at your option) any later version.
//
// GraphColoring 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 GraphColoring. If not, see .
////////////////////////////////////////////////////////////////////////////////
#include "value.hpp"
#include
#include
#include "utils/errors.hpp"
namespace graphcoloring {
std::map Value::operation_table;
Value::Value() : type(Type::NULL_TYPE) {}
Value::Value(int val_)
{
val = val_;
type = Type::VALUE;
}
Value::Value(std::vector list_)
{
list = list_;
type = Type::LIST;
}
Value::Value(const std::string& string)
{
FromString(string);
}
void Value::FromString(const std::string& string)
{
const std::map type_map =
{
{"", Type::NULL_TYPE},
{"V", Type::NUMBER_OF_VERTICES},
{"E", Type::NUMBER_OF_EDGES},
{"vertices", Type::VERTICES},
{"edges", Type::EDGES}
};
if (type_map.count(string))
{
type = type_map.at(string);
return;
}
try
{
val = std::stoi(string);
type = Type::VALUE;
}
catch (std::invalid_argument&)
{
// NOTE: Does not check if variable exists! This means that errors
// will only be caught when the variable is evaluated, but it allows
// out-of-order declaration.
type = Type::VARIABLE;
variable_name = string;
}
}
Value::Value(pugi::xml_node node)
{
std::string name = node.name();
if (name == "op")
{
std::string v1 = node.attribute("val1").value();
std::string v2 = node.attribute("val2").value();
std::string op = node.attribute("op").value();
type = Type::OPERATION;
val1 = std::shared_ptr(new Value(v1));
val2 = std::shared_ptr(new Value(v2));
ReadOperation(op);
}
else if (name == "var")
{
FromString(node.attribute("val").value());
}
else if (name == "map" || name == "fold")
{
if (name == "map")
type = Type::MAP;
else
type = Type::FOLD;
std::string op = node.attribute("op").value();
std::string v = node.attribute("val").value();
std::string start = node.attribute("start").value();
if (v == "")
utils::errors::Die("No argument passed to " + name + ".");
ReadOperation(op);
val1 = std::shared_ptr(new Value(v));
fold_start = std::shared_ptr(new Value(start));
}
else if (name == "zip")
{
type = Type::ZIP;
std::string op = node.attribute("op").value();
std::string v1 = node.attribute("val1").value();
std::string v2 = node.attribute("val2").value();
if (v1 == "" || v2 == "")
utils::errors::Die("Empty argument passed to zip.");
ReadOperation(op);
val1 = std::shared_ptr(new Value(v1));
val2 = std::shared_ptr(new Value(v2));
}
else
{
utils::errors::Die("Unknown tag: " + name);
}
}
Value::operation_t Value::SimpleOperation(std::function op)
{
return [op](const Graph&, int a, int b) { return op(a, b); };
}
Value::operation_t Value::SimpleBoolOperation(std::function op)
{
return [op](const Graph&, int a, int b) { return op(a, b) ? 1 : 0; };
}
Value::operation_t Value::SimpleLogicOperation(std::functionop)
{
return [op](const Graph&, int a, int b) {
return op(a != 0, b != 0) ? 1 : 0;
};
}
void Value::InitializeOperationTable()
{
operation_table = {
{"min", SimpleOperation([](int a, int b)->int{return std::min(a,b);})},
{"max", SimpleOperation([](int a, int b)->int{return std::max(a,b);})},
{"+", SimpleOperation(std::plus())},
{"-", SimpleOperation(std::minus())},
{"*", SimpleOperation(std::multiplies())},
{"/", SimpleOperation(std::divides())},
{"%", SimpleOperation(std::modulus())},
{"=", SimpleBoolOperation(std::equal_to())},
{"!=", SimpleBoolOperation(std::not_equal_to())},
{"<", SimpleBoolOperation(std::less())},
{">", SimpleBoolOperation(std::greater())},
{"<=", SimpleBoolOperation(std::less_equal())},
{">=", SimpleBoolOperation(std::greater_equal())},
{"and", SimpleLogicOperation(std::logical_and())},
{"or", SimpleLogicOperation(std::logical_or())},
{"not", SimpleLogicOperation([](bool a, bool b)->bool{
return !a; // Ignore second argument.
})},
{"v1", [](const Graph& graph, int e, int)->int {
return graph.GetEdgeByIDConst(e).from.id;
}},
{"v2", [](const Graph& graph, int e, int)->int {
return graph.GetEdgeByIDConst(e).to.id;
}},
{"connected", [](const Graph& graph, int v1, int v2)->int {
if (!graph.HasVertexWithID(v1) || !graph.HasVertexWithID(v2))
return 0;
return graph.HasEdge(v1, v2) ? 1 : 0;
}},
{"degree", [](const Graph& graph, int v, int)->int {
return graph.Degree(v);
}},
{"vertex-color", [](const Graph& graph, int v_id, int)->int {
const Vertex& v = graph.GetVertexByIDConst(v_id);
return (int)v.Color();
}},
{"edge-color", [](const Graph& graph, int e_id, int)->int {
const Edge& e = graph.GetEdgeByIDConst(e_id);
return (int)e.Color();
}}
};
}
void Value::ReadOperation(std::string op)
{
if (operation_table.size() == 0)
InitializeOperationTable();
if (operation_table.count(op))
{
operation = operation_table[op];
}
else
{
utils::errors::Die("Invalid operation: " + op);
}
}
std::vector Value::EvalListOperation(const Graph& graph,
std::function lookup_variable) const
{
switch (type)
{
case Type::VARIABLE:
return lookup_variable(variable_name)
.EvalListOperation(graph, lookup_variable);
case Type::VERTICES:
{
std::vector list;
for (const Vertex* v : graph.vertices)
{
list.push_back(v->id);
}
return list;
}
case Type::EDGES:
{
std::vector list;
for (const Edge* e : graph.edges)
{
list.push_back(e->id);
}
return list;
}
case Type::LIST:
{
std::vector out;
for (Value v : list)
out.push_back(v.Eval(graph, lookup_variable));
return out;
}
case Type::MAP:
{
std::vector in = val1->EvalListOperation(graph, lookup_variable);
std::vector out;
for (int v : in)
out.push_back(operation(graph, v, 0));
return out;
}
case Type::ZIP:
{
std::vector in1= val1->EvalListOperation(graph, lookup_variable);
std::vector in2= val2->EvalListOperation(graph, lookup_variable);
std::vector out;
for (int i = 0; i < (int)std::min(in1.size(),in2.size()); i++)
out.push_back(operation(graph, in1[i], in2[i]));
return out;
}
default: break;
}
std::stringstream s;
s << "Invalid list operation: " << (int)type << ".";
utils::errors::Die(s.str());
return std::vector();
}
int Value::Eval(const Graph& graph,
std::function lookup_variable) const
{
switch (type)
{
case Type::NULL_TYPE:
return 0;
case Type::VALUE:
return val;
case Type::VARIABLE:
return lookup_variable(variable_name).Eval(graph, lookup_variable);
case Type::NUMBER_OF_VERTICES:
return graph.V();
case Type::NUMBER_OF_EDGES:
return graph.E();
case Type::OPERATION:
assert(operation);
return operation(graph,
val1->Eval(graph, lookup_variable),
val2->Eval(graph, lookup_variable));
case Type::FOLD:
{
assert(operation);
int x = fold_start->Eval(graph, lookup_variable);
for (const Value& v : val1->EvalListOperation(graph, lookup_variable))
{
x = operation(graph, x, v.Eval(graph, lookup_variable));
}
return x;
}
default: break;
}
std::stringstream s;
s << "Invalid operation: " << (int)type << ".";
utils::errors::Die(s.str());
return -1;
}
} // namespace graphcoloring