ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/commandLine/commandLine.c
(Generate patch)

Comparing trunk/commandLine/commandLine.c (file contents):
Revision 518 by mmeineke, Mon May 5 21:35:19 2003 UTC vs.
Revision 521 by mmeineke, Tue May 6 19:42:41 2003 UTC

# Line 1 | Line 1
1   #include <stdio.h>
2   #include <stdlib.h>
3   #include <string.h>
4 #include <math.h>
4  
5 + #define STR_BUFFER_SIZE 500
6  
7 < #include "madProps.h"
8 < #include "frameCount.h"
7 > // *******************************************************
8 > // Uncomment the following two tyedefs to support multiple
9 > // input files
10 > // *******************************************************
11  
12 < char *program_name; /*the name of the program */
12 > typedef struct{
13 >  char fileName[STR_BUFFER_SIZE];
14 >  FILE* filePtr;
15 > } fileStruct;
16  
17 + typedef struct linkedNameTag {
18 +  char name[STR_BUFFER_SIZE];
19 +  struct linkedNameTag* next;
20 + } linkedName;
21 +
22 + // *******************************************************
23 + // end multiple file support
24 + // *******************************************************
25 +
26 +
27 + char *programName; /*the name of the program */
28   void usage(void);
29  
30 < int main(argc, argv)
31 <     int argc;
32 <     char *argv[];
30 > int main(argC, argV)
31 >     int argC;
32 >     char *argV[];
33   {
34 +  int i,j; // loop counters
35  
36 +  char* outPrefix; // the output prefix
37  
38 <  struct xyz_frame *dumpArray;
39 <  int nFrames; // the nu8mvber of frames
40 <  int lineNum = 0; // keeps track of the line number
23 <  int n_atoms; // the number of atoms
24 <  int i,j; // loop counters
25 <  int isFirst;
38 >  char* conversionCheck;
39 >  int conversionError;
40 >  int optionError;
41  
42 <  char read_buffer[2000]; /*the line buffer for reading */
43 <  char *foo; /*the pointer to the current string token */
44 <  FILE *in_file; /* the input file */
30 <  char *in_name = NULL; /*the name of the input file */
31 <  char *out_prefix; // the output prefix
32 <  char current_flag; // used in parseing the flags
42 >  char currentFlag; // used in parsing the flags
43 >  int done = 0; // multipurpose boolean
44 >  int havePrefix; // boolean for the output prefix
45  
46 +  int flag1;
47 +  char flag1Arg1[STR_BUFFER_SIZE];
48 +  char flag1Arg2[STR_BUFFER_SIZE];
49 +  int flag2;
50 +  double flag2Val;
51 +  int flag3;
52 +  int rFlag;
53 +  int iFlag, iVal;
54 +  
55 +  // **********************************************************
56 +  // to support single or multiple input files, uncomment the
57 +  // appropriate declarations
58 +  // **********************************************************
59 +  // single:
60  
61 +  char* inName;
62 +  FILE* inFile;
63  
64 <  int done = 0; // multipurpose boolean
65 <  int have_prefix = 0; // prefix boolean
66 <  int calcRMSD = 0;
64 >  // ***********************************************************
65 >  // multiple:
66 >  
67 >  fileStruct* inputArray;
68 >  linkedName* headName;
69 >  linkedName* currentName;
70 >  int nInputs;
71 >  
72 >  // end input file declarations
73 >  // ***********************************************************
74 >  
75 >  
76  
77 <  int calcGofR = 0;
78 <  char gofR1[30];
79 <  char gofR2[30];
77 >  // intialize default values
78 >  
79 >  
80 >  // ********************************************
81 >  // single input:
82 >  
83 >  inName = NULL;
84 >  inFile = NULL;
85 >  
86 >  // *********************************************
87 >  // multiple input:
88 >  
89 >  nInputs = 0;
90 >  inputArray = NULL;
91 >  headName = NULL;
92 >  currentName = NULL;
93  
94 <  int calcMuCorr = 0;
95 <  char muCorr[30];
94 >  // end file initialization
95 >  // **********************************************
96  
47  int calcCosCorr = 0;
48  char cosCorr1[30];
49  char cosCorr2[30];
97  
51  int startFrame = 0;
52  int haveStartFrame = 0;
53  int endFrame = 0;
54  int haveEndFrame = 0;
98  
99 <  program_name = argv[0]; /*save the program name in case we need it*/
99 >  outPrefix = NULL;
100 >
101 >  conversionError = 0;
102 >  optionError = 0;
103 >
104 >  havePrefix = 0;
105 >  flag1 = 0;
106 >  flag2 = 0;
107 >  flag3 = 0;
108 >  rFlag = 0;
109 >  iFlag = 0;
110 >
111    
112 <  for( i = 1; i < argc; i++){
112 >  iVal = 0;
113 >  
114 >
115 >  
116 >  // here we set the name of the program need by the usage message
117 >  programName = argV[0];
118 >  
119 >
120 >  for( i = 1; i < argC; i++){
121      
122 <    if(argv[i][0] =='-'){
122 >    if(argV[i][0] =='-'){
123  
124        // parse the option
125        
126 <      if(argv[i][1] == '-' ){
126 >      if(argV[i][1] == '-' ){
127  
128          // parse long word options
129          
130 <        if( !strcmp( argv[i], "--GofR" ) ){
131 <          calcGofR = 1;
130 >        if( !strcmp( argV[i], "--flag1" ) ){
131 >          flag1 = 1; // set flag1 to true
132 >          
133            i++;
134 <          strcpy( gofR1, argv[i] );
134 >          strcpy( flag1Arg1, argV[i] );
135            i++;
136 <          strcpy( gofR2, argv[i] );
136 >          strcpy( flag1Arg2, argV[i] );
137          }
138  
139 <        else if( !strcmp( argv[i], "--CosCorr" ) ){
140 <          calcCosCorr = 1;
139 >        else if( !strcmp( argV[i], "--flag2" ) ){
140 >          flag2 = 1; // set the flag2 to true;
141 >          
142            i++;
143 <          strcpy( cosCorr1, argv[i] );
144 <          i++;
145 <          strcpy( cosCorr2, argv[i] );
143 >          flag2Val = strtod( argV[i], &conversionCheck );
144 >          if( conversionCheck == argV[i] ) conversionError = 1;
145 >          if( *conversionCheck != '\0' ) conversionError = 1;
146 >          
147 >          if( conversionError ){
148 >            
149 >            fprintf( stderr,
150 >                     "Error converting \"%s\" to a double\n", argV[i] );
151 >            usage();
152 >            exit(0);
153 >          }
154 >          
155          }
156  
157 <        else if( !strcmp( argv[i], "--MuCorr") ){
158 <          calcMuCorr = 1;
159 <          i++;
87 <          strcpy( muCorr, argv[i] );
157 >        else if( !strcmp( argV[i], "--flag3") ){
158 >          flag3 = 1; // set flag3 to be true
159 >          
160          }
161          
162 <        else if( !strcmp( argv[i], "--startFrame" ) ){
91 <          haveStartFrame = 1;
92 <          i++;
93 <          startFrame = atoi(argv[i]);
94 <          startFrame--;
95 <        }
96 <
97 <        else if( !strcmp( argv[i], "--endFrame" ) ){
98 <          haveEndFrame = 1;
99 <          i++;
100 <          endFrame = atoi(argv[i]);
101 <        }
162 >        // anything else is an error
163  
164          else{
165            fprintf( stderr,
166 <                   "Invalid option \"%s\"\n", argv[i] );
166 >                   "Invalid option \"%s\"\n", argV[i] );
167            usage();
168 +          exit(0);
169          }
170        }
171        
# Line 113 | Line 175 | int main(argc, argv)
175          
176          done =0;
177          j = 1;
178 <        current_flag = argv[i][j];
178 >        currentFlag = argv[i][j];
179          while( (current_flag != '\0') && (!done) ){
180            
181            switch(current_flag){
182  
183 +          case 'h':
184 +            // -h => give the usage help message
185 +            
186 +            usage();
187 +            exit(0);
188 +            break;
189 +
190            case 'o':
191              // -o <prefix> => the output prefix.
192  
193 +            j++;
194 +            currentFlag = argV[i][j];
195 +              
196 +            if( currentFlag != '\0' ) optionError = 1;
197 +            
198 +            if( optionError ){
199 +              fprintf( stderr,
200 +                       "\n"
201 +                       "The -o flag should end an option sequence.\n"
202 +                       "   example: -ro <outname> *NOT* -or <outname>\n" );
203 +              usage();
204 +              exit(0);
205 +            }
206 +              
207              i++;
208 <            out_prefix = argv[i];
209 <            have_prefix = 1;
208 >            outPrefix = argV[i];
209 >            if( outPrefix[0] == '-' ) optionError = 1;
210 >                
211 >            if( optionError ){
212 >              fprintf( stderr,
213 >                       "\n"
214 >                       "\"%s\" is not a valid out prefix/name.\n"
215 >                       "Out prefix/name should not begin with a dash.\n",
216 >                       outPrefix );
217 >              usage();
218 >              exit(0);
219 >            }
220 >            
221 >            havePrefix = 1;
222              done = 1;
223              break;
224  
225 <          case 'h':
131 <            // -h => give the usage
132 <            
133 <            usage();
134 <            break;
225 >
226          
227            case 'r':
228 <            // calculates the rmsd
228 >            // the r flag
229  
230 <            calcRMSD = 1;
230 >            rFlag = 1; // set rflag to true
231              break;
232  
233 <          case 'g':
234 <            // calculate all to all g(r)
233 >          case 'i':
234 >            // -i <int>    set <int> to the iVal
235  
236 <            calcGofR = 1;
237 <            strcpy( gofR1, "all" );
238 <            strcpy( gofR2, "all" );
236 >            iFlag = 1; // set iFlag to true
237 >            j++;
238 >            currentFlag = argV[i][j];
239 >              
240 >            if( currentFlag != '\0' ) optionError = 1;
241 >            
242 >            if( optionError ){
243 >              fprintf( stderr,
244 >                       "\n"
245 >                       "The -i flag should end an option sequence.\n"
246 >                       "   example: -ri <int> *NOT* -ir <int>\n" );
247 >              usage();
248 >              exit(0);
249 >            }
250 >
251 >            i++;
252 >            iVal = (int)strtol( argV[i], &conversionCheck, 10 );
253 >            if( conversionCheck == argV[i] ) conversionError = 1;
254 >            if( *conversionCheck != '\0' ) conversionError = 1;
255 >            
256 >            if( conversionError ){
257 >              
258 >              fprintf( stderr,
259 >                       "Error converting \"%s\" to a int\n", argV[i] );
260 >              usage();
261 >              exit(0);
262 >            }
263 >            
264 >            done = 1;
265 >
266              break;
267  
268            default:
269  
270 <            (void)fprintf(stderr, "Bad option \"-%c\"\n", current_flag);
270 >            (void)fprintf(stderr,
271 >                          "\n"
272 >                          "Bad option \"-%c\"\n", current_flag);
273              usage();
274            }
275            j++;
# Line 160 | Line 280 | int main(argc, argv)
280  
281      else{
282        
283 <      if( in_name != NULL ){
283 >      
284 >      // ********************************************************
285 >      // for only a single input file, leave this as it is.
286 >      // ********************************************************
287 >      
288 >      if( inName != NULL ){
289          fprintf( stderr,
290 +                 "\n"
291                   "Error at \"%s\", program does not currently support\n"
292                   "more than one input file.\n"
293                   "\n",
294 <                 argv[i]);
294 >                 argV[i]);
295          usage();
296 +        exit(0);
297        }
298 +      
299 +      inName = argvV[i];
300 +      
301 +      // *************************************************************
302 +      
303 +      
304 +      // ************************************************************
305 +      // To support more than one input file, uncomment the following
306 +      // section.
307 +      // ************************************************************
308  
309 <      in_name = argv[i];
309 >      
310 >      nInputs++;
311 >      currentName = (linkedName *) malloc( sizeof( linkedName ) );
312 >      if( currentName == NULL ){
313 >        fprintf( stderr,
314 >                 "\n"
315 >                 "Ran out of memory\n" );
316 >        exit(0);
317 >      }
318 >      
319 >      strcpy( currentName->name, argV[i] );
320 >      
321 >      currentName->next = headName;
322 >      headName = currentName;
323 >      
324 >      // **********************************************************
325 >      // end multiple input files
326 >      // **********************************************************
327      }
328    }
329  
176  if(in_name == NULL){
177    usage();
178  }
330  
180  if( !have_prefix ) out_prefix = in_name;
181  
182  printf( "Counting number of frames..." );
183  fflush( stdout );
184  
185  nFrames = frameCount( in_name );
186  if( !haveEndFrame ) endFrame = nFrames;
331  
188  printf( "done.\n"
189          "nframes = %d\n"
190          "\n",
191          nFrames );
192  fflush( stdout );
332  
333 <  in_file = fopen(in_name, "r");
195 <  if(in_file == NULL){
196 <    printf("Cannot open file: %s\n", in_name);
197 <    exit(8);
198 <  }
333 >  // initialize the input file(s)
334  
335 <  // create and initialize the array of frames
335 >  // ***********************************************************
336 >  // single file:
337 >  
338 >  if(inName == NULL){
339 >    
340 >    fprintf( stderr,
341 >             "\n"
342 >             "Error, no input file was given\n");
343 >    usage();
344 >    exit(0);
345 >  }
346  
347 <  dumpArray = (struct xyz_frame*)calloc( nFrames,
348 <                                         sizeof( struct xyz_frame ) );
349 <  for( i=0; i<nFrames; i++ ){
350 <    dumpArray[i].nAtoms = 0;
351 <    dumpArray[i].time   = 0.0;
352 <    dumpArray[i].boxX   = 0.0;
353 <    dumpArray[i].boxY   = 0.0;
354 <    dumpArray[i].boxZ   = 0.0;
210 <    dumpArray[i].r      = NULL;
211 <    dumpArray[i].v      = NULL;
212 <    dumpArray[i].names  = NULL;
347 >  inFile = fopen( inName, "r" );
348 >  if( inFile == NULL ){
349 >    
350 >    fprintf( stderr,
351 >             "\n"
352 >             "Error trying to open \"%s\" for reading\n",
353 >             inName );
354 >    exit(0);
355    }
356  
357 <  // read the frames
357 >  // **************************************************************
358 >  // multiple files:
359    
360 <  printf( "Reading the frames into the coordinate arrays..." );
218 <  fflush( stdout );
219 <
220 <  isFirst = 1;
221 <  for(j =0; j<nFrames; j++ ){
360 >  if( !nInputs ){
361      
362 <    // read the number of atoms
362 >    fprintf( stderr,
363 >             "\n"
364 >             "Error, no input files were given\n");
365 >    usage();
366 >    exit(0);
367 >  }
368  
369 <    fgets(read_buffer, sizeof(read_buffer), in_file);
226 <    lineNum++;
369 >  // create the input array
370  
371 <    n_atoms = atoi( read_buffer );
372 <    dumpArray[j].nAtoms = n_atoms;
373 <
374 <    dumpArray[j].r =
375 <      (struct coords *)calloc(n_atoms, sizeof(struct coords));
371 >  inputArray = (fileStruct *) calloc( nInputs, sizeof( fileStruct ) );
372 >  if( inputArray == NULL ){
373 >    fprintf(stderr,
374 >            "\n"
375 >            "Ran out of memory\n" );
376 >    exit(0);
377 >  }
378 >  
379 >  j = nInputs - 1;
380 >  currentName = headName;
381 >  while( currentName != NULL ){
382      
383 <    if( isFirst ) {
384 <      dumpArray[0].names =
385 <        (atomID *)calloc( n_atoms, sizeof(atomID) );
386 <      isFirst = 0;
387 <    }
383 >    strcopy( inputArray[j].fileName, currentName->name );
384 >    inputArray[i].filePtr = NULL;
385 >    j--;
386 >    currentName = currentName->next;
387 >  }
388  
389 <    if( calcMuCorr || calcCosCorr ){
390 <          dumpArray[j].v =
391 <            (struct vect *)calloc(n_atoms, sizeof(struct vect));
243 <    }
244 <
245 <    //read the time and the box sizes
246 <
247 <    fgets(read_buffer, sizeof(read_buffer), in_file);
248 <    lineNum++;
389 >  // delete linked list
390 >  
391 >  while( headName != NULL ){
392      
393 <    foo = strtok(read_buffer, " ,;\t");
394 <    if(foo == NULL){
252 <      printf("error in reading time at line %d\n", lineNum);
253 <      exit(8);
254 <    }
255 <    
256 <    dumpArray[j].time = atof( foo );
257 <    
258 <    foo = strtok(NULL, " ,;\t");
259 <    if(foo == NULL){
260 <      printf("error in reading boxX at line %d\n", lineNum);
261 <      exit(8);
262 <    }
263 <    
264 <    dumpArray[j].boxX = atof( foo );
393 >    currentName = headName;
394 >    headName = currentName->next;
395  
396 <    foo = strtok(NULL, " ,;\t");
397 <    if(foo == NULL){
398 <      printf("error in reading boxY at line %d\n", lineNum);
269 <      exit(8);
270 <    }
271 <    
272 <    dumpArray[j].boxY = atof( foo );
273 <    
274 <    foo = strtok(NULL, " ,;\t");
275 <    if(foo == NULL){
276 <      printf("error in reading boxZ at line %d\n", lineNum);
277 <      exit(8);
278 <    }
279 <    
280 <    dumpArray[j].boxZ = atof( foo );
396 >    free( currentName );
397 >  }
398 >  
399  
400 +  // open the files for reading
401 +  
402 +  for(i=0; i<nInputs; i++){
403  
404 <    for( i=0; i < n_atoms; i++){
404 >    inputArray[i].filePtr = fopen( inName, "r" );
405 >    if( inputArray[i].filePtr == NULL ){
406        
407 <      fgets(read_buffer, sizeof(read_buffer), in_file);
408 <      lineNum++;
409 <      
410 <      // get the name and positions
407 >      fprintf( stderr,
408 >               "\n"
409 >               "Error trying to open \"%s\" for reading\n",
410 >               inputArray[i].fileName );
411 >      exit(0);
412 >    }
413  
414 <      foo = strtok(read_buffer, " ,;\t");
291 <      if(foo == NULL){
292 <        printf("error in reading atom name at line %d\n", lineNum );
293 <        exit(8);
294 <      }
295 <      
296 <      strcpy(dumpArray[0].names[i], foo); /*copy the atom name */
414 >    //read and do stuff here
415  
298      foo = strtok(NULL, " ,;\t");
299      if(foo == NULL){
300        printf("error in reading position x at line %d\n", lineNum);
301        exit(8);
302      }
303      
304      dumpArray[j].r[i].x = atof( foo );
305
306      foo = strtok(NULL, " ,;\t");
307      if(foo == NULL){
308        printf("error in reading position y at line %d\n", lineNum);
309        exit(8);
310      }
311      
312      dumpArray[j].r[i].y = atof( foo );
313
314      foo = strtok(NULL, " ,;\t");
315      if(foo == NULL){
316        printf("error in reading position z at line %d\n", lineNum);
317        exit(8);
318      }
319      
320      dumpArray[j].r[i].z = atof( foo );
321
322      if( calcCosCorr || calcMuCorr ){
323        
324        foo = strtok(NULL, " ,;\t");
325        if(foo == NULL){
326                  
327          dumpArray[j].v[i].x = 0.0;
328          dumpArray[j].v[i].y = 0.0;
329          dumpArray[j].v[i].z = 0.0;
330        }
331        else{
332
333          dumpArray[j].v[i].x = atof( foo );
334          
335          foo = strtok(NULL, " ,;\t");
336          if(foo == NULL){
337            printf("error in reading vector y at line %d\n", lineNum);
338            exit(8);
339          }
340          
341          dumpArray[j].v[i].y = atof( foo );
342          
343          foo = strtok(NULL, " ,;\t");
344          if(foo == NULL){
345            printf("error in reading vector z at line %d\n", lineNum);
346            exit(8);
347          }
348          
349          dumpArray[j].v[i].z = atof( foo );
350        }
351      }
352      
353    }
416    }
417    
418 <  (void)fclose(in_file);
418 >  // *************************************************************
419 >  // end file opening
420 >  // *************************************************************
421    
358  printf( "done\n"
359          "\n" );
360  fflush( stdout );
361  
362  
422  
364  // do calculations here.
423  
424 <  if( calcGofR ){
425 <    
368 <    fprintf( stdout,
369 <             "Calculating the g(r) between atoms \"%s\" and \"%s\"...",
370 <             gofR1, gofR2 );
371 <    fflush( stdout );
372 <    
373 <    // gofr call
374 <    GofR( out_prefix, gofR1, gofR2, dumpArray, nFrames, startFrame, endFrame );
375 <    
376 <    fprintf( stdout,
377 <             " done.\n"
378 <             "\n");
379 <    fflush(stdout);
380 <  }
424 >  //  or read files here
425 >  
426  
382  if( calcRMSD ){
383    
384    fprintf( stdout,
385             "Calculating the RMSD..." );
386    fflush( stdout );
387    
388    // RMSD call
427  
390    
391    fprintf( stdout,
392             " done.\n"
393             "\n");
394    fflush(stdout);
395  }
428  
397  if( calcMuCorr ){
398    
399    fprintf( stdout,
400             "Calculating the mu correlation for \"%s\"...",
401             muCorr);
402    fflush( stdout );
403    
404    // muCorr call
429  
430 <    
407 <    fprintf( stdout,
408 <             " done.\n"
409 <             "\n");
410 <    fflush(stdout);
411 <  }
430 >  // close files when we are done.
431  
432 <  if( calcCosCorr ){
433 <    
415 <    fprintf( stdout,
416 <             "Calculating the cos correlation between \"%s\" and \"%s\"...",
417 <             cosCorr1, cosCorr2 );
418 <    fflush( stdout );
419 <    
420 <    cosCorr( out_prefix, cosCorr1, cosCorr2, dumpArray, nFrames, startFrame,
421 <             endFrame );
422 <    
423 <    fprintf( stdout,
424 <             " done.\n"
425 <             "\n");
426 <    fflush(stdout);
427 <  }
432 >  // ***********************************************************
433 >  // single:
434    
435 <  return 0;
435 >  fclose( inFile );
436    
437 < }
437 >  // *************************************************************
438 >  // multiple:
439  
440 +  for( i=0; i<nInputs; i++){
441 +    
442 +    fclose( inputArray[i].filePtr );
443 +  }
444  
445 < void map( double *x, double *y, double *z,
446 <          double boxX, double boxY, double boxZ ){
436 <  
437 <  *x -= boxX * copysign(1.0,*x) * floor( fabs( *x/boxX ) + 0.5  );
438 <  *y -= boxY * copysign(1.0,*y) * floor( fabs( *y/boxY ) + 0.5  );
439 <  *z -= boxZ * copysign(1.0,*z) * floor( fabs( *z/boxZ ) + 0.5  );
445 >  free( inputArray ); // free the input Array if we are done;
446 >  inputArray = NULL;
447  
448 +  // ************************************************************
449 +  // end file closing
450 +  // ************************************************************
451 +
452   }
453  
454  
# Line 447 | Line 458 | void usage(){
458  
459   void usage(){
460    (void)fprintf(stdout,
461 <                "The proper usage is: %s [options] <xyz_file>\n"
461 >                "The proper usage is: %s [options] <input_file>\n"
462                  "\n"
463                  "Options:\n"
464                  "\n"
465                  "   short:\n"
466                  "   ------\n"
467                  "   -h              Display this message\n"
468 <                "   -o <prefix>     The output prefix\n"
469 <                "   -r              Calculate the RMSD\n"
470 <                "   -g              Calculate all to all g(r)\n"
468 >                "   -o <name>       The output name/prefix\n"
469 >                "   -r              the r flag\n"
470 >                "   -i <int>        set number to <#>\n"
471                  
472                  "\n"
473                  "   long:\n"
474                  "   -----\n"
475 <                "   --GofR <atom1> <atom2>    Calculates g(r) between atom1 and atom 2\n"
476 <                "                               -note: \"all\" will do all atoms\n"
477 <                "   --MuCorr <atom>           Calculate mu correlation of atom\n"
467 <                "   --CosCorr <atom1> <atom2> Calculate the cos correlation between atom1 and atom2\n"
468 <                "   --startFrame <frame#>     Specifies a frame to start correlating\n"
469 <                "   --endFrame <frame#>       Specifies a frame to stop correlating.\n"
475 >                "   --flag1 <arg1> <arg2>     does flag 1 for arg1 and 2\n"
476 >                "   --flag2 <double>          does flag 2 for double\n"
477 >                "   --flag3                   does flag 3\n"
478                  
479                  "\n"
480                  "\n",
481 <                program_name);
482 <  exit(8);
481 >                program_name)
482 >  exit(0);
483   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines