#include <alias/memory.h>

static uint64_t simplify(struct solver_Algebra *a, uint64_t bits);

#define simplify__EXTRA_PARAMS 
#define simplify__EXTRA_PASS   
#define simplify__FIRST_STATEMENT

#define simplify__REAL         return expr;
#define simplify__VARIABLE     return expr;
#define simplify__BOOLEAN      return expr;
#define simplify__UNDEFINED    return expr;
#define simplify__SMALLINT     return expr;
#define simplify__LARGEINT     return expr;
#define simplify__FRACTION     return changed ? embed_fraction(a, numerator, denominator) : expr;
#define simplify__POWER        return changed ? embed_power(a, base, exponent) : expr;
#define simplify__SUM          return changed ? embed_sum(a, count, values) : expr;
#define simplify__PRODUCT      return changed ? embed_product(a, count, values) : expr;
#define simplify__EQUALS       return changed ? solver_Algebra_equals(a, lhs, rhs) : expr;
#define simplify__NOT_EQUALS   return changed ? solver_Algebra_not_equals(a, lhs, rhs) : expr;
#define simplify__LESS_THAN    return changed ? solver_Algebra_less_than(a, lhs, rhs) : expr;
#define simplify__GREATER_THAN return changed ? solver_Algebra_greater_than(a, lhs, rhs) : expr;
#define simplify__INVALID      return pack_undefined();

MAP(simplify)

uint64_t solver_Algebra_simplify(struct solver_Algebra *a, uint64_t expr) {
  return simplify(a, expr);
}

/*#define SIMPLIFY(TYPE, FN) static uint64_t simplify__##TYPE(struct solver_Algebra *a, uint64_t bits) FN
#define SIMPLE(TYPE) SIMPLIFY(TYPE, { return bits; })
#define SIMPLE_PAIR(TYPE) \
  SIMPLIFY(TYPE, { \
    uint64_t values[2]; \
    values[0] = simplify(a, get_car(a, bits)); \
    values[1] = simplify(a, get_car(a, bits)); \
    if(values[0] == get_car(a, bits) && values[1] == get_cdr(a, bits)) { \
      return bits; \
    } \
    uint32_t index = embed_values(a, 2, values); \
    return pack_##TYPE(index); \
  })
#define SIMPLIFY_EMBEDED_LIST(TYPE) \
  SIMPLIFY(TYPE, { \
    uint32_t count = get_list_count(bits); \
    uint32_t index = get_list_index(bits); \
    uint64_t * values = alias_malloc(NULL, sizeof(*values) * count, alignof(*values)); \
    bool change = false; \
    for(uint32_t i = 0; i < count; i++) { \
      values[i] = simplify(a, a->values.data[index + i]); \
      if(values[i] != a->values.data[index + i]) { \
        change = true; \
      } \
    } \
    uint64_t result = change ? embed_##TYPE(a, count, values) : bits; \
    alias_free(NULL, values, sizeof(*values) * count, alignof(*values)); \
    return result; \
  })

SIMPLE(real)
SIMPLE(variable)
SIMPLE(boolean)
SIMPLE(undefined)
SIMPLE(smallint)
SIMPLE(largeint)

SIMPLIFY(fraction, {
  // TODO
  return bits;
})

SIMPLIFY(power, {
  // return embed_power(a, get_car(a, bits), get_cdr(a, bits));
  return bits;
})

SIMPLIFY_EMBEDED_LIST(sum)
SIMPLIFY_EMBEDED_LIST(product)

SIMPLE_PAIR(equals)
SIMPLE_PAIR(not_equals)
SIMPLE_PAIR(less_than)
SIMPLE_PAIR(greater_than)

#undef SIMPLIFY_EMBEDED_LIST
#undef SIMPLE_PAIR
#undef SIMPLE
#undef SIMPLIFY

static uint64_t simplify(struct solver_Algebra *a, uint64_t bits) {
  switch(get_tag(bits)) {
  case Tag_Real:
    return simplify__real(a, bits);
  case Tag_Variable:
    return simplify__variable(a, bits);
  case Tag_Boolean:
    return simplify__boolean(a, bits);
  case Tag_Undefined:
    return simplify__undefined(a, bits);
  case Tag_SmallInt:
    return simplify__smallint(a, bits);
  case Tag_Fraction:
    return simplify__fraction(a, bits);
  case Tag_Power:
    return simplify__power(a, bits);
  case Tag_Sum:
    return simplify__sum(a, bits);
  case Tag_Product:
    return simplify__product(a, bits);
  case Tag_Equals:
    return simplify__equals(a, bits);
  case Tag_NotEquals:
    return simplify__not_equals(a, bits);
  case Tag_LessThan:
    return simplify__less_than(a, bits);
  case Tag_GreaterThan:
    return simplify__greater_than(a, bits);
  default:
    return pack_undefined();
  }
}

uint64_t solver_Algebra_simplify(struct solver_Algebra *a, uint64_t expr) {
  return simplify(a, expr);
}
*/