#include <iostream>
#include <ctype.h>
#include <assert.h>
#include <unordered_map>

#include "Field.hh"
#include "Vector.hh"
#include "Symmetry.hh"
#include "SwitchTable.hh"
#include "PointConfiguration.hh"
#include "StrictStairCaseMatrixTrans.hh"
#include "Permutation.hh"
#include "IntegerSet.hh"

namespace topcom {
  Vector cycle_1234(const Vector& vec) {
    Vector result(5);
    result[0] = vec[1];
    result[1] = vec[2];
    result[2] = vec[3];
    result[3] = vec[0];
    result[4] = vec[4];
    return result;
  }
  Vector rotate(const Vector& vec) {
    Vector result(5);
    result[0] = -vec[1];
    result[1] = vec[0];
    result[2] = vec[2];
    result[3] = vec[3];
    result[4] = vec[4];
    return result;
  }
  Vector transpose_coords_12(const Vector& vec) {
    Vector result(4);
    result[0] = vec[1];
    result[1] = vec[0];
    result[2] = vec[2];
    result[3] = vec[3];
    return result;
  }
  Vector transpose_coords_23(const Vector& vec) {
    Vector result(4);
    result[0] = vec[0];
    result[1] = vec[2];
    result[2] = vec[1];
    result[3] = vec[3];
    return result;
  }
  Vector transpose_coords_34(const Vector& vec) {
    Vector result(4);
    result[0] = vec[0];
    result[1] = vec[1];
    result[2] = vec[3];
    result[3] = vec[2];
    return result;
  }
  Vector invert_coord_1(const Vector& vec) {
    Vector result(4);
    result[0] = -vec[0];
    result[1] = vec[1];
    result[2] = vec[2];
    result[3] = vec[3];
    return result;
  }
  Vector invert_coord_12(const Vector& vec) {
    Vector result(4);
    result[0] = -vec[0];
    result[1] = -vec[1];
    result[2] = vec[2];
    result[3] = vec[3];
    return result;
  }
  Vector invert_coord_123(const Vector& vec) {
    Vector result(4);
    result[0] = -vec[0];
    result[1] = -vec[1];
    result[2] = -vec[2];
    result[3] = vec[3];
    return result;
  }
  Vector invert_coord_1234(const Vector& vec) {
    Vector result(4);
    result[0] = -vec[0];
    result[1] = -vec[1];
    result[2] = -vec[2];
    result[3] = -vec[3];
    return result;
  }
};


int main(const int argc, const char** argv) {
  using namespace topcom;

  CommandlineOptions::init(argc, argv);

  PointConfiguration result;
  std::set<Vector> pointset;

  // we start with a unit vector in R^4 and its double:
  Vector rootpoint(4);
  rootpoint[0] = Field(FieldConstants::ONE);
  rootpoint[1] = Field(FieldConstants::ONE);
  rootpoint[2] = Field(FieldConstants::ZERO);
  rootpoint[3] = Field(FieldConstants::ZERO);
  result.augment(rootpoint);
  pointset.insert(rootpoint);
  
  PointConfiguration old_points = result;

  // next, compute the orbit w.r.t. all permutations
  // and inversions of coordinates:
  bool newpoint_found = true;
  std::set<Vector>::const_iterator finditer;
  while (newpoint_found) {
    newpoint_found = false;
    PointConfiguration new_points;
    Vector new_point(4);
    for (parameter_type pointidx = 0; pointidx < old_points.size(); ++pointidx) {
      new_point = transpose_coords_12(old_points[pointidx]);
      finditer = pointset.find(new_point);
      if (finditer == pointset.end()) {
       	newpoint_found = true;
       	new_points.augment(new_point);
       	pointset.insert(new_point);
      }
      new_point = transpose_coords_23(old_points[pointidx]);
      finditer = pointset.find(new_point);
      if (finditer == pointset.end()) {
       	newpoint_found = true;
       	new_points.augment(new_point);
       	pointset.insert(new_point);
      }
      new_point = transpose_coords_34(old_points[pointidx]);
      finditer = pointset.find(new_point);
      if (finditer == pointset.end()) {
       	newpoint_found = true;
       	new_points.augment(new_point);
       	pointset.insert(new_point);
      }
      new_point = invert_coord_1(old_points[pointidx]);
      finditer = pointset.find(new_point);
      if (finditer == pointset.end()) {
	newpoint_found = true;
	new_points.augment(new_point);
	pointset.insert(new_point);
      }
    }
    result.augment(new_points);
    old_points.swap(new_points);
    new_points.clear();
  }
  
  // now, we need to homogenize:
  result.homogenize();

  // compute all affine symmetries:
  SymmetryGroup symmetries(result, false);
  
  // required symmetries are generated by the cyclic permutation of coordinates:
  Symmetry required_sym(result.no());
  for (parameter_type j = 0; j < result.no(); ++j) {
    Vector mapped_point(rotate(result[j]));
    std::pair<bool, parameter_type> idxresult = result.index_of_point(mapped_point);
    required_sym[j] = idxresult.second;
  }
  SymmetryGroup required_syms(result.no());
  required_syms.extend(required_sym, false);

  // print results:
  MessageStreams::result() << "# vertices of the 24-cell in homogeneous coordinates" << std::endl;
  MessageStreams::result() << result << std::endl;

  MessageStreams::result() << "# switch table generators of the affine symmetries given by the Weyl group W(F4):" << std::endl;
  symmetries.write_sorted_generators(MessageStreams::result());
  MessageStreams::result() << std::endl;

  MessageStreams::result() << "# generators for required symmetries by cyclic permutation of coordinates:" << std::endl;
  required_syms.write_sorted_generators(MessageStreams::result());
  MessageStreams::result() << std::endl;

  return 0;
}

// eof kDn.cc
