#pragma once

#include <alias/data_structure/vector.h>
#include <alias/str.h>
#include <stdbool.h>

struct solver_Algebra {
  alias_Vector(uint64_t) values;
};

// lifetime
void solver_Algebra_init(struct solver_Algebra *a);
void solver_Algebra_free(struct solver_Algebra *a);

// construction
uint64_t solver_Algebra_boolean(struct solver_Algebra *a, bool value);
uint64_t solver_Algebra_undefined(struct solver_Algebra *a);
uint64_t solver_Algebra_integer(struct solver_Algebra *a, int64_t value);
uint64_t solver_Algebra_fraction(struct solver_Algebra *a, uint64_t numerator, uint64_t denominator);
uint64_t solver_Algebra_real(struct solver_Algebra *a, double value);
uint64_t solver_Algebra_variable(struct solver_Algebra *a, const char * name);
uint64_t solver_Algebra_sum(struct solver_Algebra *a, uint32_t num_operands, const uint64_t *operands);
uint64_t solver_Algebra_product(struct solver_Algebra *a, uint32_t num_operands, const uint64_t *operands);
uint64_t solver_Algebra_power(struct solver_Algebra *a, uint64_t base, uint64_t exponent);

uint64_t solver_Algebra_equals(struct solver_Algebra *a, uint64_t lhs, uint64_t rhs);
uint64_t solver_Algebra_not_equals(struct solver_Algebra *a, uint64_t lhs, uint64_t rhs);
uint64_t solver_Algebra_less_than(struct solver_Algebra *a, uint64_t lhs, uint64_t rhs);
uint64_t solver_Algebra_greater_than(struct solver_Algebra *a, uint64_t lhs, uint64_t rhs);

static inline uint64_t solver_Algebra_add(struct solver_Algebra *a, uint64_t lhs, uint64_t rhs) {
  uint64_t operands[2] = {lhs, rhs};
  return solver_Algebra_sum(a, 2, operands);
}

static inline uint64_t solver_Algebra_multiply(struct solver_Algebra *a, uint64_t lhs, uint64_t rhs) {
  uint64_t operands[2] = {lhs, rhs};
  return solver_Algebra_product(a, 2, operands);
}

static inline uint64_t solver_Algebra_negate(struct solver_Algebra *a, uint64_t expr) {
  return solver_Algebra_multiply(a, solver_Algebra_integer(a, -1), expr);
}

static inline uint64_t solver_Algebra_subtract(struct solver_Algebra *a, uint64_t lhs, uint64_t rhs) {
  return solver_Algebra_add(a, lhs, solver_Algebra_negate(a, rhs));
}

static inline uint64_t solver_Algebra_inverse(struct solver_Algebra *a, uint64_t expr) {
  return solver_Algebra_power(a, expr, solver_Algebra_integer(a, -1));
}

static inline uint64_t solver_Algebra_divide(struct solver_Algebra *a, uint64_t lhs, uint64_t rhs) {
  return solver_Algebra_multiply(a, lhs, solver_Algebra_inverse(a, rhs));
}

// operations
uint64_t solver_Algebra_parse(struct solver_Algebra *a, const char * string);

void solver_Algebra_show(struct solver_Algebra *a, uint64_t expr);

uint64_t solver_Algebra_to_boolean(struct solver_Algebra *a, uint64_t expr);

uint64_t solver_Algebra_simplify(struct solver_Algebra *a, uint64_t expr);

uint64_t solver_Algebra_substitute(struct solver_Algebra *a, uint64_t expr, uint64_t search, uint64_t replace);

uint64_t solver_Algebra_has(struct solver_Algebra *a, uint64_t expr, uint64_t search);