1 /*
2  *  Copyright (C) 2004-2021 Edward F. Valeev
3  *
4  *  This file is part of Libint.
5  *
6  *  Libint is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  Libint is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with Libint.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include <string>
22 #include <fstream>
23 #include <iface.h>
24 #include <default_params.h>
25 
26 using namespace std;
27 using namespace libint2;
28 
29 namespace {
30   const char mh_name[] = "libint2.h";
31   const char th_name[] = "libint2_types.h";
32   const char ph_name[] = "libint2_params.h";
33   const char ih_name[] = "libint2_iface.h";
34   const char ii_name[] = "libint2_iface_internal.h";
35   const char si_name[] = "libint2_static_init.cc";
36   const char sc_name[] = "libint2_static_cleanup.cc";
37   const char li_name[] = "libint2_init.cc";
38 
header_guard_open(std::ostream & os,const std::string & label)39   inline void header_guard_open(std::ostream& os, const std::string& label) {
40     os << "#ifndef _libint2_" << label << "_h_" << endl
41        << "#define _libint2_" << label << "_h_" << endl << endl;
42   }
header_guard_close(std::ostream & os)43   inline void header_guard_close(std::ostream& os) {
44     os << "#endif" << endl << endl;
45   }
46 
47 };
48 
Libint2Iface(const SafePtr<CompilationParameters> & cparams,const SafePtr<CodeContext> & ctext)49 Libint2Iface::Libint2Iface(const SafePtr<CompilationParameters>& cparams,
50                            const SafePtr<CodeContext>& ctext) :
51   null_str_(""), oss_(), cparams_(cparams), ctext_(ctext),
52   th_((cparams_->source_directory() + th_name).c_str()),
53   ph_((cparams_->source_directory() + ph_name).c_str()),
54   ih_((cparams_->source_directory() + ih_name).c_str()),
55   ii_((cparams_->source_directory() + ii_name).c_str()),
56   si_((cparams_->source_directory() + si_name).c_str()),
57   sc_((cparams_->source_directory() + sc_name).c_str()),
58   li_((cparams_->source_directory() + li_name).c_str())
59 {
60   th_ << ctext_->copyright();
61   ph_ << ctext_->copyright();
62   ih_ << ctext_->copyright();
63   ii_ << ctext_->copyright();
64   si_ << ctext_->copyright();
65   sc_ << ctext_->copyright();
66   li_ << ctext_->copyright();
67 
68   header_guard_open(th_,"libint2types");
69   header_guard_open(ph_,"libint2params");
70   header_guard_open(ih_,"libint2iface");
71   header_guard_open(ii_,"libint2ifaceint");
72 
73   ph_ << macro_define("API_PREFIX", cparams_->api_prefix());
74   ph_ << macro_define("MAX_VECLEN", cparams_->max_vector_length());
75   ph_ << macro_define("ALIGN_SIZE", cparams_->align_size());
76   if (cparams_->count_flops())
77     ph_ << macro_define("FLOP_COUNT",1);
78   if (cparams_->profile())
79     ph_ << macro_define("PROFILE",1);
80   if (cparams_->accumulate_targets())
81     ph_ << macro_define("ACCUM_INTS",1);
82   const std::string realtype(cparams_->realtype());
83   {
84     // does LIBINT_USER_DEFINED_REAL need extra include statements?
85 #ifdef LIBINT_USER_DEFINED_REAL_INCLUDES
86     ph_ << LIBINT_USER_DEFINED_REAL_INCLUDES << std::endl;
87 #endif
88     ph_ << macro_define("REALTYPE", realtype);
89   }
90   if (cparams_->contracted_targets())
91     ph_ << macro_define("CONTRACTED_INTS",1);
92 
93   ih_ << "#ifdef __cplusplus\n# include <cstddef>\n#else\n# include <stddef.h>\n#endif" << endl
94       << ctext_->code_prefix();
95 
96   oss_.str(null_str_);
97   oss_ << ctext_->std_header() << "#include <" << ih_name << ">" << endl
98                                << "#include <" << ii_name << ">" << endl
99                                << "#include <cstddef>" << endl
100                                << "#include <cassert>" << endl
101                                << "#include <cstdlib>" << endl
102                                << ctext_->code_prefix();
103   std::string pfix = oss_.str();
104   si_ << pfix;
105   sc_ << pfix;
106   li_ << "#include <libint2/util/memory.h>" << endl << pfix; // moved from libint2.h to here
107 
108   // print out declarations for the array of pointers to evaluator functions
109   LibraryTaskManager& taskmgr = LibraryTaskManager::Instance();
110   typedef LibraryTaskManager::TasksCIter tciter;
111   for(tciter t=taskmgr.first(); t!=taskmgr.plast(); ++t) {
112     const std::string& tlabel = t->label();
113     const unsigned int nbf = cparams_->num_bf(tlabel);
114 
115     ostringstream oss;
116     oss << "void (*" << ctext->label_to_name(cparams->api_prefix()) << "libint2_build_" << tlabel;
117     for(unsigned int c=0; c<nbf; ++c) {
118       const unsigned int lmax = const_cast<const CompilationParameters*>(cparams_.get())->max_am(tlabel, c);
119       oss << "[" << lmax+1 << "]";
120       std::cout << "task=" << tlabel << " center=" << c << " lmax=" << lmax << std::endl;
121     }
122     oss << ")(" << ctext_->const_modifier() << ctext_->inteval_type_name(tlabel) << "*);" << endl;
123     ih_ << "extern " << oss.str();
124     si_ << oss.str();
125   }
126 
127   // Declare library constructor/destructor
128   oss_.str(null_str_);
129   oss_ << ctext_->type_name<void>() << " "
130        << ctext_->label_to_name(cparams->api_prefix() + "libint2_static_init") << "()";
131   std::string si_fdec(oss_.str());
132 
133   oss_.str(null_str_);
134   oss_ << ctext_->type_name<void>() << " "
135        << ctext_->label_to_name(cparams->api_prefix() + "libint2_static_cleanup") << "()";
136   std::string sc_fdec(oss_.str());
137 
138   ih_ << si_fdec << ctext_->end_of_stat() << endl;
139   ih_ << sc_fdec << ctext_->end_of_stat() << endl;
140 
141   // Declare evaluator constructor/destructor (specific to the type of computation)
142   for(tciter t=taskmgr.first(); t!=taskmgr.plast(); ++t) {
143     const std::string& tlabel = t->label();
144 
145     oss_.str(null_str_);
146     oss_ << ctext_->type_name<void>() << " "
147 	     << ctext_->label_to_name(cparams->api_prefix() + "libint2_init_" + tlabel)
148 	     << "(" << ctext_->inteval_type_name(tlabel)
149 	     << "* inteval, int max_am, void* buf)";
150     std::string li_fdec(oss_.str());
151     li_decls_.push_back(li_fdec);
152 
153     oss_.str(null_str_);
154     oss_ << ctext_->type_name<size_t>() << " "
155 	     << ctext_->label_to_name(cparams->api_prefix() + "libint2_need_memory_" + tlabel)
156 	     << "(int max_am)";
157     std::string lm_fdec(oss_.str());
158     lm_decls_.push_back(lm_fdec);
159 
160     oss_.str(null_str_);
161     oss_ << ctext_->type_name<void>() << " "
162 	     << ctext_->label_to_name(cparams->api_prefix() + "libint2_cleanup_" + tlabel)
163 	     << "(" << ctext_->inteval_type_name(tlabel) << "* inteval)";
164     std::string lc_fdec(oss_.str());
165     lc_decls_.push_back(lc_fdec);
166 
167     ih_ << li_fdec << ctext_->end_of_stat() << endl;
168     ih_ << lm_fdec << ctext_->end_of_stat() << endl;
169     ih_ << lc_fdec << ctext_->end_of_stat() << endl;
170   }
171 
172   // if counting flops, need additional initializer function
173   if (cparams_->count_flops()) {
174     oss_.str(null_str_);
175     oss_ << "#ifdef __cplusplus\n#ifdef LIBINT2_FLOP_COUNT\nextern \"C++\" template <typename EvalType> void "
176        << ctext_->label_to_name(cparams->api_prefix() + "libint2_init_flopcounter")
177        << "(EvalType* inteval_vector, int inteval_vector_size)"
178        << ctext_->open_block();
179       // TODO convert to ForLoop object
180     oss_ << "for(int v=1; v!=inteval_vector_size; ++v)"
181         << ctext_->open_block()
182         << ctext_->assign("inteval_vector[v].nflops", "inteval_vector[0].nflops")
183         << ctext_->close_block()
184         << ctext_->close_block();
185     oss_ << "#endif\n#endif\n";
186 
187     lf_decl_ = oss_.str();
188     ih_ << lf_decl_ << ctext_->end_of_stat() << endl;
189   }
190 
191   ih_ << ctext_->code_postfix() << endl;
192 
193   si_ << si_fdec << ctext_->open_block();
194   sc_ << sc_fdec << ctext_->open_block();
195 
196 }
197 
~Libint2Iface()198 Libint2Iface::~Libint2Iface()
199 {
200   // For each task, print out defines for stack dimensions
201   LibraryTaskManager& taskmgr = LibraryTaskManager::Instance();
202   typedef LibraryTaskManager::TasksCIter tciter;
203   for(tciter t=taskmgr.first(); t!=taskmgr.plast(); ++t) {
204     SafePtr<TaskParameters> tparams = t->params();
205     const std::string& tlabel = t->label();
206     ph_ << macro_define(tlabel,"NUM_TARGETS",tparams->max_ntarget());
207     const unsigned int max_am = tparams->max_am();
208     for(unsigned int am=0; am<=max_am; ++am) {
209       { std::ostringstream oss; oss << "MAX_STACK_SIZE_" << am;
210         ph_ << macro_define(tlabel,oss.str(),tparams->max_stack_size(am)); }
211       { std::ostringstream oss; oss << "MAX_VECTOR_STACK_SIZE_" << am;
212         const unsigned int max_vector_stack_size = tparams->max_vector_stack_size(am);
213         ph_ << macro_define(tlabel,oss.str(),max_vector_stack_size); }
214       { std::ostringstream oss; oss << "MAX_HRR_HSRANK_" << am;
215         ph_ << macro_define(tlabel,oss.str(),tparams->max_hrr_hsrank(am)); }
216       { std::ostringstream oss; oss << "MAX_HRR_LSRANK_" << am;
217         ph_ << macro_define(tlabel,oss.str(),tparams->max_hrr_lsrank(am)); }
218     }
219   }
220 
221   // For each task, generate the evaluator type
222   th_ << "#include <libint2/util/vector.h>" << std::endl;
223   th_ << "#include <libint2/util/intrinsic_operations.h>" << std::endl;
224   th_ << "#include <libint2/util/timer.h>" << std::endl; // in case LIBINT2_PROFILE is on
225   generate_inteval_type(th_);
226 
227   // libint2_iface.h needs macros to help forming prefixed names in API
228   ih_ << ctext_->comment("Use LIBINT2_PREFIXED_NAME(fncname) to form properly prefixed function name from LIBINT2 API") << std::endl;
229   ih_ << "#define LIBINT2_PREFIXED_NAME(name) __libint2_prefixed_name__(LIBINT2_API_PREFIX,name)" << std::endl;
230   ih_ << "#define __libint2_prefixed_name__(prefix,name) __prescanned_prefixed_name__(prefix,name)" << std::endl;
231   ih_ << "#define __prescanned_prefixed_name__(prefix,name) prefix##name" << std::endl;
232   // also need macros to test what's in the evaluator
233   ih_ << ctext_->comment("Use LIBINT2_PREFIXED_NAME(fncname) to form properly prefixed function name from LIBINT2 API") << std::endl;
234   ih_ << "#define LIBINT2_DEFINED(taskname,symbol) __prescanned_libint2_defined__(taskname,symbol)" << std::endl;
235   if (cparams_->single_evaltype())
236     ih_ << "#define __prescanned_libint2_defined__(taskname,symbol) LIBINT2_DEFINED_##symbol" << std::endl << std::endl;
237   else
238     ih_ << "#define __prescanned_libint2_defined__(taskname,symbol) LIBINT2_DEFINED_##symbol##_##taskname" << std::endl << std::endl;
239 
240 
241 
242   header_guard_close(th_);
243   header_guard_close(ph_);
244   header_guard_close(ih_);
245   header_guard_close(ii_);
246 
247   std::ostringstream oss;
248   oss << ctext_->close_block() << ctext_->code_postfix() << endl << endl;
249   std::string pfix = oss.str();
250 
251   si_ << pfix;
252   sc_ << pfix;
253 
254   // Define Libint_t constructor/destructor (specific to the type of computation)
255   {
256     unsigned int i = 0;
257     for(tciter t=taskmgr.first(); t!=taskmgr.plast(); ++t,++i) {
258       const std::string& tlabel = t->label();
259 
260       li_ << lm_decls_[i] << ctext_->open_block();
261       const unsigned int max_am = t->params()->max_am();
262       for(unsigned int am=0; am<=max_am; ++am) {
263         std::string ss, vss, hsr, lsr;
264         { std::ostringstream oss;
265           oss << "MAX_STACK_SIZE_" << am; ss = oss.str(); }
266         { std::ostringstream oss;
267           oss << "MAX_VECTOR_STACK_SIZE_" << am; vss = oss.str(); }
268         { std::ostringstream oss;
269           oss << "MAX_HRR_HSRANK_" << am; hsr = oss.str(); }
270         { std::ostringstream oss;
271           oss << "MAX_HRR_LSRANK_" << am; lsr = oss.str(); }
272 
273         li_ << "assert(max_am <= " << max_am << ");" << std::endl;
274 
275         li_ << "if (max_am == " << am << ") return " << macro(tlabel,ss) << " * " << macro("MAX_VECLEN") << " + "
276             << macro(tlabel,vss) << " * " << macro("MAX_VECLEN") << " * ("
277             << macro(tlabel,hsr) << " > " << macro(tlabel,lsr) << " ? "
278             << macro(tlabel,hsr) << " : " << macro(tlabel,lsr) << ");" << std::endl;
279       }
280       li_ << "return 0; // unreachable" << std::endl;
281       li_ << ctext_->close_block();
282     }
283 
284     i = 0;
285     for(tciter t=taskmgr.first(); t!=taskmgr.plast(); ++t,++i) {
286       const std::string& tlabel = t->label();
287 
288       li_ << li_decls_[i] << ctext_->open_block();
289       li_ << "if (buf != 0) inteval->stack = reinterpret_cast<LIBINT2_REALTYPE*>(buf);" << std::endl << "else " << std::endl;
290       {
291         std::string tmp = ctext_->label_to_name(cparams_->api_prefix() + "libint2_need_memory_" + tlabel) + "(max_am)";
292 
293         // no posix_memalign? use new, with default alignment
294         li_ << ctext_->assign("inteval->stack",std::string("libint2::malloc<LIBINT2_REALTYPE>(") + tmp + std::string(")"));
295 
296       }
297 
298       const unsigned int max_am = t->params()->max_am();
299       for(unsigned int am=0; am<=max_am; ++am) {
300         std::string ss;
301         { std::ostringstream oss;
302         oss << "MAX_STACK_SIZE_" << am; ss = oss.str(); }
303 
304         li_ << "assert(max_am <= " << max_am << ");" << std::endl;
305         li_ << "if (max_am == " << am << ")" << std::endl;
306         std::string vstack_ptr("inteval->stack + ");
307         vstack_ptr += macro(tlabel,ss);
308         vstack_ptr += " * ";
309         vstack_ptr += macro("MAX_VECLEN");
310         li_ << ctext_->assign("inteval->vstack",vstack_ptr);
311       }
312 
313       if (cparams_->count_flops()) {
314         // allocate the counter and set it to zero
315         li_ << "inteval->nflops = new " << macro("UINT_LEAST64") << ";" << endl;
316         li_ << "inteval->nflops[0] = 0;" << endl;
317       }
318       if (cparams_->profile()) { // zero out the timers
319         li_ << ctext_->macro_if("LIBINT2_CPLUSPLUS_STD >= 2011");
320         li_ << "inteval->timers = new libint2::Timers<2>;" << endl;
321         li_ << "inteval->timers->clear();" << endl;
322         li_ << ctext_->macro_endif(); // >= C++11
323       }
324       li_ << ctext_->close_block();
325     }
326 
327     i = 0;
328     for(tciter t=taskmgr.first(); t!=taskmgr.plast(); ++t,++i) {
329       li_ << lc_decls_[i] << ctext_->open_block();
330 
331       li_ << "free(inteval->stack);\n";
332       li_ << ctext_->assign("inteval->stack","0");
333       li_ << ctext_->assign("inteval->vstack","0");
334       if (cparams_->count_flops()) {
335         // free the counter and set the pointer to zero
336         li_ << "delete inteval->nflops;" << endl;
337         li_ << "inteval->nflops = 0;" << endl;
338       }
339       if (cparams_->profile()) {
340         li_ << ctext_->macro_if("LIBINT2_CPLUSPLUS_STD >= 2011");
341         li_ << "delete inteval->timers;" << endl;
342         li_ << "inteval->timers = 0;" << endl;
343         li_ << ctext_->macro_endif(); // >= C++11
344       }
345       li_ << ctext_->close_block();
346     }
347   }
348 
349   li_ << ctext_->code_postfix() << std::endl << std::endl;
350 
351   th_.close();
352   ph_.close();
353   ih_.close();
354   ii_.close();
355   si_.close();
356   sc_.close();
357   li_.close();
358 }
359 
360 namespace libint2 {
361 
362   /// Parses the symbol if it is composed of a prefix followed by a number.
363   class Parser_prefixN {
364 
365     public:
Parser_prefixN(const std::string & symbol)366       Parser_prefixN(const std::string& symbol) :
367         match_(false), N_(0) {
368 
369         std::string N_str;
370         bool minus = false;
371 
372         auto prefix_rbegin = symbol.rbegin();
373         for(auto ri = symbol.rbegin(); ri != symbol.rend(); ++ri) {
374           if (isdigit(*ri)) {
375             match_ = true;
376             N_str.insert(0, 1, *ri);
377           }
378           else { // no more digits? check for a minus sign
379             prefix_rbegin = ri;
380             if (*ri == '_') {
381               std::string token("_");
382               ++ri;
383               for(int i=0; i<6 && ri != symbol.rend(); ++ri, ++i) {
384                 token.push_back(*ri);
385               }
386               if (token == "_sunim_") { // minus reversed
387                 minus = true;
388                 prefix_rbegin = ri;
389               }
390             }
391             break;
392           }
393         }
394 
395         if (match_) {
396 
397           N_ = atoi(N_str.c_str());
398           if (minus)
399             N_ *= -1;
400 
401           prefix_ = std::string(prefix_rbegin, symbol.rend());
402           std::reverse(prefix_.begin(), prefix_.end());
403         }
404       }
405 
406       /// returns true if the pattern matched
match() const407       bool match() const { return match_; }
408       /// returns N (only valid if match()==true)
N() const409       int N() const { return N_; }
410       /// returns prefix (only valid if match()==true)
prefix() const411       const std::string& prefix() const { return prefix_; }
412 
413     private:
414       bool match_;
415       std::string prefix_;
416       int N_;
417 
418   };
419 }
420 
421 void
generate_inteval_type(std::ostream & os)422 Libint2Iface::generate_inteval_type(std::ostream& os)
423 {
424   LibraryTaskManager& taskmgr = LibraryTaskManager::Instance();
425 
426   //
427   // If need to generate single type for all tasks, take a union of all symbols
428   // else process each task separately
429   //
430   typedef LibraryTaskManager::TasksCIter tciter;
431   const tciter tend = cparams_->single_evaltype() ? taskmgr.first()+1 : taskmgr.plast();
432   for(tciter t=taskmgr.first(); t!=tend; ++t) {
433     const SafePtr<TaskExternSymbols> tsymbols = t->symbols();
434 
435     // Prologue
436     os << "typedef struct {" << std::endl;
437 
438     //
439     // Declare external symbols
440     //
441     typedef TaskExternSymbols::SymbolList SymbolList;
442     std::string tlabel;
443     SymbolList symbols;
444     if (cparams_->single_evaltype()) {
445       TaskExternSymbols composite_symbols;
446       const tciter tend = taskmgr.plast();
447       for(tciter t=taskmgr.first(); t!=tend; ++t) {
448         const SafePtr<TaskExternSymbols> tsymbols = t->symbols();
449         composite_symbols.add(tsymbols->symbols());
450       }
451       symbols = composite_symbols.symbols();
452       tlabel = "";
453     }
454     else {
455       symbols = tsymbols->symbols();
456       tlabel = t->label();
457     }
458 
459     // Spend some effort to ensure reasonable ordering/grouping of symbols in the list, e.g.
460     // all symbols of form PrefixIndex (e.g. crazyprefixN) will be grouped together in the order of increasing Index.
461     SymbolList ordered_symbols;
462     // 1) convert all symbols to valid code
463     // 2) then scan for symbols matching pattern prefixN, for each prefix determine range of N
464     std::map<std::string,std::pair<int,int> > prefix_symbols;
465     for(auto s : symbols) {
466       Parser_prefixN parser(ctext_->label_to_name(s));
467       if (parser.match()) {
468         if (prefix_symbols.find(parser.prefix()) == prefix_symbols.end()) {
469           prefix_symbols[parser.prefix()] = std::make_pair(parser.N(), parser.N());
470         }
471         else {
472           const int N = parser.N();
473           auto Nlimits = prefix_symbols[parser.prefix()];
474           if (N < Nlimits.first)  Nlimits.first = N;
475           if (N > Nlimits.second) Nlimits.second = N;
476           prefix_symbols[parser.prefix()] = Nlimits;
477         }
478       }
479     }
480 
481     // 3) for each prefix iterate over the corresponding range, and add the matching symbols in the same order
482     for(auto pi=prefix_symbols.begin(); pi!=prefix_symbols.end(); ++pi) {
483       auto prefix = pi->first;
484       auto Nlimits = pi->second;
485       for(int N=Nlimits.first; N<=Nlimits.second; ++N) {
486         std::ostringstream oss; oss << prefix << (N<0 ? "-" : "") << abs(N);
487         std::string code_symbol = ctext_->label_to_name(oss.str());
488         for(auto v : symbols) {
489           if (code_symbol == ctext_->label_to_name(v)) {
490             ordered_symbols.push_back(code_symbol);
491           }
492         }
493       }
494     }
495     // 4) the rest of symbols can appear in any order
496     //    in practice, the order is lexicographic, hence xyz components are automatically ordered x,y,z
497     for(auto s : symbols) {
498       if (std::find(ordered_symbols.begin(), ordered_symbols.end(), ctext_->label_to_name(s)) == ordered_symbols.end())
499         ordered_symbols.push_back(s);
500     }
501 
502     // now dump all symbols into the evaluator data type definition
503     for(auto s=ordered_symbols.begin(); s!=ordered_symbols.end(); ++s) {
504       // for each extrnal symbol #define a macro
505       std::string tmplabel("DEFINED_"); tmplabel += ctext_->label_to_name(*s);
506       os << macro_define(tlabel,tmplabel,1);
507       // all symbols are doubles
508       os << var_declare_v<double>(*s);
509     }
510 
511     // Declare members common to all evaluators
512     os << ctext_->comment("Scratch buffer to hold intermediates") << std::endl;
513     os << ctext_->declare(ctext_->mutable_modifier() + ctext_->type_name<double*>(),std::string("stack"));
514 
515     os << ctext_->comment("Buffer to hold vector intermediates. Only used by set-level RR code if it is vectorized linewise") << std::endl;
516     os << ctext_->declare(ctext_->mutable_modifier() + ctext_->type_name<double*>(),std::string("vstack"));
517 
518     os << ctext_->comment("On completion, this contains pointers to computed targets") << std::endl;
519     if (cparams_->single_evaltype()) {
520       // figure out the maximum number of targets
521       unsigned int max_ntargets = 0;
522       const tciter tend = taskmgr.plast();
523       for(tciter t=taskmgr.first(); t!=tend; ++t) {
524         SafePtr<TaskParameters> tparams = t->params();
525         max_ntargets = std::max(max_ntargets,tparams->max_ntarget());
526       }
527       ostringstream oss;
528       oss << max_ntargets;
529       os << ctext_->declare_v(ctext_->mutable_modifier() + ctext_->type_name<double*>(),std::string("targets"),oss.str());
530     }
531     else {
532       os << ctext_->declare_v(ctext_->mutable_modifier() + ctext_->type_name<double*>(),std::string("targets"),macro(tlabel,"NUM_TARGETS"));
533     }
534 
535     os << ctext_->comment("Actual vector length. Not to exceed MAX_VECLEN! If MAX_VECLEN is 1 then veclen is not used") << std::endl;
536     os << ctext_->declare(ctext_->type_name<int>(),std::string("veclen"));
537 
538     os << ctext_->macro_if(macro("FLOP_COUNT"));
539     os << ctext_->comment("FLOP counter. Libint must be configured with --enable-flop-counter to allow FLOP counting. It is user's reponsibility to set zero nflops before computing integrals.") << std::endl;
540     os << ctext_->declare(ctext_->mutable_modifier() + macro("UINT_LEAST64*"),std::string("nflops"));
541     os << ctext_->macro_endif();
542 
543     os << ctext_->macro_if(macro("PROFILE"));
544     os << ctext_->macro_if("LIBINT2_CPLUSPLUS_STD >= 2011");
545     os << ctext_->comment("profiling timers. Libint must be configured with --enable-profile to allow profiling.") << std::endl;
546     os << "#ifdef __cplusplus" << std::endl
547        << ctext_->declare(ctext_->mutable_modifier() + "libint2::Timers<2>*",std::string("timers")) // 1 timer for HRR and 1 timer for VRR
548        << "#else // timers are not accessible from C" << std::endl
549        << "  void* timers;" << std::endl
550        << "#endif" << std::endl;
551     os << ctext_->macro_endif(); // >= C++11
552     os << ctext_->macro_endif();
553 
554     os << ctext_->macro_if(macro("ACCUM_INTS"));
555     os << ctext_->comment("If libint was configured with --enable-accum-ints then the target integrals are accumulated. To zero out the targets automatically before the computation, set this to nonzero.") << std::endl;
556     os << ctext_->declare(ctext_->type_name<int>(),std::string("zero_out_targets"));
557     os << ctext_->macro_endif();
558 
559     os << ctext_->macro_if(macro("CONTRACTED_INTS"));
560     os << ctext_->comment("If libint was configured with --enable-contracted-ints then contracted integrals are supported. Set this parameter to the total number of primitive combinations.") << std::endl;
561     os << ctext_->declare(ctext_->type_name<int>(),std::string("contrdepth"));
562     os << ctext_->macro_endif();
563 
564     // Epilogue
565     os << "} " << ctext_->inteval_type_name(tlabel) << ctext_->end_of_stat() << std::endl;
566 
567   }
568 
569   // If generating single evaluator type, create aliases from the specialized types to the actual type
570   if (cparams_->single_evaltype()) {
571     const tciter tend = taskmgr.plast();
572     for(tciter t=taskmgr.first(); t!=tend; ++t) {
573       const std::string& tlabel = t->label();
574       os << "typedef "
575 	 << ctext_->inteval_gen_type_name() << " "
576 	 << ctext_->inteval_spec_type_name(tlabel)
577 	 << ctext_->end_of_stat() << std::endl;
578     }
579   }
580 
581 }
582