/* * Name: Geoff Kuenning * Course: CS 70, Fall 2000 * Assignment #7 * * This file contains the main program for the assignment. This * program serves as a test driver for a set of DNA recombination * functions. * * Since it is a test driver, the program has a very simple * command-line interface. There are two methods of invocation: * * assign_07 mutate * * or * * assign_07 combine * * In both cases, "position" is a number, and the "strings" are * strings of characters (presumably gene sequences). Positions are * numbered from zero. * * The mutate operation replaces a single character (gene) at the * specified position of string1 with the entire contents of string2. * Note that this can make the gene sequence longer if string2 is more * than one character long, or can shorten it if string2 is the empty * string. * * The combine operation glues the first "position" characters of * string1 to the remaining characters of string2 beginning at * "position" and continuing to the end of string2. The two strings * do not need to be the same length. * * Examples: * * Shell command Output * * assign_07 mutate 3 abcdefg X abcXefg * assign_07 mutate 3 abcdefg XY abcXYefg * assign_07 mutate 3 abcdefg '' abcefg * assign_07 mutate 99 abcdefg X abcdefg * assign_07 combine 0 abcdefg uvwxyz uvwxyz * assign_07 combine 3 abcdefg uvwxyz abcxyz * assign_07 combine 99 abcdefg uvwxyz abcdefg * assign_07 combine 4 abc uvwxyz abcyz */ #include "charlist.hh" #include #include /* * Table of contents: the following functions are defined in the file */ int main(int argc, char* argv[]); // Test driver static void usage(char* programName); // Issue a usage message CharList mutate(const CharList& target, int position, const CharList& newGenes); // Do the mutation operation CharList combine(const CharList& parent1, const CharList& parent2, int position); // Do the combination operation /* * Global constants. These are used to define the layout of arguments. */ const int ARGS_NEEDED = 4; // Number of arguments expected // ..(excluding program name) const int OPERATION_ARG = 0 + 1; // Where to find operation const int POSITION_ARG = 1 + 1; // Where to find position const int STRING1_ARG = 2 + 1; // Where to find first string const int STRING2_ARG = 3 + 1; // Where to find second string /* * Main driver for testing DNA recombination. See above for * documentation. */ int main( int argc, // Argument count char* argv[]) // Argument vector { /* * Check the arguments to be sure we have the right number. Then * convert the position to an integer, and convert the two strings * into two lists of characters. */ if (argc != ARGS_NEEDED + 1) usage(argv[0]); int position = atoi(argv[POSITION_ARG]); CharList string1(argv[STRING1_ARG]); CharList string2(argv[STRING2_ARG]); /* * Figure out the operation requested, do it, and print the result. */ CharList modifiedString; if (string(argv[OPERATION_ARG]) == "mutate") modifiedString = mutate(string1, position, string2); else if (string(argv[OPERATION_ARG]) == "combine") modifiedString = combine(string1, string2, position); else usage(argv[0]); cout << modifiedString << endl; /* * Because the usage routine aborts the program, we can only get * here if we succeeded. */ return 0; } /* * Print a usage message and exit. As is conventional, the exit * status is 2 if there is a usage error. */ static void usage( char* programName) // Name we were run under { cerr << "Usage:\t" << programName << " mutate position string1 new-value\n"; cerr << '\t' << programName << " combine position string1 string2\n"; exit(2); } /* * Perform DNA mutation. We walk through the target string, copying * it to the result. When we get to the mutated position, we replace * it with a copy of the new genes to be inserted. */ CharList mutate( const CharList& target, // Gene list to be mutated int position, // Position to mutate (0-indexed) const CharList& newGenes) // Genes to insert at mutation point { CharList result; /* * Because a CharListIterator doesn't provide a way to find out * how far we are in the list, we need a separate variable to * track our position. */ int whereWeAre = 0; /* * Copy the target to the result verbatim, except that when we * reach the mutated position, we copy all of newGenes into the * result, and skip the gene at the target in that position. */ for (CharListIterator i(target); i; ++i, ++whereWeAre) { if (whereWeAre != position) result.pushTail(*i); else { for (CharListIterator j(newGenes); j; ++j) result.pushTail(*j); } } return result; } /* * Perform DNA recombination. We walk through the two parent strings * simultaneously, copying one or the other to the result. The choice * of what to copy depends on the current position relevant to the * recombination position. */ CharList combine( const CharList& parent1, // First parent's gene list const CharList& parent2, // Second parent's gene list int position) // Where to switch from first to second { CharList result; /* * We need one iterator for each parent, plus an integer to track * the current position (as in mutation). */ CharListIterator parent1Gene(parent1); CharListIterator parent2Gene(parent2); /* * Go through both gene lists. Note that the loop continues as * long as *either* parent has valid genes. A side implication of * this loop structure is that the iterators must be robust enough * to allow operator++ to be called even if they are expired. */ for (int currentPosition = 0; parent1Gene || parent2Gene; ++parent1Gene, ++parent2Gene, ++currentPosition) { /* * Depending on the current position, add one gene or the * other to the result list. Note that we only attempt the * push if the associated iterator is valid (i.e., if it can * legally be dereferenced). To do otherwise would either * produce garbage output or segfault the program, depending * on the implementation of the iterators. */ if (currentPosition < position) { if (parent1Gene) result.pushTail(*parent1Gene); } else { if (parent2Gene) result.pushTail(*parent2Gene); } } return result; }