void
  JLBasis::reconstruct_1(const Index& lambda,
			 const int j,
			 InfiniteVector<double, Index>& c) const {
    c.clear();
    
    if (lambda.j() >= j)
      c.set_coefficient(lambda, 1.0); 
    else {
#if _JL_PRECOND==0
      const double phi0factor = sqrt(35./26.);
      const double phi1factor = sqrt(105./2.);
#else
      const double phi0factor = sqrt(5./12.);
      const double phi1factor = sqrt(15./4.);
#endif
      if (lambda.e() == 0) {
 	// generator
	if (lambda.c() == 0) {
 	  // type phi_0
 	  // phi_0(x) = 1/2*phi_0(2*x+1)+phi_0(2*x)+1/2*phi_0(2*x-1)+3/4*phi_1(2*x+1)-3/4*phi_1(2*x-1)

	  int m = 2*lambda.k()-1; // m-2k=-1
	  { // phi_0(2x+1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(0.5*M_SQRT1_2, dhelp);
	  }
	  if (m >= 0) { // phi_1(2x+1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(0.75*M_SQRT1_2 * phi0factor/phi1factor, dhelp);
	  }

	  // m=2k <-> m-2k=0
	  m++;
	  { // phi_0(2x)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(M_SQRT1_2, dhelp);
	  }
	  
	  // m=2k+1 <-> m-2k=1
	  m++;
	  { // phi_0(2x-1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(0.5*M_SQRT1_2, dhelp);
	  }
	  if (m <= (1<<(lambda.j()+1))-1) { // phi_1(2x-1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(-0.75*M_SQRT1_2 * phi0factor/phi1factor, dhelp);
	  }
	} else { // lambda.c() == 1
  	  // type phi_1
 	  // phi_1(x) = -1/8*phi_0(2*x+1)+1/8*phi_0(2*x-1)-1/8*phi_1(2*x+1)+1/2*phi_1(2*x)-1/8*phi_1(2*x-1)
	  
	  int m = 2*lambda.k()-1; // m-2k=-1
	  { // phi_0(2x+1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(-0.125*M_SQRT1_2 * phi1factor/phi0factor, dhelp);
	  }
	  if (m >= 0) { // phi_1(2x+1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(-0.125*M_SQRT1_2, dhelp);
	  }

	  // m=2k <-> m-2k=0
	  m++;
	  { // phi_1(2x)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(0.5*M_SQRT1_2, dhelp);
	  }

	  // m=2k+1 <-> m-2k=1
	  m++;
	  { // phi_0(2x-1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(0.125*M_SQRT1_2 * phi1factor/phi0factor, dhelp);
	  }
	  if (m <= (1<<(lambda.j()+1))) { // phi_1(2x-1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(-0.125*M_SQRT1_2, dhelp);
	  }
	} // end if (lambda.c() == 0)
      } else { // lambda.e() == 1
	// wavelet
	if (lambda.c() == 0) {
 	  // type psi_0
 	  // psi_0(x) = -2*phi_0(2*x+1)+4*phi_0(2*x)-2*phi_0(2*x-1)-21*phi_1(2*x+1)+21*phi_1(2*x-1)
#if _JL_PRECOND==0
	  const double factor = sqrt(35./352.);
#else
	  const double factor = sqrt(5./3648.);
#endif

	  int m = 2*lambda.k()-1; // m-2k=-1
	  { // phi_0(2x+1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(-2.0*M_SQRT1_2 * factor/phi0factor, dhelp);
	  }
	  if (m >= 0) { // phi_1(2x+1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(-21.0*M_SQRT1_2 * factor/phi1factor, dhelp);
	  }

	  // m=2k <-> m-2k=0
	  m++;
	  { // phi_0(2x)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(4.0*M_SQRT1_2 * factor/phi0factor, dhelp);
	  }

	  // m=2k+1 <-> m-2k=1
	  m++;
	  { // phi_0(2x-1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(-2.0*M_SQRT1_2 * factor/phi0factor, dhelp);
	  }
	  if (m <= (1<<(lambda.j()+1))) { // phi_1(2x-1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(21.0*M_SQRT1_2 * factor/phi1factor, dhelp);
	  }
	} else { // lambda.c() == 1
 	  // type psi_1
 	  // psi_1(x) = phi_0(2*x+1)-phi_0(2*x-1)+ 9*phi_1(2*x+1)+12*phi_1(2*x)+ 9*phi_1(2*x-1)
#if _JL_PRECOND==0
	  const double factor = sqrt(35./48.);
#else
	  const double factor = sqrt(5./768.);
#endif
	  
	  int m = 2*lambda.k()-1; // m-2k=-1
	  { // phi_0(2x+1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(M_SQRT1_2 * factor/phi0factor, dhelp);
	  }
	  if (m >= 0) { // phi_1(2x+1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(9.0*M_SQRT1_2 * factor/phi1factor, dhelp);
	  }

	  // m=2k <-> m-2k=0
	  m++;
	  { // phi_1(2x)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(12.0*M_SQRT1_2 * factor/phi1factor, dhelp);
	  }

	  // m=2k+1 <-> m-2k=1
	  m++;
	  { // phi_0(2x-1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 0, m), j, dhelp);
	    c.add(-M_SQRT1_2 * factor/phi0factor, dhelp);
	  }
	  if (m <= (1<<(lambda.j()+1))) { // phi_1(2x-1)
	    InfiniteVector<double, Index> dhelp;
	    reconstruct_1(Index(lambda.j()+1, 0, 1, m), j, dhelp);
	    c.add(9.0*M_SQRT1_2 * factor/phi1factor, dhelp);
	  }
	}
      }
    }
  }
  Array1D<SampledMapping<2> >
  evaluate(const LDomainJLBasis& basis,
	   const Index& lambda,
	   const int N)
  {
    Array1D<SampledMapping<2> > r(3);

    // supp(psi_{j,e,c,k}) = 2^{-j}[k1-1,k1+1]x[k2-1,k2+1] cap Omega

    FixedArray1D<Array1D<double>,2> values;
    values[0].resize(N+1); // values in x-direction
    values[1].resize(N+1); // values in y-direction
    Array1D<double> knots;
    knots.resize(N+1);
    const double h = 1./N;
    
    // patch Omega_0 = [-1,0]x[0,1]
    for (int i = 0; i <= N; i++) {
      values[0][i] = 0.;
      values[1][i] = 0.;
    }
    if (lambda.k()[0] <= 0 && lambda.k()[1] >= 0) {
      for (int i = 0; i <= N; i++)
	knots[i] = -1.0+i*h;
      evaluate(0, lambda.j(), lambda.e()[0], lambda.c()[0], lambda.k()[0], knots, values[0]);
      for (int i = 0; i <= N; i++)
	knots[i] = i*h;
      evaluate(0, lambda.j(), lambda.e()[1], lambda.c()[1], lambda.k()[1], knots, values[1]);
    }
    r[0] = SampledMapping<2>(Point<2>(-1, 0), Point<2>(0,1), values);
    
    // patch Omega_1 = [-1,0]x[-1,0]
    for (int i = 0; i <= N; i++) {
      values[0][i] = 0.;
      values[1][i] = 0.;
    }
    if (lambda.k()[0] <= 0 && lambda.k()[1] <= 0) {
      for (int i = 0; i <= N; i++)
	knots[i] = -1.0+i*h;
      evaluate(0, lambda.j(), lambda.e()[0], lambda.c()[0], lambda.k()[0], knots, values[0]);
      evaluate(0, lambda.j(), lambda.e()[1], lambda.c()[1], lambda.k()[1], knots, values[1]);
    }
    r[1] = SampledMapping<2>(Point<2>(-1,-1), Point<2>(0,0), values);
    
    // patch Omega_2 = [0,1]x[-1,0]
    for (int i = 0; i <= N; i++) {
      values[0][i] = 0.;
      values[1][i] = 0.;
    }
    if (lambda.k()[0] >= 0 && lambda.k()[1] <= 0) {
      for (int i = 0; i <= N; i++)
	knots[i] = i*h;
      evaluate(0, lambda.j(), lambda.e()[0], lambda.c()[0], lambda.k()[0], knots, values[0]);
      for (int i = 0; i <= N; i++)
	knots[i] = -1.0+i*h;
      evaluate(0, lambda.j(), lambda.e()[1], lambda.c()[1], lambda.k()[1], knots, values[1]);
    }
    r[2] = SampledMapping<2>(Point<2>( 0,-1), Point<2>(1,0), values);
    
    return r;
  }
  double
  LDomainJLGramian::a(const Index& lambda,
		      const Index& mu) const
  {
    double r = 0;
    
    // First we compute the support intersection of \psi_\lambda and \psi_\mu:
    typedef WaveletBasis::Support Support;
    Support supp;
    
    if (intersect_supports(basis_, lambda, mu, supp))
      {
	// use that both \psi_\lambda and \psi_\mu are a tensor product of 1D bases;
	// the entry of the Gramian on each square subpatch is a product of 2 1D integrals
	
	// iterate through the subsquares of supp and compute the integral shares
 	const double h = ldexp(1.0, -supp.j); // sidelength of the subsquare
	const int N_Gauss = 6;
	FixedArray1D<Array1D<double>,2> gauss_points, gauss_weights;
	for (int i = 0; i <= 1; i++) {
	  gauss_points[i].resize(N_Gauss);
	  gauss_weights[i].resize(N_Gauss);
	  for (int ii = 0; ii < N_Gauss; ii++)
	    gauss_weights[i][ii] = h*GaussWeights[N_Gauss-1][ii];
	}
 	FixedArray1D<int,2> k;
 	FixedArray1D<Array1D<double>,2>
 	  psi_lambda_values,     // values of the components of psi_lambda at gauss_points[i]
 	  psi_mu_values;         // -"-, for psi_mu
	Array1D<double> dummy;
 	for (k[0] = supp.xmin; k[0] < supp.xmax; k[0]++) {
	  if (k[0] >= -(1<<supp.j) && k[0] < (1<<supp.j)) {
	    for (int ii = 0; ii < N_Gauss; ii++)
	      gauss_points[0][ii] = h*(2*k[0]+1+GaussPoints[N_Gauss-1][ii])/2.;
	    evaluate(lambda.j(), lambda.e()[0], lambda.c()[0], lambda.k()[0],
		     gauss_points[0], psi_lambda_values[0], dummy);
	    evaluate(mu.j(), mu.e()[0], mu.c()[0], mu.k()[0],
		     gauss_points[0], psi_mu_values[0], dummy);
	    double factor0 = 0;
	    for (int i0 = 0; i0 < N_Gauss; i0++)
	      factor0 += gauss_weights[0][i0] * psi_lambda_values[0][i0] * psi_mu_values[0][i0];
	    for (k[1] = supp.ymin; k[1] < supp.ymax; k[1]++) {
	      // check whether 2^{-supp.j}[k0,k0+1]x[k1,k1+1] is contained in Omega
	      if ((k[1] >= -(1<<supp.j) && k[1] < 0)
		  || (k[0] < 0 && k[1] >= 0 && k[1] < (1<<supp.j))) {
		for (int ii = 0; ii < N_Gauss; ii++)
		  gauss_points[1][ii] = h*(2*k[1]+1+GaussPoints[N_Gauss-1][ii])/2.;
		evaluate(lambda.j(), lambda.e()[1], lambda.c()[1], lambda.k()[1],
			 gauss_points[1], psi_lambda_values[1], dummy);
		evaluate(mu.j(), mu.e()[1], mu.c()[1], mu.k()[1],
			 gauss_points[1], psi_mu_values[1], dummy);
		double factor1 = 0;
		for (int i1 = 0; i1 < N_Gauss; i1++)
		  factor1 += gauss_weights[1][i1] * psi_lambda_values[1][i1] * psi_mu_values[1][i1];
		
		r += factor0 * factor1;
	      }
 	    }
	  }
	}
      }
    
    return r;
  }