vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
     vector<int> sum(nums.size() + 1);
     sum[0] = 0;
     for(int i=0; i<nums.size(); i++)
         sum[i+1] = sum[i] + nums[i];
     
     vector<int> sum1(nums.size(), 0), sum2(nums.size(), 0), sum3(nums.size(), 0);
     vector<vector<int>> result1(nums.size()), result2(nums.size()), result3(nums.size());
     
     for(int i=k-1; i < (nums.size() - (2 * k)); i++) {
         if(sum1[i-1] < sum[i+1] - sum[i-k+1]) {
             sum1[i] = sum[i+1] - sum[i-k+1];
             result1[i].push_back(i-k+1);
         }
         else {
             sum1[i] = sum1[i-1];
             result1[i] = result1[i-1];
         }
         //sum1[i] = max(sum1[i-1], sum[i+1] - sum[i-k+1]);
     }
     for(int i=(2*k)-1; i < (nums.size() - k); i++) {
         if(sum2[i-1] < sum1[i-k] + sum[i+1] - sum[i-k+1]) {
             sum2[i] = sum1[i-k] + sum[i+1] - sum[i-k+1];
             result2[i] = result1[i-k];
             result2[i].push_back(i-k+1);
         }
         else {
             sum2[i] = sum2[i-1];
             result2[i] = result2[i-1];
         }
         //sum2[i] = max(sum2[i-1], sum1[i-k] + sum[i+1] - sum[i-k+1]);
     }
     for(int i=(3*k)-1; i < nums.size(); i++) {
         if(sum3[i-1] < sum2[i-k] + sum[i+1] - sum[i-k+1]) {
             sum3[i] = sum2[i-k] + sum[i+1] - sum[i-k+1];
             result3[i] = result2[i-k];
             result3[i].push_back(i-k+1);
         }
         else {
             sum3[i] = sum3[i-1];
             result3[i] = result3[i-1];
         }
         //sum3[i] = max(sum3[i-1], sum2[i-k] + sum[i+1] - sum[i-k+1]);
     }
     
     return result3[nums.size()-1];
 }
static void test_type2index_list()
{
    Tensor<float, 5> tensor(2,3,5,7,11);
    tensor.setRandom();
    tensor += tensor.constant(10.0f);

    typedef Eigen::IndexList<Eigen::type2index<0>> Dims0;
    typedef Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1>> Dims1;
    typedef Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1>, Eigen::type2index<2>> Dims2;
    typedef Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1>, Eigen::type2index<2>, Eigen::type2index<3>> Dims3;
    typedef Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1>, Eigen::type2index<2>, Eigen::type2index<3>, Eigen::type2index<4>> Dims4;

#if 0
    EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims0>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims1>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims2>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims3>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims4>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
#endif

    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims0, 1, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims1, 2, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims2, 3, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims3, 4, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims4, 5, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);

    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims0, 1, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims1, 2, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims2, 3, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims3, 4, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims4, 5, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);

    const Dims0 reduction_axis0;
    Tensor<float, 4> result0 = tensor.sum(reduction_axis0);
    for (int m = 0; m < 11; ++m) {
        for (int l = 0; l < 7; ++l) {
            for (int k = 0; k < 5; ++k) {
                for (int j = 0; j < 3; ++j) {
                    float expected = 0.0f;
                    for (int i = 0; i < 2; ++i) {
                        expected += tensor(i,j,k,l,m);
                    }
                    VERIFY_IS_APPROX(result0(j,k,l,m), expected);
                }
            }
        }
    }

    const Dims1 reduction_axis1;
    Tensor<float, 3> result1 = tensor.sum(reduction_axis1);
    for (int m = 0; m < 11; ++m) {
        for (int l = 0; l < 7; ++l) {
            for (int k = 0; k < 5; ++k) {
                float expected = 0.0f;
                for (int j = 0; j < 3; ++j) {
                    for (int i = 0; i < 2; ++i) {
                        expected += tensor(i,j,k,l,m);
                    }
                }
                VERIFY_IS_APPROX(result1(k,l,m), expected);
            }
        }
    }

    const Dims2 reduction_axis2;
    Tensor<float, 2> result2 = tensor.sum(reduction_axis2);
    for (int m = 0; m < 11; ++m) {
        for (int l = 0; l < 7; ++l) {
            float expected = 0.0f;
            for (int k = 0; k < 5; ++k) {
                for (int j = 0; j < 3; ++j) {
                    for (int i = 0; i < 2; ++i) {
                        expected += tensor(i,j,k,l,m);
                    }
                }
            }
            VERIFY_IS_APPROX(result2(l,m), expected);
        }
    }

    const Dims3 reduction_axis3;
    Tensor<float, 1> result3 = tensor.sum(reduction_axis3);
    for (int m = 0; m < 11; ++m) {
        float expected = 0.0f;
        for (int l = 0; l < 7; ++l) {
            for (int k = 0; k < 5; ++k) {
                for (int j = 0; j < 3; ++j) {
                    for (int i = 0; i < 2; ++i) {
                        expected += tensor(i,j,k,l,m);
                    }
                }
            }
        }
        VERIFY_IS_APPROX(result3(m), expected);
    }

    const Dims4 reduction_axis4;
    Tensor<float, 0> result4 = tensor.sum(reduction_axis4);
    float expected = 0.0f;
    for (int m = 0; m < 11; ++m) {
        for (int l = 0; l < 7; ++l) {
            for (int k = 0; k < 5; ++k) {
                for (int j = 0; j < 3; ++j) {
                    for (int i = 0; i < 2; ++i) {
                        expected += tensor(i,j,k,l,m);
                    }
                }
            }
        }
    }
    VERIFY_IS_APPROX(result4(), expected);
}
static void test_mixed_index_list()
{
    Tensor<float, 4> tensor(2,3,5,7);
    tensor.setRandom();

    int dim2 = 1;
    int dim4 = 3;

    auto reduction_axis = make_index_list(0, dim2, 2, dim4);

    VERIFY_IS_EQUAL(internal::array_get<0>(reduction_axis), 0);
    VERIFY_IS_EQUAL(internal::array_get<1>(reduction_axis), 1);
    VERIFY_IS_EQUAL(internal::array_get<2>(reduction_axis), 2);
    VERIFY_IS_EQUAL(internal::array_get<3>(reduction_axis), 3);
    VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[0]), 0);
    VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[1]), 1);
    VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[2]), 2);
    VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[3]), 3);

    typedef IndexList<type2index<0>, int, type2index<2>, int> ReductionIndices;
    ReductionIndices reduction_indices;
    reduction_indices.set(1, 1);
    reduction_indices.set(3, 3);
    EIGEN_STATIC_ASSERT((internal::array_get<0>(reduction_indices) == 0), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::array_get<2>(reduction_indices) == 2), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::index_known_statically<ReductionIndices>()(0) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::index_known_statically<ReductionIndices>()(2) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionIndices>()(0, 0) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionIndices>()(2, 2) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
#if 0
    EIGEN_STATIC_ASSERT((internal::all_indices_known_statically<ReductionIndices>()() == false), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<ReductionIndices>()() == false), YOU_MADE_A_PROGRAMMING_MISTAKE);
#endif

    typedef IndexList<type2index<0>, type2index<1>, type2index<2>, type2index<3>> ReductionList;
    ReductionList reduction_list;
    EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionList>()(0, 0) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionList>()(1, 1) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionList>()(2, 2) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionList>()(3, 3) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
#if 0
    EIGEN_STATIC_ASSERT((internal::all_indices_known_statically<ReductionList>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
    EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<ReductionList>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
#endif

    Tensor<float, 0> result1 = tensor.sum(reduction_axis);
    Tensor<float, 0> result2 = tensor.sum(reduction_indices);
    Tensor<float, 0> result3 = tensor.sum(reduction_list);

    float expected = 0.0f;
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 3; ++j) {
            for (int k = 0; k < 5; ++k) {
                for (int l = 0; l < 7; ++l) {
                    expected += tensor(i,j,k,l);
                }
            }
        }
    }
    VERIFY_IS_APPROX(result1(), expected);
    VERIFY_IS_APPROX(result2(), expected);
    VERIFY_IS_APPROX(result3(), expected);
}
int main(int argc, char* argv[])
{
  /*if (argc!=2) exit(0);
  double T = atof(argv[1]);
  printf(" T=%.3f\n",T);*/
  system("rm lambda.T*");
  
  GRID grid("params");
  int N= grid.get_N();
  Result result1(&grid);
  Result result2(&grid);
  Result result3(&grid);

 
  for (double T=0.30; T>0.05; T-=0.01)
  { for (double U=1.5; U<4.0; U+=0.1)
    { char FN[300];
      sprintf( FN, "CHM.U%.3f.T%.3f", U, T);
      char FN2[300];
      sprintf( FN2, "%s.FAILED", FN);
      if ((not FileExists(FN))and(not FileExists(FN2))) continue;

      char ldFN[300];
      sprintf(ldFN,"lambdas_and_diffs.U%.3f.T%.3f",U,T);
      FILE* ldFile = fopen(ldFN,"w");

      double lambdas[100]; 
      int counter=0;  
      for(int it=1; it<100; it++) 
      { char FN[300];
        sprintf( FN, "CHM.U%.3f.T%.3f.it%d", U, T, it);
        if(not result1.ReadFromFile(FN)) break;
        sprintf( FN, "CHM.U%.3f.T%.3f.it%d", U, T, it+1);
        if(not result2.ReadFromFile(FN)) break;
        sprintf( FN, "CHM.U%.3f.T%.3f.it%d", U, T, it+2);
        if(not result3.ReadFromFile(FN)) break;
      
        double sum = 0;
        for(int i=0; i<N; i++)
          sum +=   sqr( real(result2.G[i]-result1.G[i])) 
                 + sqr( imag(result2.G[i]-result1.G[i]));
        double diff = sqrt(sum)/(2.0*N);
        double simple_diff = abs( imag(result2.G[N/2]-result1.G[N/2]) );
        double lambda = CalcLambda(N, result1.G, result2.G, result3.G);
        fprintf(ldFile,"%d %.15le %.15le %.15le\n",it,diff,lambda, simple_diff);
        lambdas[it-1]=lambda;
        counter++;

        complex<double>* dG = new complex<double>[N];
        for(int i=0; i<N; i++)
          dG[i] = ((result2.G[i]-result1.G[i])/diff)/(2.0*((double) N));
        sprintf( FN, "dG.U%.3f.T%.3f.it%d", U, T, it+1);
        PrintFunc(FN,N,dG,result1.omega);
      }      
      fclose(ldFile);
      
      char lFN[300];
      sprintf(lFN,"lambda.T%.3f",T);
      FILE* lFile = fopen(lFN,"a");
      fprintf(lFile,"%.15le %.15le\n",U,FindLambda(counter,lambdas) );
      fclose(lFile);
    }
  } 
  return 0;
}
void test_recursive_variant()
{
    typedef boost::make_recursive_variant<
          int
        , std::vector<boost::recursive_variant_>
        >::type var1_t;

    std::vector<var1_t> vec1;
    vec1.push_back(3);
    vec1.push_back(5);
    vec1.push_back(vec1);
    vec1.push_back(7);

    var1_t var1(vec1);
    std::string result1( boost::apply_visitor( vector_printer(), var1 ) );

    std::cout << "result1: " << result1 << '\n';
    BOOST_CHECK(result1 == "( 3 5 ( 3 5 ) 7 ) ");

    typedef boost::make_recursive_variant<
          boost::variant<int, double>
        , std::vector<boost::recursive_variant_>
        >::type var2_t;

    std::vector<var2_t> vec2;
    vec2.push_back(boost::variant<int, double>(3));
    vec2.push_back(boost::variant<int, double>(3.5));
    vec2.push_back(vec2);
    vec2.push_back(boost::variant<int, double>(7));

    var2_t var2(vec2);
    std::string result2( boost::apply_visitor( vector_printer(), var2 ) );

    std::cout << "result2: " << result2 << '\n';
    BOOST_CHECK(result2 == "( 3 3.5 ( 3 3.5 ) 7 ) ");
    
    typedef boost::make_recursive_variant<
          int
        , std::vector<
              boost::variant<
                    double
                  , std::vector<boost::recursive_variant_>
                  >
              >
        >::type var3_t;

    typedef boost::variant<double, std::vector<var3_t> > var4_t;

    std::vector<var3_t> vec3;
    vec3.push_back(3);
    vec3.push_back(5);
    std::vector<var4_t> vec4;
    vec4.push_back(3.5);
    vec4.push_back(vec3);
    vec3.push_back(vec4);
    vec3.push_back(7);

    var4_t var4(vec3);
    std::string result3( boost::apply_visitor( vector_printer(), var4 ) );

    std::cout << "result2: " << result3 << '\n';
    BOOST_CHECK(result3 == "( 3 5 ( 3.5 ( 3 5 ) ) 7 ) ");

    typedef boost::make_recursive_variant<
          double,
          std::vector<var1_t>
        >::type var5_t;

    std::vector<var5_t> vec5;
    vec5.push_back(3.5);
    vec5.push_back(vec1);
    vec5.push_back(17.25);

    std::string result5( vector_printer()(vec5) );

    std::cout << "result5: " << result5 << '\n';
    BOOST_CHECK(result5 == "( 3.5 ( 3 5 ( 3 5 ) 7 ) 17.25 ) ");

    typedef boost::make_recursive_variant<
          int,
          std::map<int, boost::recursive_variant_>
        >::type var6_t;
    var6_t var6;
}