/** @file
 *****************************************************************************
 * @author     This file is part of libsnark, developed by SCIPR Lab
 *             and contributors (see AUTHORS).
 * @copyright  MIT license (see LICENSE file)
 *****************************************************************************/

#ifndef PROTOBOARD_HPP_
#define PROTOBOARD_HPP_

#include <algorithm>
#include <cassert>
#include <cstdio>
#include <string>
#include <vector>

#include <libff/common/utils.hpp>

#include <libsnark/gadgetlib1/pb_variable.hpp>
#include <libsnark/relations/constraint_satisfaction_problems/r1cs/r1cs.hpp>

namespace libsnark {

template<typename FieldT>
class r1cs_constraint;

template<typename FieldT>
class r1cs_constraint_system;

template<typename FieldT>
class protoboard {
private:
    FieldT constant_term; /* only here, because pb.val() needs to be able to return reference to the constant 1 term */
    r1cs_variable_assignment<FieldT> values; /* values[0] will hold the value of the first allocated variable of the protoboard, *NOT* constant 1 */
    var_index_t next_free_var;
    lc_index_t next_free_lc;
    std::vector<FieldT> lc_values;
    r1cs_constraint_system<FieldT> constraint_system;

public:
    protoboard();

    void clear_values();

    FieldT& val(const pb_variable<FieldT> &var);
    FieldT val(const pb_variable<FieldT> &var) const;

    FieldT& lc_val(const pb_linear_combination<FieldT> &lc);
    FieldT lc_val(const pb_linear_combination<FieldT> &lc) const;

    void add_r1cs_constraint(const r1cs_constraint<FieldT> &constr, const std::string &annotation="");
    void augment_variable_annotation(const pb_variable<FieldT> &v, const std::string &postfix);
    bool is_satisfied() const;
    void dump_variables() const;

    size_t num_constraints() const;
    size_t num_inputs() const;
    size_t num_variables() const;

    void set_input_sizes(const size_t primary_input_size);

    r1cs_variable_assignment<FieldT> full_variable_assignment() const;
    r1cs_primary_input<FieldT> primary_input() const;
    r1cs_auxiliary_input<FieldT> auxiliary_input() const;
    r1cs_constraint_system<FieldT> get_constraint_system() const;

    friend class pb_variable<FieldT>;
    friend class pb_linear_combination<FieldT>;

private:
    var_index_t allocate_var_index(const std::string &annotation="");
    lc_index_t allocate_lc_index();
};

} // libsnark
#include <libsnark/gadgetlib1/protoboard.tcc>
#endif // PROTOBOARD_HPP_
