#include "operation.h"

#include <cassert>

void SetOp::Apply(Database& database) const
{
    assert(dc >= 0);
    assert(field >= 0);
    assert(field < static_cast<FieldId>(database.size()));
    auto& target = database[field];
    if (ins) target.insert(value);
    else if (dc == 0) target.erase(value);
}

void IT(SetOp& O1, const SetOp& O2)
{
    assert(O1.opid.s != O2.opid.s);
    if (O1.field == O2.field && O2.ins && !O1.ins && O1.value == O2.value) ++O1.dc;
}

void ET(SetOp& O1, const SetOp& O2)
{
    assert(O1.opid.s != O2.opid.s);
    if (O1.field == O2.field && O2.ins && !O1.ins && O1.value == O2.value)
    {
        assert(O1.dc > 0);
        --O1.dc;
    }
}

void DualIT(SetOp& O1, SetOp& O2)
{
    const SetOp copyO1 = O1;
    IT(O1, O2);
    IT(O2, copyO1);
}

void Transpose(SetOp& O1, SetOp& O2)
{
    ET(O2, O1);
    IT(O1, O2);
}

std::ostream& operator<<(std::ostream& os, const SetOp& operation)
{
    os << (operation.ins ? 'I' : 'D') << operation.field << ':' << operation.value;
    if (operation.dc) os << " dc=" << operation.dc;
    os << " @(" << operation.opid.s << ',' << operation.opid.t << ')';
    return os;
}

std::ostream& operator<<(std::ostream& os, const Database& database)
{
    for (std::size_t i = 0; i < database.size(); ++i)
    {
        if (i) os << ' ';
        os << 'S' << i << "={";
        for (Value value : database[i]) os << value << ' ';
        os << '}';
    }
    return os;
}
