]> git.donarmstrong.com Git - mothur.git/blob - primerdesigncommand.cpp
changes while testing
[mothur.git] / primerdesigncommand.cpp
1 //
2 //  primerdesigncommand.cpp
3 //  Mothur
4 //
5 //  Created by Sarah Westcott on 1/18/13.
6 //  Copyright (c) 2013 Schloss Lab. All rights reserved.
7 //
8
9 #include "primerdesigncommand.h"
10
11 //**********************************************************************************************************************
12 vector<string> PrimerDesignCommand::setParameters(){    
13         try {
14                 CommandParameter plabel("label", "String", "", "", "", "", "","",false,false); parameters.push_back(plabel);
15         CommandParameter plist("list", "InputTypes", "", "", "none", "none", "none","summary-list",false,true,true); parameters.push_back(plist);
16                 CommandParameter pfasta("fasta", "InputTypes", "", "", "none", "none", "none","",false,true, true); parameters.push_back(pfasta);
17                 CommandParameter pname("name", "InputTypes", "", "", "NameCount", "none", "none","",false,false,true); parameters.push_back(pname);
18         CommandParameter pcount("count", "InputTypes", "", "", "NameCount", "none", "none","",false,false,true); parameters.push_back(pcount);
19         CommandParameter plength("length", "Number", "", "18", "", "", "","",false,false); parameters.push_back(plength);
20         CommandParameter pmintm("mintm", "Number", "", "-1", "", "", "","",false,false); parameters.push_back(pmintm);
21         CommandParameter pmaxtm("maxtm", "Number", "", "-1", "", "", "","",false,false); parameters.push_back(pmaxtm);
22         CommandParameter pprocessors("processors", "Number", "", "1", "", "", "","",false,false); parameters.push_back(pprocessors);
23         CommandParameter potunumber("otunumber", "Number", "", "-1", "", "", "","",false,true,true); parameters.push_back(potunumber);
24         CommandParameter ppdiffs("pdiffs", "Number", "", "0", "", "", "","",false,false,true); parameters.push_back(ppdiffs);
25         CommandParameter pcutoff("cutoff", "Number", "", "100", "", "", "","",false,false); parameters.push_back(pcutoff);
26                 CommandParameter pinputdir("inputdir", "String", "", "", "", "", "","",false,false); parameters.push_back(pinputdir);
27                 CommandParameter poutputdir("outputdir", "String", "", "", "", "", "","",false,false); parameters.push_back(poutputdir);
28                 
29                 vector<string> myArray;
30                 for (int i = 0; i < parameters.size(); i++) {   myArray.push_back(parameters[i].name);          }
31                 return myArray;
32         }
33         catch(exception& e) {
34                 m->errorOut(e, "PrimerDesignCommand", "setParameters");
35                 exit(1);
36         }
37 }
38 //**********************************************************************************************************************
39 string PrimerDesignCommand::getHelpString(){    
40         try {
41                 string helpString = "";
42                 helpString += "The primer.design allows you to identify sequence fragments that are specific to particular OTUs.\n";
43                 helpString += "The primer.design command parameters are: list, fasta, name, count, otunumber, cutoff, length, pdiffs, mintm, maxtm, processors and label.\n";
44                 helpString += "The list parameter allows you to provide a list file and is required.\n";
45         helpString += "The fasta parameter allows you to provide a fasta file and is required.\n";
46         helpString += "The name parameter allows you to provide a name file associated with your fasta file.\n";
47         helpString += "The count parameter allows you to provide a count file associated with your fasta file.\n";
48         helpString += "The label parameter is used to indicate the label you want to use from your list file.\n";
49         helpString += "The otunumber parameter is used to indicate the otu you want to use from your list file. It is required.\n";
50         helpString += "The pdiffs parameter is used to specify the number of differences allowed in the primer. The default is 0.\n";
51         helpString += "The length parameter is used to indicate the length of the primer. The default is 18.\n";
52         helpString += "The mintm parameter is used to indicate minimum melting temperature.\n";
53         helpString += "The maxtm parameter is used to indicate maximum melting temperature.\n";
54         helpString += "The processors parameter allows you to indicate the number of processors you want to use. Default=1.\n";
55         helpString += "The cutoff parameter allows you set a percentage of sequences that support the base. For example: cutoff=97 would only return a sequence that only showed ambiguities for bases that were not supported by at least 97% of sequences.\n";
56                 helpString += "The primer.desing command should be in the following format: primer.design(list=yourListFile, fasta=yourFastaFile, name=yourNameFile)\n";
57                 helpString += "primer.design(list=final.an.list, fasta=final.fasta, name=final.names, label=0.03)\n";
58                 return helpString;
59         }
60         catch(exception& e) {
61                 m->errorOut(e, "PrimerDesignCommand", "getHelpString");
62                 exit(1);
63         }
64 }
65 //**********************************************************************************************************************
66 string PrimerDesignCommand::getOutputPattern(string type) {
67     try {
68         string pattern = "";
69         
70         if (type == "fasta") {  pattern = "[filename],[distance],otu.cons.fasta"; } 
71         else if (type == "summary") {  pattern = "[filename],[distance],primer.summary"; }
72         else if (type == "list") {  pattern = "[filename],pick,[extension]"; }
73         else { m->mothurOut("[ERROR]: No definition for type " + type + " output pattern.\n"); m->control_pressed = true;  }
74         
75         return pattern;
76     }
77     catch(exception& e) {
78         m->errorOut(e, "PrimerDesignCommand", "getOutputPattern");
79         exit(1);
80     }
81 }
82 //**********************************************************************************************************************
83 PrimerDesignCommand::PrimerDesignCommand(){     
84         try {
85                 abort = true; calledHelp = true;
86                 setParameters();
87         vector<string> tempOutNames;
88                 outputTypes["summary"] = tempOutNames; 
89         outputTypes["fasta"] = tempOutNames;
90         outputTypes["list"] = tempOutNames;
91         
92         }
93         catch(exception& e) {
94                 m->errorOut(e, "PrimerDesignCommand", "PrimerDesignCommand");
95                 exit(1);
96         }
97 }
98 //**********************************************************************************************************************
99 PrimerDesignCommand::PrimerDesignCommand(string option)  {
100         try {
101                 abort = false; calledHelp = false;   
102                 
103                 //allow user to run help
104                 if(option == "help") { help(); abort = true; calledHelp = true; }
105                 else if(option == "citation") { citation(); abort = true; calledHelp = true;}
106                 
107                 else {
108                         //valid paramters for this command
109                         vector<string> myArray = setParameters();
110                         
111                         OptionParser parser(option);
112                         map<string,string> parameters = parser.getParameters();
113                         
114                         ValidParameters validParameter;
115                         map<string,string>::iterator it;
116                         //check to make sure all parameters are valid for command
117                         for (it = parameters.begin(); it != parameters.end(); it++) { 
118                                 if (validParameter.isValidParameter(it->first, myArray, it->second) != true) {  abort = true;  }
119                         }
120                         
121             vector<string> tempOutNames;
122             outputTypes["summary"] = tempOutNames; 
123             outputTypes["fasta"] = tempOutNames;
124             outputTypes["list"] = tempOutNames;
125                         
126                         //if the user changes the input directory command factory will send this info to us in the output parameter 
127                         string inputDir = validParameter.validFile(parameters, "inputdir", false);              
128                         if (inputDir == "not found"){   inputDir = "";          }
129                         else {
130                                 string path;
131                                 it = parameters.find("count");
132                                 //user has given a template file
133                                 if(it != parameters.end()){ 
134                                         path = m->hasPath(it->second);
135                                         //if the user has not given a path then, add inputdir. else leave path alone.
136                                         if (path == "") {       parameters["count"] = inputDir + it->second;            }
137                                 }
138                                 
139                                 it = parameters.find("fasta");
140                                 //user has given a template file
141                                 if(it != parameters.end()){ 
142                                         path = m->hasPath(it->second);
143                                         //if the user has not given a path then, add inputdir. else leave path alone.
144                                         if (path == "") {       parameters["fasta"] = inputDir + it->second;            }
145                                 }
146                 
147                                 it = parameters.find("name");
148                                 //user has given a template file
149                                 if(it != parameters.end()){ 
150                                         path = m->hasPath(it->second);
151                                         //if the user has not given a path then, add inputdir. else leave path alone.
152                                         if (path == "") {       parameters["name"] = inputDir + it->second;             }
153                                 }
154                 
155                 it = parameters.find("list");
156                                 //user has given a template file
157                                 if(it != parameters.end()){ 
158                                         path = m->hasPath(it->second);
159                                         //if the user has not given a path then, add inputdir. else leave path alone.
160                                         if (path == "") {       parameters["list"] = inputDir + it->second;             }
161                                 }
162             }
163                         
164                         //check for parameters
165                         namefile = validParameter.validFile(parameters, "name", true);
166                         if (namefile == "not open") { abort = true; }   
167                         else if (namefile == "not found") { namefile = ""; }
168                         else { m->setNameFile(namefile); }
169             
170             countfile = validParameter.validFile(parameters, "count", true);
171                         if (countfile == "not open") { countfile = ""; abort = true; }
172                         else if (countfile == "not found") { countfile = "";  } 
173                         else { m->setCountTableFile(countfile); }
174             
175             //get fastafile - it is required
176             fastafile = validParameter.validFile(parameters, "fasta", true);
177                         if (fastafile == "not open") { fastafile = ""; abort=true;  }
178                         else if (fastafile == "not found") {  
179                 fastafile = m->getFastaFile(); 
180                                 if (fastafile != "") { m->mothurOut("Using " + fastafile + " as input file for the fasta parameter."); m->mothurOutEndLine(); }
181                                 else {  m->mothurOut("You have no current fastafile and the fasta parameter is required."); m->mothurOutEndLine(); abort = true; }
182             }else  { m->setFastaFile(fastafile); }
183             
184             //get listfile - it is required
185             listfile = validParameter.validFile(parameters, "list", true);
186                         if (listfile == "not open") { listfile = ""; abort=true;  }
187                         else if (listfile == "not found") {  
188                 listfile = m->getListFile(); 
189                                 if (listfile != "") { m->mothurOut("Using " + listfile + " as input file for the list parameter."); m->mothurOutEndLine(); }
190                                 else {  m->mothurOut("You have no current listfile and the list parameter is required."); m->mothurOutEndLine(); abort = true; }
191             }else  { m->setListFile(listfile); }
192
193             
194                         if ((namefile != "") && (countfile != "")) {
195                 m->mothurOut("[ERROR]: you may only use one of the following: name or count."); m->mothurOutEndLine(); abort = true;
196             }
197                         
198             
199             //if the user changes the output directory command factory will send this info to us in the output parameter 
200                         outputDir = validParameter.validFile(parameters, "outputdir", false);           if (outputDir == "not found"){  
201                                 outputDir = m->hasPath(listfile); //if user entered a file with a path then preserve it 
202                         }
203             
204             string temp = validParameter.validFile(parameters, "cutoff", false);  if (temp == "not found") { temp = "100"; }
205                         m->mothurConvert(temp, cutoff); 
206             
207             temp = validParameter.validFile(parameters, "pdiffs", false);  if (temp == "not found") { temp = "0"; }
208                         m->mothurConvert(temp, pdiffs); 
209             
210             temp = validParameter.validFile(parameters, "length", false);  if (temp == "not found") { temp = "18"; }
211                         m->mothurConvert(temp, length); 
212             
213             temp = validParameter.validFile(parameters, "mintm", false);  if (temp == "not found") { temp = "-1"; }
214                         m->mothurConvert(temp, minTM); 
215             
216             temp = validParameter.validFile(parameters, "maxtm", false);  if (temp == "not found") { temp = "-1"; }
217                         m->mothurConvert(temp, maxTM); 
218             
219             temp = validParameter.validFile(parameters, "otunumber", false);  if (temp == "not found") { temp = "-1"; }
220                         m->mothurConvert(temp, otunumber); 
221             if (otunumber < 1) {  m->mothurOut("[ERROR]: You must provide an OTU number, aborting.\n"); abort = true; }
222             
223             temp = validParameter.validFile(parameters, "processors", false);   if (temp == "not found"){       temp = m->getProcessors();      }
224                         m->setProcessors(temp);
225                         m->mothurConvert(temp, processors);
226             
227             label = validParameter.validFile(parameters, "label", false);                       
228                         if (label == "not found") { label = ""; m->mothurOut("You did not provide a label, I will use the first label in your inputfile."); m->mothurOutEndLine(); label=""; }
229         
230             if (countfile == "") { 
231                 if (namefile == "") {
232                     vector<string> files; files.push_back(fastafile);
233                     parser.getNameFile(files);
234                 }
235             }
236                 }
237         }
238         catch(exception& e) {
239                 m->errorOut(e, "PrimerDesignCommand", "PrimerDesignCommand");
240                 exit(1);
241         }
242 }
243 //**********************************************************************************************************************
244 int PrimerDesignCommand::execute(){
245         try {
246                 
247                 if (abort == true) { if (calledHelp) { return 0; }  return 2;   }
248         
249         int start = time(NULL);
250         //////////////////////////////////////////////////////////////////////////////
251         //              get file inputs                                             //
252         //////////////////////////////////////////////////////////////////////////////
253         
254         //reads list file and selects the label the users specified or the first label
255         getListVector();
256         if (otunumber > list->getNumBins()) { m->mothurOut("[ERROR]: You selected an OTU number larger than the number of OTUs you have in your list file, quitting.\n"); return 0; }
257         
258         map<string, int> nameMap;
259         unsigned long int numSeqs;  //used to sanity check the files. numSeqs = total seqs for namefile and uniques for count.
260                                     //list file should have all seqs if namefile was used to create it and only uniques in count file was used.
261         
262         if (namefile != "")         {  nameMap = m->readNames(namefile, numSeqs);       }
263         else if (countfile != "")   {  nameMap = readCount(numSeqs);                    }
264         else { numSeqs = list->getNumSeqs();  }
265         
266         //sanity check
267         if (numSeqs != list->getNumSeqs()) {
268             if (namefile != "")         {  m->mothurOut("[ERROR]: Your list file contains " + toString(list->getNumSeqs()) + " sequences, and your name file contains " + toString(numSeqs) + " sequences, aborting. Do you have the correct files? Perhaps you forgot to include the name file when you clustered? \n");   }
269             else if (countfile != "") {
270                 m->mothurOut("[ERROR]: Your list file contains " + toString(list->getNumSeqs()) + " sequences, and your count file contains " + toString(numSeqs) + " unique sequences, aborting. Do you have the correct files? Perhaps you forgot to include the count file when you clustered? \n");  
271             }
272             m->control_pressed = true;
273         }
274         
275         if (m->control_pressed) { delete list; return 0; }
276         
277         //////////////////////////////////////////////////////////////////////////////
278         //              process data                                                //
279         //////////////////////////////////////////////////////////////////////////////
280         m->mothurOut("\nFinding consensus sequences for each otu..."); cout.flush();
281         
282         vector<Sequence> conSeqs = createProcessesConSeqs(nameMap, numSeqs);
283         
284         map<string, string> variables; 
285         variables["[filename]"] = outputDir + m->getRootName(m->getSimpleName(listfile));
286         variables["[distance]"] = list->getLabel();
287         string consFastaFile = getOutputFileName("fasta", variables);
288         outputNames.push_back(consFastaFile); outputTypes["fasta"].push_back(consFastaFile);
289         ofstream out;
290         m->openOutputFile(consFastaFile, out);
291         for (int i = 0; i < conSeqs.size(); i++) {  conSeqs[i].printSequence(out);  }
292         out.close();
293         
294         m->mothurOut("Done.\n\n");
295         
296         set<string> primers = getPrimer(conSeqs[otunumber-1]);  
297         
298         if (m->control_pressed) { delete list; for (int i = 0; i < outputNames.size(); i++) {   m->mothurRemove(outputNames[i]); } return 0; }
299         
300         string consSummaryFile = getOutputFileName("summary", variables);
301         outputNames.push_back(consSummaryFile); outputTypes["summary"].push_back(consSummaryFile);
302         ofstream outSum;
303         m->openOutputFile(consSummaryFile, outSum);
304         
305         outSum << "PrimerOtu: " << otunumber << " Members: " << list->get(otunumber-1) << endl << "Primers\tminTm\tmaxTm" << endl;
306         
307         //find min and max melting points
308         vector<double> minTms;
309         vector<double> maxTms;
310         string primerString = "";
311         for (set<string>::iterator it = primers.begin(); it != primers.end();) {
312             
313             double minTm, maxTm;
314             findMeltingPoint(*it, minTm, maxTm);
315             if ((minTM == -1) && (maxTM == -1)) { //user did not set min or max Tm so save this primer
316                 minTms.push_back(minTm);
317                 maxTms.push_back(maxTm);
318                 outSum << *it << '\t' << minTm << '\t' << maxTm << endl;
319                 it++;
320             }else if ((minTM == -1) && (maxTm <= maxTM)){ //user set max and no min, keep if below max
321                 minTms.push_back(minTm);
322                 maxTms.push_back(maxTm);
323                 outSum << *it << '\t' << minTm << '\t' << maxTm << endl;
324                 it++;
325             }else if ((maxTM == -1) && (minTm >= minTM)){ //user set min and no max, keep if above min
326                 minTms.push_back(minTm);
327                 maxTms.push_back(maxTm);
328                 outSum << *it << '\t' << minTm << '\t' << maxTm << endl;
329                 it++;
330             }else if ((maxTm <= maxTM) && (minTm >= minTM)) { //keep if above min and below max
331                 minTms.push_back(minTm);
332                 maxTms.push_back(maxTm);
333                 outSum << *it << '\t' << minTm << '\t' << maxTm << endl;
334                 it++;
335             }else { primers.erase(it++);  } //erase because it didn't qualify
336         }
337         
338         outSum << "\nOTUNumber\tPrimer\tStart\tEnd\tLength\tMismatches\tminTm\tmaxTm\n";
339         outSum.close();
340         
341         //check each otu's conseq for each primer in otunumber
342         set<int> otuToRemove = createProcesses(consSummaryFile, minTms, maxTms, primers, conSeqs);
343         
344         if (m->control_pressed) { delete list; for (int i = 0; i < outputNames.size(); i++) {   m->mothurRemove(outputNames[i]); } return 0; }
345         
346         //print new list file
347         map<string, string> mvariables; 
348         mvariables["[filename]"] = outputDir + m->getRootName(m->getSimpleName(listfile));
349         mvariables["[extension]"] = m->getExtension(listfile);
350         string newListFile = getOutputFileName("list", mvariables);
351         outputNames.push_back(newListFile); outputTypes["list"].push_back(newListFile);
352         ofstream outList;
353         m->openOutputFile(newListFile, outList);
354         
355         outList << list->getLabel() << '\t' << (list->getNumBins()-otuToRemove.size()) << '\t';
356         for (int j = 0; j < list->getNumBins(); j++) {
357             if (m->control_pressed) { break; }
358             //good otus
359             if (otuToRemove.count(j) == 0) {  
360                 string bin = list->get(j);
361                 if (bin != "") {  outList << bin << '\t';  } 
362             }
363         }
364         outList << endl;
365         outList.close();
366         
367         if (m->control_pressed) { delete list; for (int i = 0; i < outputNames.size(); i++) {   m->mothurRemove(outputNames[i]); } return 0; }
368         
369         delete list;
370         
371         m->mothurOut("It took " + toString(time(NULL) - start) + " secs to process " + toString(list->getNumBins()) + " OTUs.\n");
372         
373         
374         //output files created by command
375                 m->mothurOutEndLine();
376                 m->mothurOut("Output File Names: "); m->mothurOutEndLine();
377                 for (int i = 0; i < outputNames.size(); i++) {  m->mothurOut(outputNames[i]); m->mothurOutEndLine();    }
378                 m->mothurOutEndLine();
379         
380         return 0;
381                 
382     }
383         catch(exception& e) {
384                 m->errorOut(e, "PrimerDesignCommand", "execute");
385                 exit(1);
386         }
387 }
388 //********************************************************************/
389 //used http://www.biophp.org/minitools/melting_temperature/ as a reference to substitute degenerate bases 
390 // in order to find the min and max Tm values.
391 //Tm =  64.9°C + 41°C x (number of G’s and C’s in the primer â€“ 16.4)/N
392
393 /* A = adenine
394  * C = cytosine
395  * G = guanine
396  * T = thymine
397  * R = G A (purine)
398  * Y = T C (pyrimidine)
399  * K = G T (keto)
400  * M = A C (amino)
401  * S = G C (strong bonds)
402  * W = A T (weak bonds)
403  * B = G T C (all but A)
404  * D = G A T (all but C)
405  * H = A C T (all but G)
406  * V = G C A (all but T)
407  * N = A G C T (any) */
408
409 int PrimerDesignCommand::findMeltingPoint(string primer, double& minTm, double& maxTm){
410     try {
411         string minTmprimer = primer;
412         string maxTmprimer = primer;
413         
414         //find minimum Tm string substituting for degenerate bases
415         for (int i = 0; i < minTmprimer.length(); i++) {
416             minTmprimer[i] = toupper(minTmprimer[i]);
417             
418             if (minTmprimer[i] == 'Y') { minTmprimer[i] = 'A'; }
419             else if (minTmprimer[i] == 'R') { minTmprimer[i] = 'A'; }
420             else if (minTmprimer[i] == 'W') { minTmprimer[i] = 'A'; }
421             else if (minTmprimer[i] == 'K') { minTmprimer[i] = 'A'; }
422             else if (minTmprimer[i] == 'M') { minTmprimer[i] = 'A'; }
423             else if (minTmprimer[i] == 'D') { minTmprimer[i] = 'A'; }
424             else if (minTmprimer[i] == 'V') { minTmprimer[i] = 'A'; }
425             else if (minTmprimer[i] == 'H') { minTmprimer[i] = 'A'; }
426             else if (minTmprimer[i] == 'B') { minTmprimer[i] = 'A'; }
427             else if (minTmprimer[i] == 'N') { minTmprimer[i] = 'A'; }
428             else if (minTmprimer[i] == 'S') { minTmprimer[i] = 'G'; }
429         }
430         
431         //find maximum Tm string substituting for degenerate bases
432         for (int i = 0; i < maxTmprimer.length(); i++) {
433             maxTmprimer[i] = toupper(maxTmprimer[i]);
434             
435             if (maxTmprimer[i] == 'Y') { maxTmprimer[i] = 'G'; }
436             else if (maxTmprimer[i] == 'R') { maxTmprimer[i] = 'G'; }
437             else if (maxTmprimer[i] == 'W') { maxTmprimer[i] = 'A'; }
438             else if (maxTmprimer[i] == 'K') { maxTmprimer[i] = 'G'; }
439             else if (maxTmprimer[i] == 'M') { maxTmprimer[i] = 'G'; }
440             else if (maxTmprimer[i] == 'D') { maxTmprimer[i] = 'G'; }
441             else if (maxTmprimer[i] == 'V') { maxTmprimer[i] = 'G'; }
442             else if (maxTmprimer[i] == 'H') { maxTmprimer[i] = 'G'; }
443             else if (maxTmprimer[i] == 'B') { maxTmprimer[i] = 'G'; }
444             else if (maxTmprimer[i] == 'N') { maxTmprimer[i] = 'G'; }
445             else if (maxTmprimer[i] == 'S') { maxTmprimer[i] = 'G'; }
446         }
447         
448         int numGC = 0;
449         for (int i = 0; i < minTmprimer.length(); i++) {
450             if (minTmprimer[i] == 'G')       { numGC++; }
451             else if (minTmprimer[i] == 'C')  { numGC++; }
452         }
453         
454         minTm = 64.9 + 41 * (numGC - 16.4) / (double) minTmprimer.length();
455         
456         numGC = 0;
457         for (int i = 0; i < maxTmprimer.length(); i++) {
458             if (maxTmprimer[i] == 'G')       { numGC++; }
459             else if (maxTmprimer[i] == 'C')  { numGC++; }
460         }
461         
462         maxTm = 64.9 + 41 * (numGC - 16.4) / (double) maxTmprimer.length();
463         
464         return 0;
465     }
466         catch(exception& e) {
467                 m->errorOut(e, "PrimerDesignCommand", "findMeltingPoint");
468                 exit(1);
469         }
470 }
471 //********************************************************************/
472 //search for a primer over the sequence string
473 bool PrimerDesignCommand::findPrimer(string rawSequence, string primer, vector<int>& primerStart, vector<int>& primerEnd, vector<int>& mismatches){
474         try {
475         bool foundAtLeastOne = false;  //innocent til proven guilty
476         
477         //look for exact match
478         if(rawSequence.length() < primer.length()) {  return false;  }
479                         
480         //search for primer
481         for (int j = 0; j < rawSequence.length()-length; j++){
482             
483             if (m->control_pressed) {  return foundAtLeastOne; }
484             
485             string rawChunk = rawSequence.substr(j, length);
486             
487             int numDiff = countDiffs(primer, rawChunk);
488            
489             if(numDiff <= pdiffs){
490                 primerStart.push_back(j);
491                 primerEnd.push_back(j+length);
492                 mismatches.push_back(numDiff);
493                 foundAtLeastOne = true;
494             }
495         }
496                 
497                 return foundAtLeastOne;
498                 
499         }
500         catch(exception& e) {
501                 m->errorOut(e, "PrimerDesignCommand", "findPrimer");
502                 exit(1);
503         }
504 }
505 //********************************************************************/
506 //find all primers for the given sequence
507 set<string> PrimerDesignCommand::getPrimer(Sequence primerSeq){
508         try {
509         set<string> primers;
510         
511         string rawSequence = primerSeq.getUnaligned();
512         
513         for (int j = 0; j < rawSequence.length()-length; j++){
514             if (m->control_pressed) { break; }
515             
516             string primer = rawSequence.substr(j, length);
517             primers.insert(primer);
518         }
519         
520         return primers;
521         }
522         catch(exception& e) {
523                 m->errorOut(e, "PrimerDesignCommand", "getPrimer");
524                 exit(1);
525         }
526 }
527 /**************************************************************************************************/
528 set<int> PrimerDesignCommand::createProcesses(string newSummaryFile, vector<double>& minTms, vector<double>& maxTms, set<string>& primers, vector<Sequence>& conSeqs) {
529         try {
530                 
531                 vector<int> processIDS;
532                 int process = 1;
533         set<int> otusToRemove;
534         int numBinsProcessed = 0;
535                 
536                 //sanity check
537         int numBins = conSeqs.size();
538                 if (numBins < processors) { processors = numBins; }
539                 
540                 //divide the otus between the processors
541                 vector<linePair> lines;
542                 int numOtusPerProcessor = numBins / processors;
543                 for (int i = 0; i < processors; i++) {
544                         int startIndex =  i * numOtusPerProcessor;
545                         int endIndex = (i+1) * numOtusPerProcessor;
546                         if(i == (processors - 1)){      endIndex = numBins;     }
547                         lines.push_back(linePair(startIndex, endIndex));
548                 }
549                 
550 #if defined (__APPLE__) || (__MACH__) || (linux) || (__linux) || (__linux__) || (__unix__) || (__unix)          
551                 
552                 //loop through and create all the processes you want
553                 while (process != processors) {
554                         int pid = fork();
555                         
556                         if (pid > 0) {
557                                 processIDS.push_back(pid);  //create map from line number to pid so you can append files in correct order later
558                                 process++;
559                         }else if (pid == 0){
560                 //clear old file because we append in driver
561                 m->mothurRemove(newSummaryFile + toString(getpid()) + ".temp");
562                 
563                                 otusToRemove = driver(newSummaryFile + toString(getpid()) + ".temp", minTms, maxTms, primers, conSeqs, lines[process].start, lines[process].end, numBinsProcessed);
564                 
565                 string tempFile = toString(getpid()) + ".otus2Remove.temp";
566                 ofstream outTemp;
567                 m->openOutputFile(tempFile, outTemp);
568                 
569                 outTemp << numBinsProcessed << endl;
570                 outTemp << otusToRemove.size() << endl;
571                 for (set<int>::iterator it = otusToRemove.begin(); it != otusToRemove.end(); it++) { outTemp << *it << endl; }
572                 outTemp.close();
573                 
574                                 exit(0);
575                         }else { 
576                                 m->mothurOut("[ERROR]: unable to spawn the necessary processes."); m->mothurOutEndLine(); 
577                                 for (int i = 0; i < processIDS.size(); i++) { kill (processIDS[i], SIGINT); }
578                                 exit(0);
579                         }
580                 }
581                 
582                 //do my part
583                 otusToRemove = driver(newSummaryFile, minTms, maxTms, primers, conSeqs, lines[0].start, lines[0].end, numBinsProcessed);
584                 
585                 //force parent to wait until all the processes are done
586                 for (int i=0;i<processIDS.size();i++) { 
587                         int temp = processIDS[i];
588                         wait(&temp);
589                 }
590         
591         for (int i = 0; i < processIDS.size(); i++) {
592             string tempFile = toString(processIDS[i]) +  ".otus2Remove.temp";
593             ifstream intemp;
594             m->openInputFile(tempFile, intemp);
595             
596             int num;
597             intemp >> num; m->gobble(intemp);
598             if (num != (lines[i+1].end - lines[i+1].start)) { m->mothurOut("[ERROR]: process " + toString(processIDS[i]) + " did not complete processing all OTUs assigned to it, quitting.\n"); m->control_pressed = true; }
599             intemp >> num; m->gobble(intemp);
600             for (int k = 0; k < num; k++) {
601                 int otu;
602                 intemp >> otu; m->gobble(intemp);
603                 otusToRemove.insert(otu); 
604             }
605             intemp.close();
606             m->mothurRemove(tempFile);
607         }
608
609         
610     #else
611                 
612                 //////////////////////////////////////////////////////////////////////////////////////////////////////
613                 //Windows version shared memory, so be careful when passing variables through the primerDesignData struct. 
614                 //Above fork() will clone, so memory is separate, but that's not the case with windows, 
615                 //////////////////////////////////////////////////////////////////////////////////////////////////////
616                 
617                 vector<primerDesignData*> pDataArray; 
618                 DWORD   dwThreadIdArray[processors-1];
619                 HANDLE  hThreadArray[processors-1]; 
620                 
621                 //Create processor worker threads.
622                 for( int i=1; i<processors; i++ ){
623                         // Allocate memory for thread data.
624                         string extension = toString(i) + ".temp";
625                         m->mothurRemove(newSummaryFile+extension);
626             
627                         primerDesignData* tempPrimer = new primerDesignData((newSummaryFile+extension), m, lines[i].start, lines[i].end, minTms, maxTms, primers, conSeqs, pdiffs, otunumber, length, i);
628                         pDataArray.push_back(tempPrimer);
629                         processIDS.push_back(i);
630                         
631                         //MySeqSumThreadFunction is in header. It must be global or static to work with the threads.
632                         //default security attributes, thread function name, argument to thread function, use default creation flags, returns the thread identifier
633                         hThreadArray[i-1] = CreateThread(NULL, 0, MyPrimerThreadFunction, pDataArray[i-1], 0, &dwThreadIdArray[i-1]);   
634                 }
635                 
636         
637                 //using the main process as a worker saves time and memory
638                 otusToRemove = driver(newSummaryFile, minTms, maxTms, primers, conSeqs, lines[0].start, lines[0].end, numBinsProcessed);
639                 
640                 //Wait until all threads have terminated.
641                 WaitForMultipleObjects(processors-1, hThreadArray, TRUE, INFINITE);
642                 
643                 //Close all thread handles and free memory allocations.
644                 for(int i=0; i < pDataArray.size(); i++){
645             for (set<int>::iterator it = pDataArray[i]->otusToRemove.begin(); it != pDataArray[i]->otusToRemove.end(); it++) { 
646                                 otusToRemove.insert(*it);  
647                         }
648             int num = pDataArray[i]->numBinsProcessed;
649             if (num != (lines[processIDS[i]].end - lines[processIDS[i]].start)) { m->mothurOut("[ERROR]: process " + toString(processIDS[i]) + " did not complete processing all OTUs assigned to it, quitting.\n"); m->control_pressed = true; }
650                         CloseHandle(hThreadArray[i]);
651                         delete pDataArray[i];
652                 }
653                 
654 #endif          
655                 
656                 //append output files
657                 for(int i=0;i<processIDS.size();i++){
658                         m->appendFiles((newSummaryFile + toString(processIDS[i]) + ".temp"), newSummaryFile);
659                         m->mothurRemove((newSummaryFile + toString(processIDS[i]) + ".temp"));
660                 }
661                 
662                 return otusToRemove;    
663                 
664         }
665         catch(exception& e) {
666                 m->errorOut(e, "PrimerDesignCommand", "createProcesses");
667                 exit(1);
668         }
669 }
670 //**********************************************************************************************************************
671 set<int> PrimerDesignCommand::driver(string summaryFileName, vector<double>& minTms, vector<double>& maxTms, set<string>& primers, vector<Sequence>& conSeqs, int start, int end, int& numBinsProcessed){
672         try {
673         set<int> otuToRemove;
674         
675         ofstream outSum;
676         m->openOutputFileAppend(summaryFileName, outSum);
677         
678         for (int i = start; i < end; i++) {
679         
680             if (m->control_pressed) { break; }
681             
682             if (i != (otunumber-1)) {
683                 int primerIndex = 0;
684                 for (set<string>::iterator it = primers.begin(); it != primers.end(); it++) {
685                     vector<int> primerStarts;
686                     vector<int> primerEnds;
687                     vector<int> mismatches;
688                     
689                     bool found = findPrimer(conSeqs[i].getUnaligned(), (*it), primerStarts, primerEnds, mismatches);
690                     
691                     //if we found it report to the table
692                     if (found) {
693                         for (int j = 0; j < primerStarts.size(); j++) {
694                             outSum << (i+1) << '\t' << *it << '\t' << primerStarts[j] << '\t' << primerEnds[j] << '\t' << length << '\t' << mismatches[j] << '\t' << minTms[primerIndex] << '\t' << maxTms[primerIndex] << endl;
695                         }
696                         otuToRemove.insert(i);
697                     }
698                     primerIndex++;
699                 }
700             }
701             numBinsProcessed++;
702         }
703         outSum.close();
704         
705         
706         return otuToRemove;
707     }
708         catch(exception& e) {
709                 m->errorOut(e, "PrimerDesignCommand", "driver");
710                 exit(1);
711         }
712 }
713 /**************************************************************************************************/ 
714 vector< vector< vector<unsigned int> > > PrimerDesignCommand::driverGetCounts(map<string, int>& nameMap, unsigned long int& fastaCount, vector<unsigned int>& otuCounts, unsigned long long& start, unsigned long long& end){
715     try {
716         vector< vector< vector<unsigned int> > > counts;
717         map<string, int> seq2Bin;
718         alignedLength = 0;
719         
720         ifstream in;
721                 m->openInputFile(fastafile, in);
722         
723                 in.seekg(start);
724         
725                 bool done = false;
726                 fastaCount = 0;
727         
728                 while (!done) {
729             if (m->control_pressed) { in.close(); return counts; }
730             
731                         Sequence seq(in); m->gobble(in);
732             
733                         if (seq.getName() != "") {
734                 if (fastaCount == 0) { alignedLength = seq.getAligned().length(); initializeCounts(counts, alignedLength, seq2Bin, nameMap, otuCounts); }
735                 else if (alignedLength != seq.getAligned().length()) {
736                     m->mothurOut("[ERROR]: your sequences are not all the same length. primer.design requires sequences to be aligned."); m->mothurOutEndLine(); m->control_pressed = true; break;
737                 }
738                 
739                 int num = 1;
740                 map<string, int>::iterator itCount;
741                 if (namefile != "") { 
742                     itCount = nameMap.find(seq.getName());
743                     if (itCount == nameMap.end()) {  m->mothurOut("[ERROR]: " + seq.getName() + " is in your fasta file and not in your name file, aborting."); m->mothurOutEndLine(); m->control_pressed = true; break; }
744                     else { num = itCount->second; }
745                     fastaCount+=num;
746                 }else if (countfile != "") {
747                     itCount = nameMap.find(seq.getName());
748                     if (itCount == nameMap.end()) {  m->mothurOut("[ERROR]: " + seq.getName() + " is in your fasta file and not in your count file, aborting."); m->mothurOutEndLine(); m->control_pressed = true; break; }
749                     else { num = itCount->second; }
750                     fastaCount++;
751                 }else {
752                     fastaCount++;
753                 }
754                 
755                 //increment counts
756                 itCount = seq2Bin.find(seq.getName());
757                 if (itCount == seq2Bin.end()) {
758                     if ((namefile != "") || (countfile != "")) {
759                         m->mothurOut("[ERROR]: " + seq.getName() + " is in your fasta file and not in your list file, aborting. Perhaps you forgot to include your name or count file while clustering.\n"); m->mothurOutEndLine(); m->control_pressed = true; break;
760                     }else{
761                         m->mothurOut("[ERROR]: " + seq.getName() + " is in your fasta file and not in your list file, aborting."); m->mothurOutEndLine(); m->control_pressed = true; break;
762                     }
763                 }else {
764                     otuCounts[itCount->second] += num;
765                     string aligned = seq.getAligned();
766                     for (int i = 0; i < alignedLength; i++) {
767                         char base = toupper(aligned[i]);
768                         if (base == 'A') { counts[itCount->second][i][0]+=num; }
769                         else if (base == 'T') { counts[itCount->second][i][1]+=num; }
770                         else if (base == 'G') { counts[itCount->second][i][2]+=num; }
771                         else if (base == 'C') { counts[itCount->second][i][3]+=num; }
772                         else { counts[itCount->second][i][4]+=num; }
773                     }
774                 }
775
776             }
777
778 #if defined (__APPLE__) || (__MACH__) || (linux) || (__linux) || (__linux__) || (__unix__) || (__unix)
779             unsigned long long pos = in.tellg();
780             if ((pos == -1) || (pos >= end)) { break; }
781 #else
782             if (in.eof()) { break; }
783 #endif
784                 }
785                                 
786                 in.close();
787         
788         return counts;
789     }
790         catch(exception& e) {
791                 m->errorOut(e, "PrimerDesignCommand", "driverGetCounts");
792                 exit(1);
793         }
794 }
795 /**************************************************************************************************/
796 vector<Sequence> PrimerDesignCommand::createProcessesConSeqs(map<string, int>& nameMap, unsigned long int& numSeqs) {
797         try {
798         vector< vector< vector<unsigned int> > > counts;
799         vector<unsigned int> otuCounts;
800                 vector<int> processIDS;
801                 int process = 1;
802         unsigned long int fastaCount = 0;
803                 
804 #if defined (__APPLE__) || (__MACH__) || (linux) || (__linux) || (__linux__) || (__unix__) || (__unix)          
805                 
806         vector<unsigned long long> positions; 
807         vector<fastaLinePair> lines;
808         positions = m->divideFile(fastafile, processors);
809         for (int i = 0; i < (positions.size()-1); i++) {        lines.push_back(fastaLinePair(positions[i], positions[(i+1)])); }
810
811                 //loop through and create all the processes you want
812                 while (process != processors) {
813                         int pid = fork();
814                         
815                         if (pid > 0) {
816                                 processIDS.push_back(pid);  //create map from line number to pid so you can append files in correct order later
817                                 process++;
818                         }else if (pid == 0){
819                                 counts = driverGetCounts(nameMap, fastaCount, otuCounts, lines[process].start, lines[process].end);
820                 
821                 string tempFile = toString(getpid()) + ".cons_counts.temp";
822                 ofstream outTemp;
823                 m->openOutputFile(tempFile, outTemp);
824                 
825                 outTemp << fastaCount << endl;
826                 //pass counts
827                 outTemp << counts.size() << endl;
828                 for (int i = 0; i < counts.size(); i++) { 
829                     outTemp << counts[i].size() << endl;
830                     for (int j = 0; j < counts[i].size(); j++) { 
831                         for (int k = 0; k < 5; k++) {  outTemp << counts[i][j][k] << '\t'; }
832                         outTemp << endl;
833                     }
834                 }
835                 //pass otuCounts
836                 outTemp << otuCounts.size() << endl;
837                 for (int i = 0; i < otuCounts.size(); i++) { outTemp << otuCounts[i] << '\t'; }
838                 outTemp << endl;
839                 outTemp.close();
840                 
841                                 exit(0);
842                         }else { 
843                                 m->mothurOut("[ERROR]: unable to spawn the necessary processes."); m->mothurOutEndLine(); 
844                                 for (int i = 0; i < processIDS.size(); i++) { kill (processIDS[i], SIGINT); }
845                                 exit(0);
846                         }
847                 }
848                 
849                 //do my part
850                 counts = driverGetCounts(nameMap, fastaCount, otuCounts, lines[0].start, lines[0].end);
851                 
852                 //force parent to wait until all the processes are done
853                 for (int i=0;i<processIDS.size();i++) { 
854                         int temp = processIDS[i];
855                         wait(&temp);
856                 }
857         
858         for (int i = 0; i < processIDS.size(); i++) {
859             string tempFile = toString(processIDS[i]) +  ".cons_counts.temp";
860             ifstream intemp;
861             m->openInputFile(tempFile, intemp);
862             
863             unsigned long int num;
864             intemp >> num; m->gobble(intemp); fastaCount += num;
865             intemp >> num; m->gobble(intemp);
866             if (num != counts.size()) { m->mothurOut("[ERROR]: " + tempFile + " was not built correctly by the child process, quitting.\n"); m->control_pressed = true; }
867             else {
868                 //read counts
869                 for (int k = 0; k < num; k++) {
870                     int alength;
871                     intemp >> alength; m->gobble(intemp);
872                     if (alength != alignedLength) {  m->mothurOut("[ERROR]: your sequences are not all the same length. primer.design requires sequences to be aligned."); m->mothurOutEndLine(); m->control_pressed = true; }
873                     else {
874                         for (int j = 0; j < alength; j++) {
875                             for (int l = 0; l < 5; l++) {  unsigned int numTemp; intemp >> numTemp; m->gobble(intemp); counts[k][j][l] += numTemp;  }
876                         }
877                     }
878                 }
879                 //read otuCounts
880                 intemp >> num; m->gobble(intemp);
881                 for (int k = 0; k < num; k++) {  
882                     unsigned int numTemp; intemp >> numTemp; m->gobble(intemp); 
883                     otuCounts[k] += numTemp; 
884                 }
885             }
886             intemp.close();
887             m->mothurRemove(tempFile);
888         }
889         
890         
891 #else
892         unsigned long long start = 0;
893         unsigned long long end = 1000;
894                 counts = driverGetCounts(nameMap, fastaCount, otuCounts, start, end);   
895 #endif          
896         
897         //you will have a nameMap error if there is a namefile or countfile, but if those aren't given we want to make sure the fasta and list file match.
898         if (fastaCount != numSeqs) {
899             if ((namefile == "") && (countfile == ""))   {  m->mothurOut("[ERROR]: Your list file contains " + toString(list->getNumSeqs()) + " sequences, and your fasta file contains " + toString(fastaCount) + " sequences, aborting. Do you have the correct files? Perhaps you forgot to include the name or count file? \n");   }
900             m->control_pressed = true;
901         }
902         
903                 vector<Sequence> conSeqs;
904         
905         if (m->control_pressed) { return conSeqs; }
906         
907                 //build consensus seqs
908         string snumBins = toString(counts.size());
909         for (int i = 0; i < counts.size(); i++) {
910             if (m->control_pressed) { break; }
911             
912             string otuLabel = "Otu";
913             string sbinNumber = toString(i+1);
914             if (sbinNumber.length() < snumBins.length()) { 
915                 int diff = snumBins.length() - sbinNumber.length();
916                 for (int h = 0; h < diff; h++) { otuLabel += "0"; }
917             }
918             otuLabel += sbinNumber; 
919             
920             string cons = "";
921             for (int j = 0; j < counts[i].size(); j++) {
922                 cons += getBase(counts[i][j], otuCounts[i]);
923             }
924             Sequence consSeq(otuLabel, cons);
925             conSeqs.push_back(consSeq);
926         }
927         
928         if (m->control_pressed) { conSeqs.clear(); return conSeqs; }
929         
930         return conSeqs;
931         
932                 
933         }
934         catch(exception& e) {
935                 m->errorOut(e, "PrimerDesignCommand", "createProcessesConSeqs");
936                 exit(1);
937         }
938 }
939 //***************************************************************************************************************
940
941 char PrimerDesignCommand::getBase(vector<unsigned int> counts, int size){  //A,T,G,C,Gap
942         try{
943                 /* A = adenine
944          * C = cytosine
945          * G = guanine
946          * T = thymine
947          * R = G A (purine)
948          * Y = T C (pyrimidine)
949          * K = G T (keto)
950          * M = A C (amino)
951          * S = G C (strong bonds)
952          * W = A T (weak bonds)
953          * B = G T C (all but A)
954          * D = G A T (all but C)
955          * H = A C T (all but G)
956          * V = G C A (all but T)
957          * N = A G C T (any) */
958                 
959                 char conBase = 'N';
960                 
961                 //zero out counts that don't make the cutoff
962                 float percentage = (100.0 - cutoff) / 100.0;
963         
964                 for (int i = 0; i < counts.size(); i++) {
965             float countPercentage = counts[i] / (float) size;
966                         if (countPercentage < percentage) { counts[i] = 0; }
967                 }
968                 
969                 //any
970                 if ((counts[0] != 0) && (counts[1] != 0) && (counts[2] != 0) && (counts[3] != 0) && (counts[4] != 0)) {  conBase = 'n'; }
971                 //any no gap
972                 else if ((counts[0] != 0) && (counts[1] != 0) && (counts[2] != 0) && (counts[3] != 0) && (counts[4] == 0)) {  conBase = 'N'; }
973                 //all but T
974                 else if ((counts[0] != 0) && (counts[1] == 0) && (counts[2] != 0) && (counts[3] != 0) && (counts[4] != 0)) {  conBase = 'v'; }  
975                 //all but T no gap
976                 else if ((counts[0] != 0) && (counts[1] == 0) && (counts[2] != 0) && (counts[3] != 0) && (counts[4] == 0)) {  conBase = 'V'; }  
977                 //all but G
978                 else if ((counts[0] != 0) && (counts[1] != 0) && (counts[2] == 0) && (counts[3] != 0) && (counts[4] != 0)) {  conBase = 'h'; }  
979                 //all but G no gap
980                 else if ((counts[0] != 0) && (counts[1] != 0) && (counts[2] == 0) && (counts[3] != 0) && (counts[4] == 0)) {  conBase = 'H'; }  
981                 //all but C
982                 else if ((counts[0] != 0) && (counts[1] != 0) && (counts[2] != 0) && (counts[3] == 0) && (counts[4] != 0)) {  conBase = 'd'; }  
983                 //all but C no gap
984                 else if ((counts[0] != 0) && (counts[1] != 0) && (counts[2] != 0) && (counts[3] == 0) && (counts[4] == 0)) {  conBase = 'D'; }  
985                 //all but A
986                 else if ((counts[0] == 0) && (counts[1] != 0) && (counts[2] != 0) && (counts[3] != 0) && (counts[4] != 0)) {  conBase = 'b'; }  
987                 //all but A no gap
988                 else if ((counts[0] == 0) && (counts[1] != 0) && (counts[2] != 0) && (counts[3] != 0) && (counts[4] == 0)) {  conBase = 'B'; }  
989                 //W = A T (weak bonds)
990                 else if ((counts[0] != 0) && (counts[1] != 0) && (counts[2] == 0) && (counts[3] == 0) && (counts[4] != 0)) {  conBase = 'w'; }  
991                 //W = A T (weak bonds) no gap
992                 else if ((counts[0] != 0) && (counts[1] != 0) && (counts[2] == 0) && (counts[3] == 0) && (counts[4] == 0)) {  conBase = 'W'; }  
993                 //S = G C (strong bonds)
994                 else if ((counts[0] == 0) && (counts[1] == 0) && (counts[2] != 0) && (counts[3] != 0) && (counts[4] != 0)) {  conBase = 's'; }  
995                 //S = G C (strong bonds) no gap
996                 else if ((counts[0] == 0) && (counts[1] == 0) && (counts[2] != 0) && (counts[3] != 0) && (counts[4] == 0)) {  conBase = 'S'; }  
997                 //M = A C (amino)
998                 else if ((counts[0] != 0) && (counts[1] == 0) && (counts[2] == 0) && (counts[3] != 0) && (counts[4] != 0)) {  conBase = 'm'; }  
999                 //M = A C (amino) no gap
1000                 else if ((counts[0] != 0) && (counts[1] == 0) && (counts[2] == 0) && (counts[3] != 0) && (counts[4] == 0)) {  conBase = 'M'; }  
1001                 //K = G T (keto)
1002                 else if ((counts[0] == 0) && (counts[1] != 0) && (counts[2] != 0) && (counts[3] == 0) && (counts[4] != 0)) {  conBase = 'k'; }  
1003                 //K = G T (keto) no gap
1004                 else if ((counts[0] == 0) && (counts[1] != 0) && (counts[2] != 0) && (counts[3] == 0) && (counts[4] == 0)) {  conBase = 'K'; }  
1005                 //Y = T C (pyrimidine)
1006                 else if ((counts[0] == 0) && (counts[1] != 0) && (counts[2] == 0) && (counts[3] != 0) && (counts[4] != 0)) {  conBase = 'y'; }  
1007                 //Y = T C (pyrimidine) no gap
1008                 else if ((counts[0] == 0) && (counts[1] != 0) && (counts[2] == 0) && (counts[3] != 0) && (counts[4] == 0)) {  conBase = 'Y'; }  
1009                 //R = G A (purine)
1010                 else if ((counts[0] != 0) && (counts[1] == 0) && (counts[2] != 0) && (counts[3] == 0) && (counts[4] != 0)) {  conBase = 'r'; }  
1011                 //R = G A (purine) no gap
1012                 else if ((counts[0] != 0) && (counts[1] == 0) && (counts[2] != 0) && (counts[3] == 0) && (counts[4] == 0)) {  conBase = 'R'; }  
1013                 //only A
1014                 else if ((counts[0] != 0) && (counts[1] == 0) && (counts[2] == 0) && (counts[3] == 0) && (counts[4] != 0)) {  conBase = 'a'; }  
1015                 //only A no gap
1016                 else if ((counts[0] != 0) && (counts[1] == 0) && (counts[2] == 0) && (counts[3] == 0) && (counts[4] == 0)) {  conBase = 'A'; }  
1017                 //only T
1018                 else if ((counts[0] == 0) && (counts[1] != 0) && (counts[2] == 0) && (counts[3] == 0) && (counts[4] != 0)) {  conBase = 't'; }  
1019                 //only T no gap
1020                 else if ((counts[0] == 0) && (counts[1] != 0) && (counts[2] == 0) && (counts[3] == 0) && (counts[4] == 0)) {  conBase = 'T'; }  
1021                 //only G
1022                 else if ((counts[0] == 0) && (counts[1] == 0) && (counts[2] != 0) && (counts[3] == 0) && (counts[4] != 0)) {  conBase = 'g'; }  
1023                 //only G no gap
1024                 else if ((counts[0] == 0) && (counts[1] == 0) && (counts[2] != 0) && (counts[3] == 0) && (counts[4] == 0)) {  conBase = 'G'; }  
1025                 //only C
1026                 else if ((counts[0] == 0) && (counts[1] == 0) && (counts[2] == 0) && (counts[3] != 0) && (counts[4] != 0)) {  conBase = 'c'; }  
1027                 //only C no gap
1028                 else if ((counts[0] == 0) && (counts[1] == 0) && (counts[2] == 0) && (counts[3] != 0) && (counts[4] == 0)) {  conBase = 'C'; }  
1029                 //only gap
1030                 else if ((counts[0] == 0) && (counts[1] == 0) && (counts[2] == 0) && (counts[3] == 0) && (counts[4] != 0)) {  conBase = '-'; }
1031                 //cutoff removed all counts
1032                 else if ((counts[0] == 0) && (counts[1] == 0) && (counts[2] == 0) && (counts[3] == 0) && (counts[4] == 0)) {  conBase = 'N'; }
1033                 else{ m->mothurOut("[ERROR]: cannot find consensus base."); m->mothurOutEndLine(); }
1034                 
1035                 return conBase;
1036                 
1037         }
1038         catch(exception& e) {
1039                 m->errorOut(e, "PrimerDesignCommand", "getBase");
1040                 exit(1);
1041         }
1042 }
1043
1044 //**********************************************************************************************************************
1045 int PrimerDesignCommand::initializeCounts(vector< vector< vector<unsigned int> > >& counts, int length, map<string, int>& seq2Bin, map<string, int>& nameMap, vector<unsigned int>& otuCounts){
1046         try {
1047         counts.clear();
1048         otuCounts.clear();
1049         seq2Bin.clear();
1050         
1051         //vector< vector< vector<unsigned int> > > counts - otu < spot_in_alignment < counts_for_A,T,G,C,Gap > > >
1052         for (int i = 0; i < list->getNumBins(); i++) {
1053             string binNames = list->get(i);
1054             vector<string> names;
1055             m->splitAtComma(binNames, names);
1056             otuCounts.push_back(0);
1057             
1058             //lets be smart and only map the unique names if a name or count file was given to save search time and memory
1059             if ((namefile != "") || (countfile != "")) {
1060                 for (int j = 0; j < names.size(); j++) {
1061                     map<string, int>::iterator itNames = nameMap.find(names[j]);
1062                     if (itNames != nameMap.end()) { //add name because its a unique one
1063                         seq2Bin[names[j]] = i;
1064                     }
1065                 }
1066             }else { //map everyone
1067                 for (int j = 0; j < names.size(); j++) { seq2Bin[names[j]] = i;  }
1068             }
1069             
1070             vector<unsigned int> temp; temp.resize(5, 0); //A,T,G,C,Gap
1071             vector< vector<unsigned int> > temp2;
1072             for (int j = 0; j < length; j++) {
1073                 temp2.push_back(temp);
1074             }
1075             counts.push_back(temp2);
1076         }
1077         
1078         return 0;
1079     }
1080         catch(exception& e) {
1081                 m->errorOut(e, "PrimerDesignCommand", "initializeCounts");
1082                 exit(1);
1083         }
1084 }
1085 //**********************************************************************************************************************
1086 map<string, int> PrimerDesignCommand::readCount(unsigned long int& numSeqs){
1087         try {
1088         map<string, int> nameMap;
1089         
1090         CountTable ct;
1091         ct.readTable(countfile, false);
1092         vector<string> namesOfSeqs = ct.getNamesOfSeqs();
1093         numSeqs = ct.getNumUniqueSeqs();
1094         
1095         for (int i = 0; i < namesOfSeqs.size(); i++) {
1096             if (m->control_pressed) { break; }
1097             
1098             nameMap[namesOfSeqs[i]] = ct.getNumSeqs(namesOfSeqs[i]);
1099         }
1100         
1101         return nameMap;
1102     }
1103         catch(exception& e) {
1104                 m->errorOut(e, "PrimerDesignCommand", "readCount");
1105                 exit(1);
1106         }
1107 }
1108 //**********************************************************************************************************************
1109 int PrimerDesignCommand::getListVector(){
1110         try {
1111                 InputData input(listfile, "list");
1112                 list = input.getListVector();
1113                 string lastLabel = list->getLabel();
1114                 
1115                 if (label == "") { label = lastLabel;  return 0; }
1116                 
1117                 //if the users enters label "0.06" and there is no "0.06" in their file use the next lowest label.
1118                 set<string> labels; labels.insert(label);
1119                 set<string> processedLabels;
1120                 set<string> userLabels = labels;
1121                 
1122                 //as long as you are not at the end of the file or done wih the lines you want
1123                 while((list != NULL) && (userLabels.size() != 0)) {
1124                         if (m->control_pressed) {  return 0;  }
1125                         
1126                         if(labels.count(list->getLabel()) == 1){
1127                                 processedLabels.insert(list->getLabel());
1128                                 userLabels.erase(list->getLabel());
1129                                 break;
1130                         }
1131                         
1132                         if ((m->anyLabelsToProcess(list->getLabel(), userLabels, "") == true) && (processedLabels.count(lastLabel) != 1)) {
1133                                 string saveLabel = list->getLabel();
1134                                 
1135                                 delete list;
1136                                 list = input.getListVector(lastLabel);
1137                                 
1138                                 processedLabels.insert(list->getLabel());
1139                                 userLabels.erase(list->getLabel());
1140                                 
1141                                 //restore real lastlabel to save below
1142                                 list->setLabel(saveLabel);
1143                                 break;
1144                         }
1145                         
1146                         lastLabel = list->getLabel();                   
1147                         
1148                         //get next line to process
1149                         //prevent memory leak
1150                         delete list; 
1151                         list = input.getListVector();
1152                 }
1153                 
1154                 
1155                 if (m->control_pressed) {  return 0;  }
1156                 
1157                 //output error messages about any remaining user labels
1158                 set<string>::iterator it;
1159                 bool needToRun = false;
1160                 for (it = userLabels.begin(); it != userLabels.end(); it++) {  
1161                         m->mothurOut("Your file does not include the label " + *it); 
1162                         if (processedLabels.count(lastLabel) != 1) {
1163                                 m->mothurOut(". I will use " + lastLabel + "."); m->mothurOutEndLine();
1164                                 needToRun = true;
1165                         }else {
1166                                 m->mothurOut(". Please refer to " + lastLabel + "."); m->mothurOutEndLine();
1167                         }
1168                 }
1169                 
1170                 //run last label if you need to
1171                 if (needToRun == true)  {
1172                         delete list; 
1173                         list = input.getListVector(lastLabel);
1174                 }       
1175                 
1176                 return 0;
1177         }
1178         catch(exception& e) {
1179                 m->errorOut(e, "PrimerDesignCommand", "getListVector"); 
1180                 exit(1);
1181         }
1182 }
1183 //********************************************************************/
1184 /* A = adenine
1185  * C = cytosine
1186  * G = guanine
1187  * T = thymine
1188  * R = G A (purine)
1189  * Y = T C (pyrimidine)
1190  * K = G T (keto)
1191  * M = A C (amino)
1192  * S = G C (strong bonds)
1193  * W = A T (weak bonds)
1194  * B = G T C (all but A)
1195  * D = G A T (all but C)
1196  * H = A C T (all but G)
1197  * V = G C A (all but T)
1198  * N = A G C T (any) */
1199 int PrimerDesignCommand::countDiffs(string oligo, string seq){
1200         try {
1201                 
1202                 int length = oligo.length();
1203                 int countDiffs = 0;
1204                 
1205                 for(int i=0;i<length;i++){
1206             
1207                         oligo[i] = toupper(oligo[i]);
1208             seq[i] = toupper(seq[i]);
1209             
1210                         if(oligo[i] != seq[i]){
1211                 if(oligo[i] == 'A' && (seq[i] != 'A' && seq[i] != 'M' && seq[i] != 'R' && seq[i] != 'W' && seq[i] != 'D' && seq[i] != 'H' && seq[i] != 'V'))       {    countDiffs++;   }
1212                 else if(oligo[i] == 'C' && (seq[i] != 'C' && seq[i] != 'Y' && seq[i] != 'M' && seq[i] != 'S' && seq[i] != 'B' && seq[i] != 'H' && seq[i] != 'V'))       {       countDiffs++;   }
1213                 else if(oligo[i] == 'G' && (seq[i] != 'G' && seq[i] != 'R' && seq[i] != 'K' && seq[i] != 'S' && seq[i] != 'B' && seq[i] != 'D' && seq[i] != 'V'))       {       countDiffs++;   }
1214                 else if(oligo[i] == 'T' && (seq[i] != 'T' && seq[i] != 'Y' && seq[i] != 'K' && seq[i] != 'W' && seq[i] != 'B' && seq[i] != 'D' && seq[i] != 'H'))       {       countDiffs++;   }
1215                 else if((oligo[i] == '.' || oligo[i] == '-'))           {       countDiffs++;   }
1216                                 else if((oligo[i] == 'N' || oligo[i] == 'I') && (seq[i] == 'N'))                         {      countDiffs++;   }
1217                                 else if(oligo[i] == 'R' && (seq[i] != 'A' && seq[i] != 'G'))                        {   countDiffs++;   }
1218                                 else if(oligo[i] == 'Y' && (seq[i] != 'C' && seq[i] != 'T'))                        {   countDiffs++;   }
1219                                 else if(oligo[i] == 'M' && (seq[i] != 'C' && seq[i] != 'A'))                        {   countDiffs++;   }
1220                                 else if(oligo[i] == 'K' && (seq[i] != 'T' && seq[i] != 'G'))                        {   countDiffs++;   }
1221                                 else if(oligo[i] == 'W' && (seq[i] != 'T' && seq[i] != 'A'))                        {   countDiffs++;   }
1222                                 else if(oligo[i] == 'S' && (seq[i] != 'C' && seq[i] != 'G'))                        {   countDiffs++;   }
1223                                 else if(oligo[i] == 'B' && (seq[i] != 'C' && seq[i] != 'T' && seq[i] != 'G'))       {   countDiffs++;   }
1224                                 else if(oligo[i] == 'D' && (seq[i] != 'A' && seq[i] != 'T' && seq[i] != 'G'))       {   countDiffs++;   }
1225                                 else if(oligo[i] == 'H' && (seq[i] != 'A' && seq[i] != 'T' && seq[i] != 'C'))       {   countDiffs++;   }
1226                                 else if(oligo[i] == 'V' && (seq[i] != 'A' && seq[i] != 'C' && seq[i] != 'G'))       {   countDiffs++;   }       
1227             }
1228                         
1229                 }
1230                 
1231                 return countDiffs;
1232         }
1233         catch(exception& e) {
1234                 m->errorOut(e, "PrimerDesignCommand", "countDiffs");
1235                 exit(1);
1236         }
1237 }
1238 //**********************************************************************************************************************
1239
1240
1241