static bool product_merge_single_term(struct solver_Algebra *a, uint64_t lhs, uint64_t rhs, uint64_t *merged) {
  if(is_number(lhs) && is_number(rhs)) {
    *merged = number_multiply(a, lhs, rhs);
    return true;
  }

  if(is_number(lhs) && lhs == LITERAL_ZERO) {
    *merged = LITERAL_ZERO;
    return true;
  }

  if(is_number(rhs) && rhs == LITERAL_ZERO) {
    *merged = LITERAL_ZERO;
    return true;
  }

  if(is_number(lhs) && lhs == LITERAL_ONE) {
    *merged = rhs;
    return true;
  }

  if(is_number(rhs) && rhs == LITERAL_ONE) {
    *merged = lhs;
    return true;
  }

  return false;
}

static uint64_t embed_product(struct solver_Algebra *a, uint32_t count, const uint64_t *values) {
  uint64_t merged;

  if(count == 0) return LITERAL_ZERO;
  if(count == 1) return values[0];
  if(count == 2) {
    if(product_merge_single_term(a, values[0], values[1], &merged)) {
      return merged;
    }
  }

  uint32_t new_count = 0;
  uint64_t * new_values = alias_stack_allocation(sizeof(*new_values) * count, alignof(*new_values));

  for(uint32_t i = 0; i < count; i++) {
    uint32_t k;
    for(k = 0; k < new_count; k++) {
      if(product_merge_single_term(a, new_values[k], values[i], &merged)) {
        if(merged == LITERAL_ZERO) return LITERAL_ZERO;
        new_values[k] = merged;
        break;
      }
    }
    if(k == new_count) {
      if(values[i] == LITERAL_ZERO) return LITERAL_ZERO;
      new_values[new_count++] = values[i];
    }
  }

  if(new_count == 0) return LITERAL_ZERO;
  if(new_count == 1) return new_values[0];
  if(new_count == 2) {
    if(product_merge_single_term(a, new_values[0], new_values[1], &merged)) {
      return merged;
    }
  }

  tabula_qsort(new_values, new_count, sizeof(*new_values), qsort_term_compare, a);  

  struct EncodedList product;
  product.count = new_count;
  product.index = embed_values(a, new_count, new_values);
  return pack_product(product);
}

uint64_t solver_Algebra_product(struct solver_Algebra *a, uint32_t count, const uint64_t *values) {
  return embed_product(a, count, values);
}