/* Program to perform date conversions */ /* CMSCD1003 Programming Assignment, 1999 */ /* The include files conio.h, ctype.h, stdlib.h, string.h, and time.h */ /* are needed by some of the functions contained in this program */ #include #include #include #include #include #include /* Program Outline */ /* do { Clear the screen Display six blank lines Display main menu and user prompt Get user's choice switch user choice of 1 : clear the screen and display six blank lines Prompt user to enter a year Accept year Display year in roman numerals Prompt user to hit a key to continue Accept keystroke 2: clear the screen and display six blank lines Call the RomanToDecimal function Prompt user to hit a key to continue Accept keystroke 3: clear the screen and display six blank lines Prompt for and accept a date (dd/mm/yyyy) Display date IF month Jan,Feb or March Display Gregorian_to_julian conversion of dd,mm,yyyy-1 ELSE Display Gregorian_to_julian conversion of dd,mm,yyyy Prompt user to hit a key to continue Accept keystroke 4: clear the screen and display six blank lines Prompt for and accept a Julian day number Display Julian_to_gregorian conversion of JDN Prompt user to hit a key to continue Accept keystroke 5: clear the screen and display six blank lines select a random number, n Do this with the following lines of code- randomize () ; n = rand () % 4000 ; Display user prompt and Roman version of chosen number Accept user's guess IF guess equals chosen number Display congratulatory message ELSE Display comiserations and correct answer Prompt user to hit a key to continue Accept keystroke 6: clear the screen and display six blank lines Prompt for and accept date (dd/mm/yyyy) calculate day of week for the date Prompt for and accept user's guess of day of week IF guess equals day of week Display congratulatory message ELSE Display comiserations and correct answer Prompt user to hit a key to continue Accept keystroke } while user not choose to exit. */ void main () { /* Prototypes for the program functions you will write */ /* Functions are covered in detail in Chapter 10 of the book */ char * DecimalToRoman (int) ; void JulianToGregorian (long, int*, int*, int*) ; int GetDay (int, int, int) ; /* Prototypes for functions we have written for you */ void RomanToDecimal (void) ; int translate (char) ; long GregorianToJulian (int, int, int) ; char * lookup (int day) ; /* Variable declarations. We have declared all the variables you need */ /* Think carefully before introducing any new ones */ int day, month, year, choice, cntr ; int n, guess, dayofweek ; char dummy ; long julian ; /* Why have we made this a long integer? */ /* Insert your code here */ } /* End of function main */ /* ****************************************************************** */ /* Below is where you will complete the various functions. For each, */ /* remember to 'uncomment' it */ /* ****************************************************************** */ /* Function GetDay returns the day of the week of a given date */ /* int GetDay (int dd, int mm, int yyyy) */ /* Description... Subtract mm from 14, divide the result by 12 and store in 'temp' Set year = yyyy - temp Set month = mm + temp * 12 - 2 | | | year year year | month | | Return |dd + year + ---- - ---- + ---- + 31 | ----- | | MOD 7 | 4 100 400 | 12 | | | | */ /* { int temp, month, year ; } */ /* Function RomanToDecimal. Gives the decimal value of a Roman number */ /* void RomanToDecimal (void) */ /* Description.... Prompt for and accept a Roman number FOR each character in Roman number Convert character to uppercase (use the 'toupper' function for this) Display Roman number on screen Convert number to decimal (see below) Display results (we have done this bit for you) */ /* Note, there are two ways you can do the conversion: a) quick b) full. A - Quick Method (carries less credit) -------------------------------------- The quick method works backwards through the number string Note, it will not validate strings for correctness and thus will give erroneous output for malformed numbers like XLC (40 instead of the implied 140). Outline for the method: Set total = last digit value Set predecessor digit = ' ' FOR each digit from penultimate downto first DO IF current digit < predecessor THEN subtract current digit value from total ELSE add current digit value to total Set predecessor = current ; set valid = 1 (true) B - Full Method (for full credit) --------------------------------- The full method parses (validates) the Roman number string according to the rules set out in the coursework specification. Set previous = ' ' Set counter = 0 WHILE counter < length of Roman number string (use function 'strlen') Set valid = 1 Set current = roman [counter] Translate current and store result in 'i' (use function 'translate' which we have written for you) IF counter + 1 < length of Roman number string (not at penultimate character) Set next = roman [counter + 1] ELSE Set next = ' ' IF i > value of next Add i to number Add 1 to counter Set previous = current ELSE Set 4 rules to 0 or 1 (true/false) - use the following code: rule1 = (current != 'V') ; rule2 = (((translate (next) == (i * 5))) || ((translate (next) == (i * 10)))) ; rule3 = (previous == ' ') || ((translate (previous) >= (i * 10))) ; if (cntr + 2 < strlen (roman)) nextbutone = roman [cntr + 2] ; else nextbutone = ' ' ; rule4 = 1 ; if (nextbutone != ' ') rule4 = (translate (nextbutone) < i) ; IF all four rules ok Add (value of next - i) to number Add 2 to counter Set previous = current ELSE Set valid = 0 Set counter = 1 more than length of Roman number string */ /* { char roman [50] ; char check [50]; int number = 0, i, counter ; char current, previous, next, nextbutone ; int rule1, rule2, rule3, rule4, valid ; // Display results if (valid) { printf (" In decimal : %d.\n", number) ; strcpy(check, DecimalToRoman (number)) ; if (strcmp (check, roman) != 0) printf (" By the way, the number should have been written as %s\n", check) ; } else printf (" Sorry, your Roman number was malformed. Invalid portion : %c%c%c%c\n", previous, current, next, nextbutone) ; } */ /* Function translate is needed by RomanToDecimal */ int translate (char digit) { int i ; switch (digit) { case 'M' : i = 1000 ; break ; case 'D' : i = 500 ; break ; case 'C' : i = 100 ; break ; case 'L' : i = 50 ; break ; case 'X' : i = 10 ; break ; case 'V' : i = 5 ; break ; case 'I' : i = 1 ; break ; case ' ' : i = 0 ; break ; } return i ; } /* long GregorianToJulian (int dd, int mm, int yyyy) ; */ /* A Julian date is the absolute count of the number of days that have passed since 12:00 1/01/4713 B.C. on the Julian Proleptic Calendar. The device of Julian Day Number was introduced by Joseph Scalinger (1540-1609). It effectively ended the use of the Egyptian calendar and the Era of Nabonassar for astronomical purposes, as had been introduced by Claudius Ptolemy (c.100-c.170 AD). Scalinger picked 4713 BC because it was the first year on a number of different calendar cycles and was earlier than any possible historical dates that he knew of. "Julian Day Numbers" may refer to integer numbers corresponding to whole days, while the "Julian Date" may mean an integer plus decimal that brings the Julian count down to precise parts of a day. To convert dates from the Julian or Gregorian calendars to Julian Day Numbers, first the year of the Julian Period must be determined. An AD/CE year is simply added to 4713. Thus, 1997 yields 6710. Years BC/BCE must be expressed as negatives of AD/CE years. 747 BC corresponds to -746 AD (since 1 BC = 0 AD) = 3967. But the year of the Julian Period is awkward for purposes of calculation. If 4713 BC is set to Year 0 instead of Year 1, this is more convenient. The "Scalinger Year" is thus one less than the year of the Julian Period, and may be obtained by adding 4712 instead of 4713 to the year of the AD/CE era. We use Julian Day Numbers as a way of avoiding problems associated with date boundaries, such as Y2K etc. */ /* Method for calculating a Julian Day Number (JDN). For the purposes of calculation a year begins on 1 March, so January and February belong to the previous year. Thus, February, 1997 is taken to be in the year 1996. 1. Establish the Julian Year by adding year to 4712 (using the Scalinger adjustment). Hence, 1997 = 6709. (Year would be 6708 for January and February). 2. Calculate number of four year cycles in the Julian calendar by dividing year by four. For 1997: 6709/4 = 1677, remainder 1. This gives 1677 four-year cycles with the current year being year 1 (range 0-3) in the present cycle. 3. Caclulate number of days. - multiply number of cycles by number of days in four Julian years (1461). Hence, 1677 * 1491 - multiply remainder by number of days in a common Julian year (365). Hence 1 * 365 - Add together: (1677 * 1491) + (1 * 365) = 2,450,462. 4. Get the day number for the month using the following table: Month Day Month Day Month Day ----------------------------------------- 3 Mar 0 7 Jul 122 11 Nov 245 4 Apr 31 8 Aug 153 12 Dec 275 5 May 61 9 Sep 184 1 Jan 306 6 June 92 10 Oct 214 2 Feb 337 Add the day of the month and the day number for the month to the previous total. Thus, is the date was 21 September, we add 21 and 184 to previous total: 2,450,462 + 21 + 184 = 2,450,667. 5. Adjust for year starting on 1 January: Add number of days between 1 Jan 4713 and 0 March 4714 = 59. Hence, 2,450,667 + 59 = 2,450,726 6. Correct for difference between Julian and Gregorian calendar. The Gregorian calendar was introduced in 1582. In the table below find the century of the Gregorian date and add the corresponding correction value to the total. Century Correction Century Correction ----------------------------------------- 1582-1599 -10 1800 -12 1600 -10 1900 -13 1700 -11 2000 -13 Thus, the Julian Day Number for 21 September, 1997 on our Greogorian calendar is: 2,450,726 + -13 = 2,450,713. From all this, we get the following outline design: Set jYear = yyyy + 4712 Set cycles = jyear / 4 Set cycleYear = jYear MOD 4 Set days = cycles + no.days in 4 years + cycleYear * no.days in year Set monthDays = months [mm - 1] IF yyyy < 1582 Set correction = 0 ELSE IF yyyy < 1700 Set correction = -10 ELSE IF yyyy < 1800 Set correction = -11 ELSE IF yyyy < 1900 Set correction = -12 ELSE Set correction = -13 Return days + monthDays + dd + 59 + correction */ /* { const int months [12] = {306,337,0,31,61,92,122,153,184,214,245,275} ; const int daysInFourYears = 1461 ; const int daysInYear = 365 ; long jYear, cycles, cycleYear, days ; int monthDays, correction ; } */ /**********************************************************************/ /* Below are the functions we have already written. There is no need */ /* to change them, but you may learn something from reading them */ /**********************************************************************/ /* Function DecimalToRoman takes an integer and returns a Roman */ /* numeral string */ char * DecimalToRoman (int number) /* Outline: Calculate number of 1000s (m) in number and find the left over Calculate number of 500s (d) in left over and find the left over Calculate number of 100s (c) in left over and find the left over Calculate number of 50s (l) in left over and find the left over Calculate number of 10s (x) in left over and find the left over Calculate number of 5s (v) in left over and find the left over Number of 1s (i) = left over Build a string of 'M's based on number of 1000s (m) IF (number of 500s (d) + number of 100s (c) == 5) (a 900 found) make 500s string = 'CM' ELSE build string of 'D's based on number of 500s (d) build string of 'C's based on number of 100s (c) IF (number of 50s (l) + number of 10s (x) == 5) (90 found) make 50s string = 'XC' ELSE build string of 'L's based on number of 50s (l) build string of 'X's based on number of 10s (x) IF (number of 5s (v) + number of 1s (i) == 5) (9 found) make 5s string = 'IX' ELSE build string of 'V's based on number of 5s (v) build string of 'I's based on number of 1s (i) Copy string of 'M's to Roman Append string of 'D's to Roman Append string of 'C's to Roman Append string of 'L's to Roman Append string of 'X's to Roman Append string of 'V's to Roman Append string of 'I's to Roman Return Roman */ { char static roman [50] ; /* Why is this a 'static' variable? */ int m, d, c, l, x, v, i ; char mstring [20] = ""; char istring [20] = ""; char dstring [20] = ""; char cstring [20] = ""; char lstring [20] = ""; char xstring [20] = ""; char vstring [20] = ""; int remainder ; void build (char *, int, char *) ; m = number / 1000 ; remainder = number % 1000 ; d = remainder / 500 ; remainder = remainder % 500 ; c = remainder / 100 ; remainder = remainder % 100 ; l = remainder / 50 ; remainder = remainder % 50 ; x = remainder / 10 ; remainder = remainder % 10 ; v = remainder / 5 ; i = remainder % 5 ; build ("M", m, mstring) ; if (d + c == 5) /* 900 found */ strcpy (dstring, "CM") ; else { build ("D", d, dstring) ; build ("C", c, cstring) ; } if (l + x == 5) /* 90 found */ strcpy (lstring, "XC") ; else { build ("L", l, lstring) ; build ("X", x, xstring) ; } if (v + i == 5) /* 9 found */ strcpy (vstring, "IX") ; else { build ("V", v, vstring) ; build ("I", i, istring) ; } strcpy (roman, mstring) ; strcat (roman, dstring) ; strcat (roman, cstring) ; strcat (roman, lstring) ; strcat (roman, xstring) ; strcat (roman, vstring) ; strcat (roman, istring) ; return roman ; } /* Function 'build' is needed by DecimalToRoman */ void build (char *numeral, int quantity, char *thestring) { int cntr ; char number = numeral [0] ; if (quantity == 4) { switch (number) { case 'M' : strcpy (thestring, "MMMM") ; break ; case 'C' : strcpy (thestring, "CD") ; break ; case 'X' : strcpy (thestring, "XL") ; break ; case 'I' : strcpy (thestring, "IV") ; break ; } } else for (cntr = 1; cntr <= quantity; cntr ++) strcat (thestring, numeral) ; } /* Function lookup. Takes an integer (0-6) and returns the name */ /* of the corresponding day. Uses the conditional operator ? to do */ /* this. This is not in the text book. Ask if you want an explanation */ /* otherwise just call the function as normal and don't worry about */ /* how it works!!! */ char * lookup (int day) { return (day==0 ? "Sunday" : day==1 ? "Monday" : day==2 ? "Tuesday" : day==3 ? "Wednesday" : day==4 ? "Thursday" : day==5 ? "Friday" : "Saturday") ; } /* Function JulianToGregorian. Takes 4 arguments: Julian day number, */ /* and dd, mm, and yyyy for a Gregorian date. Function calculates */ /* Gregorian date corresponding to JDN and stores it in dd, mm, yyyy */ /* Note: this function makes use of a widely-published algorithm. */ /* Unfortunately, it seems to have been optimsed for efficiency */ /* rather than for comprehensibility. There's a lesson here: */ /* if you don't need to understand something, don't. */ void JulianToGregorian (long jd, int * dd, int * mm, int * yyyy) { long temp, i, n, j ; temp = jd + 68569 ; n = (4 * temp) / 146097 ; temp = temp - (146097 * n + 3) / 4 ; i = (4000 * (temp + 1)) / 1461001 ; temp = temp - (1461 * i) / 4 + 31 ; j = (80 * temp) / 2447 ; *dd = temp - (2447 * j) / 80 ; temp = j / 11 ; *mm = j + 2 - (12 * temp) ; *yyyy = 100 * (n - 49) + i + temp ; }