// -*- C++ -*-
//==============================================================================================
//
//	This file is part of LiDIA --- a library for computational number theory
//
//	Copyright (c) 1994--2005 the LiDIA Group.  All rights reserved.
//
//	See http://www.informatik.tu-darmstadt.de/TI/LiDIA/
//
//----------------------------------------------------------------------------------------------
//
//	$Id$
//
//	Author	: Thomas Papanikolaou, Safuat Hamdy
//	Changes	: See CVS log
//
//==============================================================================================

#include <string>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cassert>
#include <cstdlib>

struct type_to_name {
  template<typename T>
  struct apply {
    static std::string const value;
  };
};

template<>
std::string const type_to_name::apply<char>::value = "char";
template<>
std::string const type_to_name::apply<signed char>::value = "signed char";
template<>
std::string const type_to_name::apply<unsigned char>::value = "unsigned char";

template<>
std::string const type_to_name::apply<short>::value = "short";
template<>
std::string const type_to_name::apply<unsigned short>::value = "unsigned short";

template<>
std::string const type_to_name::apply<int>::value = "int";
template<>
std::string const type_to_name::apply<unsigned int>::value = "unsigned int";

template<>
std::string const type_to_name::apply<long>::value = "long";
template<>
std::string const type_to_name::apply<unsigned long>::value = "unsigned long";


template<typename ResultT, typename IntT>
struct compute_bytes_to_int_flag {
  ResultT
  operator()(int idx) {
    assert(0 <= idx);
    assert(idx < sizeof(IntT));
    assert(sizeof(IntT) <= sizeof(ResultT));
    
    unsigned char bytes[sizeof(IntT)];
    for(int i = 0; i < sizeof(IntT); ++i) {
      bytes[i] = (i == idx) ? 0xff : 0x00;
    }
    
    return *reinterpret_cast<IntT*>(bytes);
  }
};


template<typename ValueT, typename IntT>
void
bytes_to_int_flag_specializations(std::ostream& os) {
  compute_bytes_to_int_flag<ValueT, IntT> generator;
  std::string const& type = type_to_name::apply<IntT>::value;

  for(int idx = 0; idx < sizeof(IntT); ++idx) {
    unsigned long value = generator(idx); 
    os << "  template<>\n"
       << "  struct bytes_to_int_flag<" << type << ", " << idx << "> {\n"
       << "    static " << type << " const value = " << "0x"
       << std::hex << std::setw(2*sizeof(IntT)) << std::setfill('0')
       << value << ";\n"
       << "  };\n"
       << "\n";
  }
}

void
header(std::ostream& os) {
  os << "// This file is automatically generated -- do not modify!\n"
     << "// Copyright (c) 2005 the LiDIA Group.\n"
     << "// See http://www.informatik.tu-darmstadt.de/TI/LiDIA/\n"
     << "\n";

  os << "#ifdef LIDIA_NAMESPACE\n"
     << "namespace LiDIA {\n"
     << "#endif\n"
     << "\n"
     << "\n";
}

void
footer(std::ostream& os) {
  os << "#ifdef LIDIA_NAMESPACE\n"
     << "} // namespace LiDIA\n"
     << "#endif\n"
     << "\n";
}


#define BYTES_TO_INT_FLAG(ValueT, IntT) \
bytes_to_int_flag_specializations<ValueT, IntT>(os);

void
write_bytes_to_int_flag(std::ostream& os) {
  os << "  /*************************************/\n"
     << "  /*  bytes_to_int_flag<inttype, idx>  */\n"
     << "  /*************************************/\n"
     << "\n";
  
  os << "  // Constants that describe the mapping of byte arrays to\n"
     << "  // integral types, used, e.g., in MPQS.\n"
     << "  // Let T be an integral type and c an unsigned char array of\n"
     << "  // length sizeof(T). Then\n"
     << "  //   bytes_to_int_flag<T, j>::value == *reinterpret_cast<T*>(c)\n"
     << "  // provided 0 <= j < sizeof(T), c[j] == 0xff, and\n"
     << "  // c[i] == 0x00 for all i != j.\n"
     << "\n";

  os << "  template<typename T, int idx>\n"
     << "  struct bytes_to_int_flag;\n"
     << "\n";

  BYTES_TO_INT_FLAG(unsigned char, char);
  BYTES_TO_INT_FLAG(unsigned char, signed char);
  BYTES_TO_INT_FLAG(unsigned char, unsigned char);
  BYTES_TO_INT_FLAG(unsigned short, short);
  BYTES_TO_INT_FLAG(unsigned short, unsigned short);
  BYTES_TO_INT_FLAG(unsigned int, int);
  BYTES_TO_INT_FLAG(unsigned int, unsigned int);
  BYTES_TO_INT_FLAG(unsigned long, long);
  BYTES_TO_INT_FLAG(unsigned long, unsigned long);

  os << "\n";
}


struct none_t {
};

template<typename T, typename tail = none_t>
struct type_node {
  typedef tail next;
  typedef T type;
};

typedef type_node<signed char,
                  type_node<short,
                            type_node<int,
                                      type_node<long> > > > int_list;

struct true_t {
  static bool const vlue = true;
};

struct false_t {
  static bool const vlue = false;
};


struct find_in_list {
  template<typename list, typename predicate, typename bool_t>
  struct aux {
    typedef typename list::type type;
  };

  template<typename list, typename predicate>
  struct apply {
    typedef typename list::type elem_type;
    typedef typename predicate::template apply<elem_type>::type pred_result;
    
    typedef typename aux<list, predicate, pred_result>::type type;
  };
};

template<typename list, typename predicate>
struct find_in_list::aux<list, predicate, false_t> {
  typedef typename list::next next;
  typedef typename apply<next, predicate>::type type;
};

template<typename predicate>
struct find_in_list::apply<none_t, predicate> {
  typedef none_t type;
};


struct is_zero {
  template<int n>
  struct apply {
    typedef false_t type;
  };
};

template<>
struct is_zero::apply<0> {
  typedef true_t type;
};


template<int size>
struct size_match {
  template<typename T>
  struct apply {
    typedef typename is_zero::apply<sizeof(T) - size>::type type;
  };
};

struct fitting_int_type {
  template<typename candidate_list, int size>
  struct apply {
    typedef size_match<size> pred;
    typedef typename find_in_list::apply<candidate_list, pred>::type type;
  };
};


template<typename IntT>
IntT
compute_mask(unsigned char bytemask) {
  IntT result = 0;
  for(int i = 0; i < sizeof(IntT); ++i) {
    result <<= 8;
    result |= bytemask;
  }
  return result;
}


template<typename int32_type>
void
write_int32(std::ostream& os, int32_type val, std::string const& name) {
  os << "    static " << type_to_name::apply<int32_type>::value << " const "
     << name << " = 0x" 
     << std::hex << std::setw(8) << std::setfill('0')
     << val << ";\n";
}

template<typename int32_type>
void
write_int32_consts(std::ostream& os) {
  compute_bytes_to_int_flag<int32_type, int32_type> generator;

  int32_type const octets_0_1_0xff = generator(0) | generator(1);
  int32_type const octets_2_3_0xff = generator(2) | generator(3);
  int32_type const octet_0_0xff = generator(0);
  int32_type const octet_1_0xff = generator(1);
  int32_type const octet_2_0xff = generator(2);
  int32_type const octet_3_0xff = generator(3);

  int32_type const mask_0x80 = compute_mask<int32_type>(0x80);


  os << "  /*********************/\n"
     << "  /*  int32 constants  */\n"
     << "  /*********************/\n"
     << "\n";

  os << "  struct int32_consts {\n";

  write_int32(os, octets_0_1_0xff, "octets_0_1_0xff");
  write_int32(os, octets_2_3_0xff, "octets_2_3_0xff");
  write_int32(os, octet_0_0xff, "octet_0_0xff");
  write_int32(os, octet_1_0xff, "octet_1_0xff");
  write_int32(os, octet_2_0xff, "octet_2_0xff");
  write_int32(os, octet_3_0xff, "octet_3_0xff");

  os << "\n";

  write_int32(os, octets_0_1_0xff & mask_0x80, "octets_0_1_0x80");
  write_int32(os, octets_2_3_0xff & mask_0x80, "octets_2_3_0x80");
  write_int32(os, octet_0_0xff & mask_0x80, "octet_0_0x80");
  write_int32(os, octet_1_0xff & mask_0x80, "octet_1_0x80");
  write_int32(os, octet_2_0xff & mask_0x80, "octet_2_0x80");
  write_int32(os, octet_3_0xff & mask_0x80, "octet_3_0x80");

  os << "  };\n"
     << "\n"
     << "\n";
}

template<>
void
write_int32_consts<none_t>(std::ostream& os) {
  os << "  // no 32 bit int type found\n"
     << "\n";
}


template<typename int64_type>
void
write_int64(std::ostream& os, int64_type val, std::string const& name) {
  os << "    static " << type_to_name::apply<int64_type>::value << " const "
     << name << " = 0x" 
     << std::hex << std::setw(16) << std::setfill('0')
     << val << ";\n";
}

template<typename int64_type>
void
write_int64_consts(std::ostream& os) {
  compute_bytes_to_int_flag<int64_type, int64_type> generator;

  int64_type const octets_0_1_2_3_0xff = (generator(0) | generator(1) |
                                           generator(2) | generator(3));
  int64_type const octets_4_5_6_7_0xff = (generator(4) | generator(5) |
                                           generator(6) | generator(7));
  int64_type const octets_0_1_0xff = generator(0) | generator(1);
  int64_type const octets_2_3_0xff = generator(2) | generator(3);
  int64_type const octets_4_5_0xff = generator(4) | generator(5);
  int64_type const octets_6_7_0xff = generator(6) | generator(7);
  int64_type const octet_0_0xff = generator(0);
  int64_type const octet_1_0xff = generator(1);
  int64_type const octet_2_0xff = generator(2);
  int64_type const octet_3_0xff = generator(3);
  int64_type const octet_4_0xff = generator(4);
  int64_type const octet_5_0xff = generator(5);
  int64_type const octet_6_0xff = generator(6);
  int64_type const octet_7_0xff = generator(7);

  int64_type const mask_0x80 = compute_mask<int64_type>(0x80);


  os << "  /*********************/\n"
     << "  /*  int64 constants  */\n"
     << "  /*********************/\n"
     << "\n";

  os << "  struct int64_consts {\n";

  write_int64(os, octets_0_1_2_3_0xff, "octets_0_1_2_3_0xff");
  write_int64(os, octets_4_5_6_7_0xff, "octets_4_5_6_7_0xff");
  write_int64(os, octets_0_1_0xff, "octets_0_1_0xff");
  write_int64(os, octets_2_3_0xff, "octets_2_3_0xff");
  write_int64(os, octets_4_5_0xff, "octets_4_5_0xff");
  write_int64(os, octets_6_7_0xff, "octets_6_7_0xff");
  write_int64(os, octet_0_0xff, "octet_0_0xff");
  write_int64(os, octet_1_0xff, "octet_1_0xff");
  write_int64(os, octet_2_0xff, "octet_2_0xff");
  write_int64(os, octet_3_0xff, "octet_3_0xff");
  write_int64(os, octet_4_0xff, "octet_4_0xff");
  write_int64(os, octet_5_0xff, "octet_5_0xff");
  write_int64(os, octet_6_0xff, "octet_6_0xff");
  write_int64(os, octet_7_0xff, "octet_7_0xff");

  os << "\n";

  write_int64(os, octets_0_1_2_3_0xff & mask_0x80, "octets_0_1_2_3_0x80");
  write_int64(os, octets_4_5_6_7_0xff & mask_0x80, "octets_4_5_6_7_0x80");
  write_int64(os, octets_0_1_0xff & mask_0x80, "octets_0_1_0x80");
  write_int64(os, octets_2_3_0xff & mask_0x80, "octets_2_3_0x80");
  write_int64(os, octets_4_5_0xff & mask_0x80, "octets_4_5_0x80");
  write_int64(os, octets_6_7_0xff & mask_0x80, "octets_6_7_0x80");
  write_int64(os, octet_0_0xff & mask_0x80, "octet_0_0x80");
  write_int64(os, octet_1_0xff & mask_0x80, "octet_1_0x80");
  write_int64(os, octet_2_0xff & mask_0x80, "octet_2_0x80");
  write_int64(os, octet_3_0xff & mask_0x80, "octet_3_0x80");
  write_int64(os, octet_4_0xff & mask_0x80, "octet_4_0x80");
  write_int64(os, octet_5_0xff & mask_0x80, "octet_5_0x80");
  write_int64(os, octet_6_0xff & mask_0x80, "octet_6_0x80");
  write_int64(os, octet_7_0xff & mask_0x80, "octet_7_0x80");

  os << "  };\n"
     << "\n"
     << "\n";
}

template<>
void
write_int64_consts<none_t>(std::ostream& os) {
  os << "  // no 64 bit int type found\n"
     << "\n";
}

int
main(int, char**) {
  typedef fitting_int_type::apply<int_list, 4>::type int32_type;
  typedef fitting_int_type::apply<int_list, 8>::type int64_type;

  std::ostream& os = std::cout;

  header(os);
  write_bytes_to_int_flag(os);
  write_int32_consts<int32_type>(os);
  write_int64_consts<int64_type>(os);
  footer(os);

  return os ? EXIT_SUCCESS : EXIT_FAILURE;
}
