NivaarExam PrepOfficial exam papers ↗

25-Comp-A4 Program Design and Data Structures · May 2016

Question 1 of 8: Programming — Horoscope Sign and Element Compatibility

Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)

Notes on this paper

Paper format. 98-Comp-A4 Program Design and Data Structures, May 2016 — 3 hours, closed book, no calculator permitted. Eight questions of equal weight (20 marks each: some split as (a) 10 + (b) 10); candidates answer any five, so a complete paper is 100 marks. Pseudocode or any high-level language is accepted, and the examiner's note states explicitly that marking emphasises the operation of the program, not syntactic details. All eight questions are answered below, because the whole set is the more useful revision resource. Answers are given in C or C++ as the question dictates; each is compilable as written, but a clear, correctly reasoned pseudocode answer would earn the same marks.

Reference texts for this subject.

  • Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms, 4th ed. — tree traversals (ch. 12), sorting and Quicksort (ch. 7), asymptotic analysis (ch. 3).
  • Weiss, Data Structures and Algorithm Analysis in C, 2nd ed. — linked lists (ch. 3), binary search trees (ch. 4).
  • Deitel & Deitel, C++ How to Program, 10th ed. — class design and templates (ch. 9–12), file streams (ch. 14).
  • Kernighan & Ritchie, The C Programming Language, 2nd ed. — character/array I/O idioms (ch. 1, 7), pointers and structures (ch. 5–6).
  • Stroustrup, The C++ Programming Language, 4th ed. — class templates and value semantics (ch. 3, 25–27).

The Computer Engineering citation list is built around architecture and networking texts (Patterson & Hennessy, Tanenbaum, Mano); this subject is programming and data structures, so the works above are cited instead.

Question 1: Programming — Horoscope Sign and Element Compatibility (20 marks: (a) 10, (b) 10)

Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.

Given. Twelve zodiac date ranges, each with an inclusive start and end date within the (leap-year-independent) civil calendar, and a grouping of the twelve signs into four 3-sign Elements.

Find. A program that (a) validates a month/day pair and prints its sign, or reports Invalid birthdate; and (b) also prints the other two signs in the same Element.

Approach. Encode each sign's start date as a single sortable key (month*100 + day) in a small table ordered by calendar date; validate the day against a fixed days-per-month table, then scan the table for the first entry whose key is ≥ the input's key. Element membership is a second, independent lookup table of the same twelve signs.

(a) Determine the sign from a birthdate (10 marks)

  1. Turn twelve overlapping "start–end" ranges into one ordered boundary table. Because the ranges partition the year with no gaps, each sign is fully identified by the date at which it ends: reading the twelve end-dates in calendar order (Jan 19 Capricorn, Feb 18 Aquarius, Mar 20 Pisces, …, Dec 21 Sagittarius) and closing the table with a thirteenth Dec 31 Capricorn sentinel gives a table that a birthdate can be matched against with one linear scan — no special-casing of the one range (Capricorn) that wraps New Year's Day, since Capricorn simply appears at both the very start and the very end of the table.
  2. Validate the date before looking anything up. A month must lie in 1–12, and a day must not exceed that month's length. No year is given, so there is no way to test for a leap year; February is taken as 28 days, matching ordinary usage when a year is unspecified. This resolves the paper's own example: 2 30 is invalid regardless of the leap convention, since 30 exceeds even a leap February's 29 days.
  3. Write the program.
    #include <stdio.h>
    #include <string.h>   /* strcmp(), used by part (b) */
    
    #define N_SIGNS 13
    
    /* Upper-bound key (month*100+day) of each sign's date range, and its name.
       The list is in calendar order starting from Jan 1; Capricorn appears
       twice (start-of-year tail and end-of-year run) so the table never needs
       to treat the December-to-January wraparound as a special case. */
    static const int  upper_key[N_SIGNS] =
        {119, 218, 320, 419, 520, 621, 722, 822, 922, 1022, 1121, 1221, 1231};
    static const char *sign_name[N_SIGNS] =
        {"Capricorn", "Aquarius", "Pisces", "Aries", "Taurus", "Gemini",
         "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn"};
    static const int days_in_month[12] =
        {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    
    /* Returns an index into sign_name[], or -1 if the birthdate is invalid. */
    int horoscope_sign(int month, int day)
    {
        int key, i;
    
        if (month < 1 || month > 12)
            return -1;
        if (day < 1 || day > days_in_month[month - 1])
            return -1;
    
        key = month * 100 + day;
        for (i = 0; i < N_SIGNS; i++)
            if (key <= upper_key[i])
                return i;
        return -1;   /* unreachable: 1231 covers every valid date */
    }
    
    int main(void)
    {
        int month, day, idx;
    
        printf("Enter birthdate: ");
        if (scanf("%d %d", &month, &day) != 2) {
            printf("Invalid birthdate\n");
            return 1;
        }
    
        idx = horoscope_sign(month, day);
        if (idx < 0)
            printf("Invalid birthdate\n");
        else
            printf("Sign is %s\n", sign_name[idx]);
    
        return 0;
    }
  4. Check the printed examples. 10 18 gives key 1018, which is ≤ 1022 (Libra's upper bound) and > 922 (Virgo's), so the scan stops at Libra — matching “Sign is Libra”. 1 12 gives key 112, ≤ 119, so it stops at the first table entry, Capricorn. 2 30 fails validation outright (30 > 28) before the table is even consulted. $$\boxed{\text{horoscope\_sign}(10,18)=\text{Libra},\ \ (1,12)=\text{Capricorn},\ \ (2,30)=\text{Invalid}}$$

(b) Print the two most-compatible signs (10 marks)

  1. Add a second table, independent of the first. The Element grouping is not a function of the date arithmetic in part (a) — it is a separate fact about each of the twelve signs — so it is looked up by sign name (or by re-using the same index into sign_name[]) rather than re-derived from the birthdate.
    /* Element of sign_name[i], parallel to that array. */
    static const char *element_of[N_SIGNS] =
        {"EARTH", "AIR", "WATER", "FIRE", "EARTH", "AIR",
         "WATER", "FIRE", "EARTH", "AIR", "WATER", "FIRE", "EARTH"};
    
    /* Extends main(): after printing the sign, print the other two signs
       sharing its Element (skipping the sign itself and its Capricorn twin
       at index 12, which is the same sign as index 0). */
    void print_compatible(int idx)
    {
        int i;
        printf("Most compatible with: ");
        int twin = (idx == 12) ? 0 : idx;             /* Capricorn's other table slot */
        for (i = 0; i < N_SIGNS - 1; i++) {           /* stop before the Capricorn twin */
            /* strcmp() compares the whole Element name, so no two different
               Elements can match on a shared first letter. */
            if (i != idx && i != twin &&
                strcmp(element_of[i], element_of[idx]) == 0)
                printf("%s ", sign_name[i]);
        }
        printf("\n");
    }
    The i != idx && i != twin guard is what stops a sign from listing itself as its own compatible match: Capricorn occupies two table slots that must be treated as one sign, so a late-December date (which the scan resolves to index 12) has to exclude index 0 as well as its own index, or it would print Capricorn as one of its own two matches.
  2. Confirm against the question's own grouping. Aries is FIRE, so its two companions are Leo and Sagittarius; Cancer is WATER, so its companions are Pisces and Scorpio. $$\boxed{\text{compatible}(\text{Aries})=\{\text{Leo},\text{Sagittarius}\},\ \ \text{compatible}(\text{Cancer})=\{\text{Pisces},\text{Scorpio}\}}$$
Question 1 — results
BirthdateSignCompatible with
10 18LibraGemini, Aquarius
1 12CapricornTaurus, Virgo
2 30Invalid birthdate—
3 21AriesLeo, Sagittarius
6 22CancerPisces, Scorpio
← Paper overview