/* Swedish Christmas/New Year vacation in Picat. In Sweden, the following days are considered holidays: - Dec 24 Christmas Eve (this is the day we distribute Christmas gifts) - Dec 25 Christmas Day - Dec 26 Boxing Day - Dec 31 New Year's Eve - Jan 1 New Year's Day - Jan 6 Thirteen's Day (Epiphany) White collar workers usually don't work on Saturdays or Sundays. Since I'm a white collar person, hence I consider these two days to be free. The query: What years are optimal in terms of free days compared to the days needed to take vacation in order to get the longest contiguous vacation (including Saturdays and Sundays)? Note: The program don't use any fancy optimization, just calculating the dates and try to get a "reasonably long" contiguous Xmas free time. Summary of the different Xmas days years: - xmas: The day of Xmas Eve - FirstFreeDay: The first free day (Saturday or Xmas Eve) - LastFreeDay: 6'th january or later if there are only holidays (Sat/Sun) - RangeLen: number of days between FirstFreeDays and LastFreeDay - NumFreeDays: Number of free days in the range - NumNonFreeDays: Number of non-free days in the range (the amount one have to take vacation) - Ratio: the number of number free days divided by the total number of days (including vacation), higher is better * Here is the order of years from 2013 and onward (skipping duplicate Xmas Eve Days): [xmas=tue,firstFree=[2013,12,21],lastFree=[2014,1,6],rangeLen=17,numFreeDays=14,numNonFreeDays=3,xmasRatio=82.35%] [xmas=wed,firstFree=[2014,12,20],lastFree=[2015,1,6],rangeLen=18,numFreeDays=14,numNonFreeDays=4,xmasRatio=77.78%] [xmas=thu,firstFree=[2015,12,19],lastFree=[2016,1,6],rangeLen=19,numFreeDays=13,numNonFreeDays=6,xmasRatio=68.42%] [xmas=sat,firstFree=[2016,12,24],lastFree=[2017,1,8],rangeLen=16,numFreeDays=10,numNonFreeDays=6,xmasRatio=62.50%] [xmas=sun,firstFree=[2017,12,23],lastFree=[2018,1,7],rangeLen=16,numFreeDays=11,numNonFreeDays=5,xmasRatio=68.75%] [xmas=mon,firstFree=[2018,12,22],lastFree=[2019,1,6],rangeLen=16,numFreeDays=13,numNonFreeDays=3,xmasRatio=81.25%] [xmas=fri,firstFree=[2021,12,24],lastFree=[2022,1,6],rangeLen=14,numFreeDays=11,numNonFreeDays=3,xmasRatio=78.57%] * Ordered by Ratio (higher better) [xmas=tue,firstFree=[2013,12,21],lastFree=[2014,1,6],rangeLen=17,numFreeDays=14,numNonFreeDays=3,xmasRatio=82.35%] [xmas=mon,firstFree=[2018,12,22],lastFree=[2019,1,6],rangeLen=16,numFreeDays=13,numNonFreeDays=3,xmasRatio=81.25%] [xmas=fri,firstFree=[2021,12,24],lastFree=[2022,1,6],rangeLen=14,numFreeDays=11,numNonFreeDays=3,xmasRatio=78.57%] [xmas=wed,firstFree=[2014,12,20],lastFree=[2015,1,6],rangeLen=18,numFreeDays=14,numNonFreeDays=4,xmasRatio=77.78%] [xmas=sun,firstFree=[2017,12,23],lastFree=[2018,1,7],rangeLen=16,numFreeDays=11,numNonFreeDays=5,xmasRatio=68.75%] [xmas=thu,firstFree=[2015,12,19],lastFree=[2016,1,6],rangeLen=19,numFreeDays=13,numNonFreeDays=6,xmasRatio=68.42%] [xmas=sat,firstFree=[2016,12,24],lastFree=[2017,1,8],rangeLen=16,numFreeDays=10,numNonFreeDays=6,xmasRatio=62.50%] * Ordered by the total length of the range ("RangeLen") [xmas=thu,firstFree=[2015,12,19],lastFree=[2016,1,6],rangeLen=19,numFreeDays=13,numNonFreeDays=6,xmasRatio=68.42%] [xmas=wed,firstFree=[2014,12,20],lastFree=[2015,1,6],rangeLen=18,numFreeDays=14,numNonFreeDays=4,xmasRatio=77.78%] [xmas=tue,firstFree=[2013,12,21],lastFree=[2014,1,6],rangeLen=17,numFreeDays=14,numNonFreeDays=3,xmasRatio=82.35%] [xmas=mon,firstFree=[2018,12,22],lastFree=[2019,1,6],rangeLen=16,numFreeDays=13,numNonFreeDays=3,xmasRatio=81.25%] [xmas=sun,firstFree=[2017,12,23],lastFree=[2018,1,7],rangeLen=16,numFreeDays=11,numNonFreeDays=5,xmasRatio=68.75%] [xmas=sat,firstFree=[2016,12,24],lastFree=[2017,1,8],rangeLen=16,numFreeDays=10,numNonFreeDays=6,xmasRatio=62.50%] [xmas=fri,firstFree=[2021,12,24],lastFree=[2022,1,6],rangeLen=14,numFreeDays=11,numNonFreeDays=3,xmasRatio=78.57%] * Ordered by the number of "vacation free" days in the range ("numFreeDays") [xmas=wed,firstFree=[2014,12,20],lastFree=[2015,1,6],rangeLen=18,numFreeDays=14,numNonFreeDays=4,xmasRatio=77.78%] [xmas=tue,firstFree=[2013,12,21],lastFree=[2014,1,6],rangeLen=17,numFreeDays=14,numNonFreeDays=3,xmasRatio=82.35%] [xmas=thu,firstFree=[2015,12,19],lastFree=[2016,1,6],rangeLen=19,numFreeDays=13,numNonFreeDays=6,xmasRatio=68.42%] [xmas=mon,firstFree=[2018,12,22],lastFree=[2019,1,6],rangeLen=16,numFreeDays=13,numNonFreeDays=3,xmasRatio=81.25%] [xmas=sun,firstFree=[2017,12,23],lastFree=[2018,1,7],rangeLen=16,numFreeDays=11,numNonFreeDays=5,xmasRatio=68.75%] [xmas=fri,firstFree=[2021,12,24],lastFree=[2022,1,6],rangeLen=14,numFreeDays=11,numNonFreeDays=3,xmasRatio=78.57%] [xmas=sat,firstFree=[2016,12,24],lastFree=[2017,1,8],rangeLen=16,numFreeDays=10,numNonFreeDays=6,xmasRatio=62.50%] From this we can conclude that Tuesday years (like 2013) are the best, and Saturday years (like 2016) are the worst. (Darn, I missed that great 2013 opportunity...) This Picat model was created by Hakan Kjellerstrand, hakank@gmail.com See also my Picat page: http://www.hakank.org/picat/ */ import util. main => go. go => Days = new_map([0=sun,1=mon,2=tue,3=wed,4=thu,5=fri,6=sat]), StartYear = 2013, EndYear = 2050, % % Here we only consider the 7 different weekdays for Christmas Eve. % We try to find a reasonably valid scheme of days to start the Xmas % vacation. % Holidays = new_map([[12,24]=1,[12,25]=1,[12,26]=1,[12,31]=1,[1,1]=1,[1,6]=1]), XmasDay = new_map(), Summary = [], % Continue to check until all Xmas weekdays has been checked foreach(Year in StartYear..EndYear, length(XmasDay.keys()) < 7) DowNum=dow(Year, 12,24), % day of week (number) Dow = Days.get(DowNum), % day of week (symbol) % We haven't seen this weekday if not XmasDay.has_key(Dow) then println(checking=[Dow,Year]), % the days to go go back from Xmas Eve (kind of heuristic) Back = (DowNum+1) mod 6, println([dow=Dow,dowNum=DowNum,back=Back]), J = g2j(Year,12,24-Back), % convert to Julian day NumFreeDays = 0, % number of free days: Xmas/New Year's related Found13Day = false, % for calculating the LastFreeDay LastFreeDayCheck = true, FirstFreeDay = _, % first free day LastFreeDay = _, % last free day after 13'th eve DayStatus = [], InRange = _, % range of free days (including Sat and Sun) foreach(JD in J..J+24) G = j2g(JD), % convert back to Gregorian date [ThisYear,ThisMonth,ThisDay] = G, FreeP = "Not free", % Free/Not Free indicator Dow2 = dow(ThisYear,ThisMonth,ThisDay), % weekday of "this" day Status = _, % Is this a free day? if membchk(Days.get(Dow2),[sat,sun]) ; Holidays.has_key([ThisMonth,ThisDay]) then NumFreeDays := NumFreeDays + 1, FreeP := "Free!", if var(FirstFreeDay) then FirstFreeDay := G, InRange := true end, if ThisMonth == 1, ThisDay == 6 then Found13Day := true end, if Found13Day == true, LastFreeDayCheck == true then LastFreeDay := G end, Status := free else Status = not_free, if Found13Day then LastFreeDayCheck := false, InRange := false end end, if nonvar(FirstFreeDay), LastFreeDayCheck == true then DayStatus := DayStatus ++ [G=Status] end, if InRange == true then println([G,Days.get(Dow2),FreeP]) end end, println(firstFreeDay=FirstFreeDay), println(lastFreeDay=LastFreeDay), HolidayRangeLen = DayStatus.length, println(holidayRangeLen=HolidayRangeLen), println(numFreeDays=NumFreeDays), NumNonFreeDays = DayStatus.length-NumFreeDays, println(numNonFreeDays=NumNonFreeDays), XmasRatio = to_fstring("%2.2f%%", 100*NumFreeDays / HolidayRangeLen), Summary := Summary ++ [[xmas=Dow,firstFree=FirstFreeDay,lastFree=LastFreeDay,rangeLen=HolidayRangeLen,numFreeDays=NumFreeDays,numNonFreeDays=NumNonFreeDays, xmasRatio=XmasRatio]] end, XmasDay.put(Dow,XmasDay.get(Dow,0)+1), nl end, foreach(Row in Summary) println(Row) end, nl. % % http://en.wikipedia.org/wiki/Julian_day % gregorian2julian % g2j(Year,Month,Day) = JD => A = floor((14-Month) / 12), % 1 for Jan or Feb, 0 for other months Y = Year + 4800 - A, M = Month + 12*A - 3, % 0 for Mars, 11 for Feb JD = Day + floor( (153*M + 2) / 5) + 365*Y + floor(Y/4) - floor(Y / 100) + floor(Y / 400) - 32045. % % julian2gregorian % j2g(JD) = Date => Y=4716, V=3, J=1401, U=5, M=2, S=153, N=12, W=2, R=4, B=274277, P=1461, C= -38, F = JD + J + (((4 * JD + B) div 146097) * 3) div 4 + C, E = R * F + V, G = mod(E, P) div R, H = U * G + W, Day = (mod(H, S)) div U + 1, Month = mod(H div S + M, N) + 1, Year = (E div P) - Y + (N + M - Month) div N, Date = [Year,Month,Day]. % % Day of week, Sakamoto's method % http://en.wikipedia.org/wiki/Weekday_determination#Sakamoto.27s_Method % dow(Y, M, D) = R => T = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4], if M < 3 then Y := Y - 1 end, R = (Y + Y // 4 - Y // 100 + Y // 400 + T[M] + D) mod 7.