% Simple script that prints a calendar for any year according to the Gregorian Calendar
%
% Author:  Charles Riedesel

year  = input('Enter year yyyy: ');

% ----------------------------------------------------------------------
% Leap Day?  True if divisble by 400 or (divisible by 4 but not 100)

leap_day = mod(year,400) == 0 || (mod(year,100) ~= 0 && mod(year,4) == 0);

% ----------------------------------------------------------------------
% Each non-leap year moves Jan. 1 one day later in the week, while a leap year moves
% Jan. 1 two days later in the week.  Calculate the total number of days to shift 
% starting with year 1, which is the year plus the number of leap years.

leap_days = year + floor(year/4) - floor(year/100) + floor(year/400) - leap_day;

% ----------------------------------------------------------------------
% Find offsets for each month starting with January (with no offset).  Build an 
% array using modular arithmetic to determine the number of days (1 to 7) each month
% must be offset.

days = [0 31 28+leap_day 31 30 31 30 31 31 30 31 30 31];    % days per month
offs = zeros(1,13);
offs(1) = mod(leap_days, 7);                                % January offset
for ii = 2:13
    offs(ii) = offs(ii-1)+days(ii);                         % remaining offsets
end
offs = mod(offs, 7) + 1;                                    % mod one more time

% ----------------------------------------------------------------------
% Offset the offsets so that each of the 12 months starts with a negative number
% which will count up as each date is printed.  The idea is to print blanks until
% the date reaches 1, then blanks again after surpassing number of days in the month

date = -offs + 2;

% ----------------------------------------------------------------------
% Day and month headers

day_names = ['Su';'Mo';'Tu';'We';'Th';'Fr';'Sa'];
mon_names = ['Jan';'Feb';'Mar';'Apr';'May';'Jun';'Jul';'Aug';'Sep';'Oct';'Nov';'Dec'];

% ----------------------------------------------------------------------
% Repeat for each group of three months in a 3 x 4 grid of months

for months = 1:3:10

    % ------------------------------------------------------------------
    % Print the three month names for each group of months in the grid

    for month = months:months+2
        fprintf('         %s           ', mon_names(month,:));
    end
    fprintf('\n');

    % ------------------------------------------------------------------
    % Print names of days for each of the three months in the grid

    for month = months:months+2
        for day = 1:7
            fprintf(' %s', day_names(day,:)); 
        end
        fprintf('  ');
    end
    fprintf('\n');

    % ------------------------------------------------------------------
    % Allow six lines to print out all the days of the three months

    for week = 1:6
        for month = months:months+2
             for day = 1:7

                  % ----------------------------------------------------
                  % Tricky!  Only print the day number if it is inthe
                  % range of 1 to the number of days in that month.
                  % Otherwise pad with spaces.

                  if date(month) > 0 && date(month) <= days(month+1)
                      fprintf(' %2d', date(month));
                  else
                      fprintf('   ');
                  end

                  % ----------------------------------------------------
                  % Advance to the next day of the month being printed

                  date(month) = date(month) + 1;
             end
             fprintf('  ');     % Advance to next month in the grid
        end
        fprintf('\n');          % Drop to next week of the month
    end
    fprintf('\n');              % Drop an extra line to next set of months

% ----------------------------------------------------------------------
% Continue to next group of three months in the 3 x 4 grid, or end

end

