xref: /netbsd-src/external/bsd/ntp/dist/tests/libntp/calendar.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: calendar.c,v 1.3 2024/08/18 20:47:26 christos Exp $	*/
2067f5680Schristos 
3f17b710fSchristos #include "config.h"
4f17b710fSchristos 
5a6f3f22fSchristos #include "ntp_stdlib.h" /* test fail without this include, for some reason */
6f17b710fSchristos #include "ntp_calendar.h"
7067f5680Schristos #include "ntp_calgps.h"
85645e8e7Schristos #include "ntp_unixtime.h"
9067f5680Schristos #include "ntp_fp.h"
10f17b710fSchristos #include "unity.h"
11f17b710fSchristos 
12f17b710fSchristos #include <string.h>
13f17b710fSchristos 
14067f5680Schristos static char mbuf[128];
15067f5680Schristos 
16f17b710fSchristos static int leapdays(int year);
17f17b710fSchristos 
184c290c01Schristos void	setUp(void);
19a6f3f22fSchristos int	isGT(int first, int second);
20a6f3f22fSchristos int	leapdays(int year);
21a6f3f22fSchristos char *	CalendarFromCalToString(const struct calendar *cal);
22a6f3f22fSchristos char *	CalendarFromIsoToString(const struct isodate *iso);
23a6f3f22fSchristos int	IsEqualCal(const struct calendar *expected, const struct calendar *actual);
24a6f3f22fSchristos int	IsEqualIso(const struct isodate *expected, const struct isodate *actual);
25a6f3f22fSchristos char *	DateFromCalToString(const struct calendar *cal);
26a6f3f22fSchristos char *	DateFromIsoToString(const struct isodate *iso);
27a6f3f22fSchristos int	IsEqualDateCal(const struct calendar *expected, const struct calendar *actual);
28a6f3f22fSchristos int	IsEqualDateIso(const struct isodate *expected, const struct isodate *actual);
295645e8e7Schristos 
30067f5680Schristos void	test_Constants(void);
31a6f3f22fSchristos void	test_DaySplitMerge(void);
32067f5680Schristos void	test_WeekSplitMerge(void);
33a6f3f22fSchristos void	test_SplitYearDays1(void);
34a6f3f22fSchristos void	test_SplitYearDays2(void);
35067f5680Schristos void	test_SplitEraDays(void);
36067f5680Schristos void	test_SplitEraWeeks(void);
37a6f3f22fSchristos void	test_RataDie1(void);
38a6f3f22fSchristos void	test_LeapYears1(void);
39a6f3f22fSchristos void	test_LeapYears2(void);
40067f5680Schristos void	test_LeapYears3(void);
41a6f3f22fSchristos void	test_RoundTripDate(void);
42a6f3f22fSchristos void	test_RoundTripYearStart(void);
43a6f3f22fSchristos void	test_RoundTripMonthStart(void);
44a6f3f22fSchristos void	test_RoundTripWeekStart(void);
45a6f3f22fSchristos void	test_RoundTripDayStart(void);
46a6f3f22fSchristos void	test_IsoCalYearsToWeeks(void);
47a6f3f22fSchristos void	test_IsoCalWeeksToYearStart(void);
48a6f3f22fSchristos void	test_IsoCalWeeksToYearEnd(void);
49a6f3f22fSchristos void	test_DaySecToDate(void);
50067f5680Schristos void	test_GpsRollOver(void);
51067f5680Schristos void	test_GpsRemapFunny(void);
52f17b710fSchristos 
53067f5680Schristos void	test_GpsNtpFixpoints(void);
545645e8e7Schristos void	test_NtpToNtp(void);
555645e8e7Schristos void	test_NtpToTime(void);
564c290c01Schristos 
57067f5680Schristos void	test_CalUMod7(void);
58067f5680Schristos void	test_CalIMod7(void);
59067f5680Schristos void	test_RellezCentury1_1(void);
60067f5680Schristos void	test_RellezCentury3_1(void);
61067f5680Schristos void	test_RellezYearZero(void);
62067f5680Schristos 
63067f5680Schristos 
644c290c01Schristos void
654c290c01Schristos setUp(void)
664c290c01Schristos {
674c290c01Schristos 	init_lib();
684c290c01Schristos 
694c290c01Schristos 	return;
704c290c01Schristos }
714c290c01Schristos 
724c290c01Schristos 
73a6f3f22fSchristos /*
74a6f3f22fSchristos  * ---------------------------------------------------------------------
75a6f3f22fSchristos  * test support stuff
76a6f3f22fSchristos  * ---------------------------------------------------------------------
77a6f3f22fSchristos  */
78a6f3f22fSchristos int
79a6f3f22fSchristos isGT(int first, int second)
80a6f3f22fSchristos {
81f17b710fSchristos 	if(first > second) {
82f17b710fSchristos 		return TRUE;
83a6f3f22fSchristos 	} else {
84a6f3f22fSchristos 		return FALSE;
85a6f3f22fSchristos 	}
86f17b710fSchristos }
87f17b710fSchristos 
88a6f3f22fSchristos int
89a6f3f22fSchristos leapdays(int year)
90f17b710fSchristos {
91f17b710fSchristos 	if (year % 400 == 0)
92f17b710fSchristos 		return 1;
93f17b710fSchristos 	if (year % 100 == 0)
94f17b710fSchristos 		return 0;
95f17b710fSchristos 	if (year % 4 == 0)
96f17b710fSchristos 		return 1;
97f17b710fSchristos 	return 0;
98f17b710fSchristos }
99f17b710fSchristos 
100a6f3f22fSchristos char *
101a6f3f22fSchristos CalendarFromCalToString(
102a6f3f22fSchristos     const struct calendar *cal)
103a6f3f22fSchristos {
104a6f3f22fSchristos 	char * str = malloc(sizeof (char) * 100);
105a6f3f22fSchristos 	snprintf(str, 100, "%u-%02u-%02u (%u) %02u:%02u:%02u",
106a6f3f22fSchristos 		 cal->year, (u_int)cal->month, (u_int)cal->monthday,
107a6f3f22fSchristos 		 cal->yearday,
108a6f3f22fSchristos 		 (u_int)cal->hour, (u_int)cal->minute, (u_int)cal->second);
109a6f3f22fSchristos 	str[99] = '\0'; /* paranoia rulez! */
110a6f3f22fSchristos 	return str;
111f17b710fSchristos }
112f17b710fSchristos 
113a6f3f22fSchristos char *
114a6f3f22fSchristos CalendarFromIsoToString(
115a6f3f22fSchristos 	const struct isodate *iso)
116a6f3f22fSchristos {
117a6f3f22fSchristos 	char * str = emalloc (sizeof (char) * 100);
118a6f3f22fSchristos 	snprintf(str, 100, "%u-W%02u-%02u %02u:%02u:%02u",
119a6f3f22fSchristos 		 iso->year, (u_int)iso->week, (u_int)iso->weekday,
120a6f3f22fSchristos 		 (u_int)iso->hour, (u_int)iso->minute, (u_int)iso->second);
121a6f3f22fSchristos 	str[99] = '\0'; /* paranoia rulez! */
122a6f3f22fSchristos 	return str;
123f17b710fSchristos }
124f17b710fSchristos 
125a6f3f22fSchristos int
126a6f3f22fSchristos IsEqualCal(
127a6f3f22fSchristos 	const struct calendar *expected,
128a6f3f22fSchristos 	const struct calendar *actual)
129a6f3f22fSchristos {
130a6f3f22fSchristos 	if (expected->year == actual->year &&
131a6f3f22fSchristos 	    (!expected->yearday || expected->yearday == actual->yearday) &&
132a6f3f22fSchristos 	    expected->month == actual->month &&
133a6f3f22fSchristos 	    expected->monthday == actual->monthday &&
134a6f3f22fSchristos 	    expected->hour == actual->hour &&
135a6f3f22fSchristos 	    expected->minute == actual->minute &&
136a6f3f22fSchristos 	    expected->second == actual->second) {
137f17b710fSchristos 		return TRUE;
138f17b710fSchristos 	} else {
1394c290c01Schristos 		char *p_exp = CalendarFromCalToString(expected);
1404c290c01Schristos 		char *p_act = CalendarFromCalToString(actual);
1414c290c01Schristos 
1424c290c01Schristos 		printf("expected: %s but was %s", p_exp, p_act);
1434c290c01Schristos 
1444c290c01Schristos 		free(p_exp);
1454c290c01Schristos 		free(p_act);
1464c290c01Schristos 
147f17b710fSchristos 		return FALSE;
148f17b710fSchristos 	}
149f17b710fSchristos }
150f17b710fSchristos 
151a6f3f22fSchristos int
152a6f3f22fSchristos IsEqualIso(
153a6f3f22fSchristos 	const struct isodate *expected,
154a6f3f22fSchristos 	const struct isodate *actual)
155a6f3f22fSchristos {
156a6f3f22fSchristos 	if (expected->year == actual->year &&
157a6f3f22fSchristos 	    expected->week == actual->week &&
158a6f3f22fSchristos 	    expected->weekday == actual->weekday &&
159a6f3f22fSchristos 	    expected->hour == actual->hour &&
160a6f3f22fSchristos 	    expected->minute == actual->minute &&
161a6f3f22fSchristos 	    expected->second == actual->second) {
162f17b710fSchristos 		return TRUE;
163f17b710fSchristos 	} else {
164a6f3f22fSchristos 		printf("expected: %s but was %s",
165a6f3f22fSchristos 		       CalendarFromIsoToString(expected),
166a6f3f22fSchristos 		       CalendarFromIsoToString(actual));
167f17b710fSchristos 		return FALSE;
168f17b710fSchristos 	}
169f17b710fSchristos }
170f17b710fSchristos 
171a6f3f22fSchristos char *
172a6f3f22fSchristos DateFromCalToString(
173a6f3f22fSchristos 	const struct calendar *cal)
174a6f3f22fSchristos {
175f17b710fSchristos 
176a6f3f22fSchristos 	char * str = emalloc (sizeof (char) * 100);
177a6f3f22fSchristos 	snprintf(str, 100, "%u-%02u-%02u (%u)",
178a6f3f22fSchristos 		 cal->year, (u_int)cal->month, (u_int)cal->monthday,
179a6f3f22fSchristos 		 cal->yearday);
180a6f3f22fSchristos 	str[99] = '\0'; /* paranoia rulez! */
181a6f3f22fSchristos 	return str;
182f17b710fSchristos }
183f17b710fSchristos 
184a6f3f22fSchristos char *
185a6f3f22fSchristos DateFromIsoToString(
186a6f3f22fSchristos 	const struct isodate *iso)
187a6f3f22fSchristos {
188f17b710fSchristos 
189a6f3f22fSchristos 	char * str = emalloc (sizeof (char) * 100);
190a6f3f22fSchristos 	snprintf(str, 100, "%u-W%02u-%02u",
191a6f3f22fSchristos 		 iso->year, (u_int)iso->week, (u_int)iso->weekday);
192a6f3f22fSchristos 	str[99] = '\0'; /* paranoia rulez! */
193a6f3f22fSchristos 	return str;
194f17b710fSchristos }
195f17b710fSchristos 
196a6f3f22fSchristos int/*BOOL*/
197a6f3f22fSchristos IsEqualDateCal(
198a6f3f22fSchristos 	const struct calendar *expected,
199a6f3f22fSchristos 	const struct calendar *actual)
200a6f3f22fSchristos {
201a6f3f22fSchristos 	if (expected->year == actual->year &&
202a6f3f22fSchristos 	    (!expected->yearday || expected->yearday == actual->yearday) &&
203a6f3f22fSchristos 	    expected->month == actual->month &&
204a6f3f22fSchristos 	    expected->monthday == actual->monthday) {
205f17b710fSchristos 		return TRUE;
206f17b710fSchristos 	} else {
207a6f3f22fSchristos 		printf("expected: %s but was %s",
208a6f3f22fSchristos 		       DateFromCalToString(expected),
209a6f3f22fSchristos 		       DateFromCalToString(actual));
210f17b710fSchristos 		return FALSE;
211f17b710fSchristos 	}
212f17b710fSchristos }
213f17b710fSchristos 
214a6f3f22fSchristos int/*BOOL*/
215a6f3f22fSchristos IsEqualDateIso(
216a6f3f22fSchristos 	const struct isodate *expected,
217a6f3f22fSchristos 	const struct isodate *actual)
218a6f3f22fSchristos {
219a6f3f22fSchristos 	if (expected->year == actual->year &&
220a6f3f22fSchristos 	    expected->week == actual->week &&
221a6f3f22fSchristos 	    expected->weekday == actual->weekday) {
222f17b710fSchristos 		return TRUE;
223f17b710fSchristos 	} else {
224a6f3f22fSchristos 		printf("expected: %s but was %s",
225a6f3f22fSchristos 		       DateFromIsoToString(expected),
226a6f3f22fSchristos 		       DateFromIsoToString(actual));
227f17b710fSchristos 		return FALSE;
228f17b710fSchristos 	}
229f17b710fSchristos }
230f17b710fSchristos 
231067f5680Schristos static int/*BOOL*/
232067f5680Schristos strToCal(
233067f5680Schristos 	struct calendar * jd,
234067f5680Schristos 	const char * str
235067f5680Schristos 	)
236067f5680Schristos {
237067f5680Schristos 	unsigned short y,m,d, H,M,S;
238067f5680Schristos 
239067f5680Schristos 	if (6 == sscanf(str, "%hu-%2hu-%2huT%2hu:%2hu:%2hu",
240067f5680Schristos 			&y, &m, &d, &H, &M, &S)) {
241067f5680Schristos 		memset(jd, 0, sizeof(*jd));
242067f5680Schristos 		jd->year     = y;
243067f5680Schristos 		jd->month    = (uint8_t)m;
244067f5680Schristos 		jd->monthday = (uint8_t)d;
245067f5680Schristos 		jd->hour     = (uint8_t)H;
246067f5680Schristos 		jd->minute   = (uint8_t)M;
247067f5680Schristos 		jd->second   = (uint8_t)S;
248067f5680Schristos 
249067f5680Schristos 		return TRUE;
250067f5680Schristos 	}
251067f5680Schristos 	return FALSE;
252067f5680Schristos }
253f17b710fSchristos 
254a6f3f22fSchristos /*
255a6f3f22fSchristos  * ---------------------------------------------------------------------
256a6f3f22fSchristos  * test cases
257a6f3f22fSchristos  * ---------------------------------------------------------------------
258a6f3f22fSchristos  */
259a6f3f22fSchristos 
260a6f3f22fSchristos /* days before month, with a full-year pad at the upper end */
261f17b710fSchristos static const u_short real_month_table[2][13] = {
262f17b710fSchristos 	/* -*- table for regular years -*- */
263f17b710fSchristos 	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
264f17b710fSchristos 	/* -*- table for leap years -*- */
265f17b710fSchristos 	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
266f17b710fSchristos };
267f17b710fSchristos 
268a6f3f22fSchristos /* days in month, with one month wrap-around at both ends */
269f17b710fSchristos static const u_short real_month_days[2][14] = {
270f17b710fSchristos 	/* -*- table for regular years -*- */
271f17b710fSchristos 	{ 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 },
272f17b710fSchristos 	/* -*- table for leap years -*- */
273f17b710fSchristos 	{ 31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31 }
274f17b710fSchristos };
275f17b710fSchristos 
276067f5680Schristos void
277067f5680Schristos test_Constants(void)
278067f5680Schristos {
279067f5680Schristos 	int32_t		rdn;
280067f5680Schristos 	struct calendar	jdn;
281067f5680Schristos 
282067f5680Schristos 	jdn.year     = 1900;
283067f5680Schristos 	jdn.month    = 1;
284067f5680Schristos 	jdn.monthday = 1;
285067f5680Schristos 	rdn = ntpcal_date_to_rd(&jdn);
286067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(DAY_NTP_STARTS, rdn, "(NTP EPOCH)");
287067f5680Schristos 
288067f5680Schristos 	jdn.year     = 1980;
289067f5680Schristos 	jdn.month    = 1;
290067f5680Schristos 	jdn.monthday = 6;
291067f5680Schristos 	rdn = ntpcal_date_to_rd(&jdn);
292067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(DAY_GPS_STARTS, rdn, "(GPS EPOCH)");
293067f5680Schristos }
294067f5680Schristos 
295a6f3f22fSchristos /* test the day/sec join & split ops, making sure that 32bit
296a6f3f22fSchristos  * intermediate results would definitely overflow and the hi DWORD of
297a6f3f22fSchristos  * the 'vint64' is definitely needed.
298a6f3f22fSchristos  */
299a6f3f22fSchristos void
3004c290c01Schristos test_DaySplitMerge(void)
3014c290c01Schristos {
302f17b710fSchristos 	int32 day,sec;
3034c290c01Schristos 
304f17b710fSchristos 	for (day = -1000000; day <= 1000000; day += 100) {
305f17b710fSchristos 		for (sec = -100000; sec <= 186400; sec += 10000) {
306a6f3f22fSchristos 			vint64		merge;
307a6f3f22fSchristos 			ntpcal_split	split;
308a6f3f22fSchristos 			int32		eday;
309a6f3f22fSchristos 			int32		esec;
310a6f3f22fSchristos 
311a6f3f22fSchristos 			merge = ntpcal_dayjoin(day, sec);
312a6f3f22fSchristos 			split = ntpcal_daysplit(&merge);
313a6f3f22fSchristos 			eday  = day;
314a6f3f22fSchristos 			esec  = sec;
315f17b710fSchristos 
316f17b710fSchristos 			while (esec >= 86400) {
317f17b710fSchristos 				eday += 1;
318f17b710fSchristos 				esec -= 86400;
319f17b710fSchristos 			}
320f17b710fSchristos 			while (esec < 0) {
321f17b710fSchristos 				eday -= 1;
322f17b710fSchristos 				esec += 86400;
323f17b710fSchristos 			}
324f17b710fSchristos 
325f17b710fSchristos 			TEST_ASSERT_EQUAL(eday, split.hi);
326f17b710fSchristos 			TEST_ASSERT_EQUAL(esec, split.lo);
327f17b710fSchristos 		}
328f17b710fSchristos 	}
3294c290c01Schristos 
3304c290c01Schristos 	return;
331f17b710fSchristos }
332f17b710fSchristos 
333a6f3f22fSchristos void
334067f5680Schristos test_WeekSplitMerge(void)
335067f5680Schristos {
336067f5680Schristos 	int32 wno,sec;
337067f5680Schristos 
338067f5680Schristos 	for (wno = -1000000; wno <= 1000000; wno += 100) {
339067f5680Schristos 		for (sec = -100000; sec <= 2*SECSPERWEEK; sec += 10000) {
340067f5680Schristos 			vint64		merge;
341067f5680Schristos 			ntpcal_split	split;
342067f5680Schristos 			int32		ewno;
343067f5680Schristos 			int32		esec;
344067f5680Schristos 
345067f5680Schristos 			merge = ntpcal_weekjoin(wno, sec);
346067f5680Schristos 			split = ntpcal_weeksplit(&merge);
347067f5680Schristos 			ewno  = wno;
348067f5680Schristos 			esec  = sec;
349067f5680Schristos 
350067f5680Schristos 			while (esec >= SECSPERWEEK) {
351067f5680Schristos 				ewno += 1;
352067f5680Schristos 				esec -= SECSPERWEEK;
353067f5680Schristos 			}
354067f5680Schristos 			while (esec < 0) {
355067f5680Schristos 				ewno -= 1;
356067f5680Schristos 				esec += SECSPERWEEK;
357067f5680Schristos 			}
358067f5680Schristos 
359067f5680Schristos 			TEST_ASSERT_EQUAL(ewno, split.hi);
360067f5680Schristos 			TEST_ASSERT_EQUAL(esec, split.lo);
361067f5680Schristos 		}
362067f5680Schristos 	}
363067f5680Schristos 
364067f5680Schristos 	return;
365067f5680Schristos }
366067f5680Schristos 
367067f5680Schristos void
3684c290c01Schristos test_SplitYearDays1(void)
3694c290c01Schristos {
370f17b710fSchristos 	int32 eyd;
3714c290c01Schristos 
372f17b710fSchristos 	for (eyd = -1; eyd <= 365; eyd++) {
373f17b710fSchristos 		ntpcal_split split = ntpcal_split_yeardays(eyd, 0);
374f17b710fSchristos 		if (split.lo >= 0 && split.hi >= 0) {
375a6f3f22fSchristos 			TEST_ASSERT_TRUE(isGT(12,split.hi));
376a6f3f22fSchristos 			TEST_ASSERT_TRUE(isGT(real_month_days[0][split.hi+1], split.lo));
377f17b710fSchristos 			int32 tyd = real_month_table[0][split.hi] + split.lo;
378f17b710fSchristos 			TEST_ASSERT_EQUAL(eyd, tyd);
379f17b710fSchristos 		} else
380f17b710fSchristos 			TEST_ASSERT_TRUE(eyd < 0 || eyd > 364);
381f17b710fSchristos 	}
3824c290c01Schristos 
3834c290c01Schristos 	return;
384f17b710fSchristos }
385f17b710fSchristos 
386a6f3f22fSchristos void
3874c290c01Schristos test_SplitYearDays2(void)
3884c290c01Schristos {
389f17b710fSchristos 	int32 eyd;
3904c290c01Schristos 
391f17b710fSchristos 	for (eyd = -1; eyd <= 366; eyd++) {
392f17b710fSchristos 		ntpcal_split split = ntpcal_split_yeardays(eyd, 1);
393f17b710fSchristos 		if (split.lo >= 0 && split.hi >= 0) {
394a6f3f22fSchristos 			/* basic checks do not work on compunds :( */
395a6f3f22fSchristos 			/* would like: TEST_ASSERT_TRUE(12 > split.hi); */
396a6f3f22fSchristos 			TEST_ASSERT_TRUE(isGT(12,split.hi));
397a6f3f22fSchristos 			TEST_ASSERT_TRUE(isGT(real_month_days[1][split.hi+1], split.lo));
398f17b710fSchristos 			int32 tyd = real_month_table[1][split.hi] + split.lo;
399f17b710fSchristos 			TEST_ASSERT_EQUAL(eyd, tyd);
400f17b710fSchristos 		} else
401f17b710fSchristos 			TEST_ASSERT_TRUE(eyd < 0 || eyd > 365);
402f17b710fSchristos 		}
4034c290c01Schristos 
4044c290c01Schristos 	return;
405f17b710fSchristos }
406f17b710fSchristos 
407a6f3f22fSchristos void
408067f5680Schristos test_SplitEraDays(void)
409067f5680Schristos {
410067f5680Schristos 	int32_t		ed, rd;
411067f5680Schristos 	ntpcal_split	sd;
412067f5680Schristos 	for (ed = -10000; ed < 1000000; ++ed) {
413067f5680Schristos 		sd = ntpcal_split_eradays(ed, NULL);
414067f5680Schristos 		rd = ntpcal_days_in_years(sd.hi) + sd.lo;
415067f5680Schristos 		TEST_ASSERT_EQUAL(ed, rd);
416067f5680Schristos 		TEST_ASSERT_TRUE(0 <= sd.lo && sd.lo <= 365);
417067f5680Schristos 	}
418067f5680Schristos }
419067f5680Schristos 
420067f5680Schristos void
421067f5680Schristos test_SplitEraWeeks(void)
422067f5680Schristos {
423067f5680Schristos 	int32_t		ew, rw;
424067f5680Schristos 	ntpcal_split	sw;
425067f5680Schristos 	for (ew = -10000; ew < 1000000; ++ew) {
426067f5680Schristos 		sw = isocal_split_eraweeks(ew);
427067f5680Schristos 		rw = isocal_weeks_in_years(sw.hi) + sw.lo;
428067f5680Schristos 		TEST_ASSERT_EQUAL(ew, rw);
429067f5680Schristos 		TEST_ASSERT_TRUE(0 <= sw.lo && sw.lo <= 52);
430067f5680Schristos 	}
431067f5680Schristos }
432067f5680Schristos 
433067f5680Schristos void
4344c290c01Schristos test_RataDie1(void)
4354c290c01Schristos {
436a6f3f22fSchristos 	int32	 testDate = 1; /* 0001-01-01 (proleptic date) */
437f17b710fSchristos 	struct calendar expected = { 1, 1, 1, 1 };
438f17b710fSchristos 	struct calendar actual;
439f17b710fSchristos 
440f17b710fSchristos 	ntpcal_rd_to_date(&actual, testDate);
441a6f3f22fSchristos 	TEST_ASSERT_TRUE(IsEqualDateCal(&expected, &actual));
4424c290c01Schristos 
4434c290c01Schristos 	return;
444f17b710fSchristos }
445f17b710fSchristos 
446a6f3f22fSchristos /* check last day of february for first 10000 years */
447a6f3f22fSchristos void
4484c290c01Schristos test_LeapYears1(void)
4494c290c01Schristos {
450f17b710fSchristos 	struct calendar dateIn, dateOut;
451f17b710fSchristos 
452f17b710fSchristos 	for (dateIn.year = 1; dateIn.year < 10000; ++dateIn.year) {
453f17b710fSchristos 		dateIn.month	= 2;
454f17b710fSchristos 		dateIn.monthday = 28 + leapdays(dateIn.year);
455f17b710fSchristos 		dateIn.yearday	= 31 + dateIn.monthday;
456f17b710fSchristos 
457f17b710fSchristos 		ntpcal_rd_to_date(&dateOut, ntpcal_date_to_rd(&dateIn));
458f17b710fSchristos 
459a6f3f22fSchristos 		TEST_ASSERT_TRUE(IsEqualDateCal(&dateIn, &dateOut));
460f17b710fSchristos 	}
4614c290c01Schristos 
4624c290c01Schristos 	return;
463f17b710fSchristos }
464f17b710fSchristos 
465a6f3f22fSchristos /* check first day of march for first 10000 years */
466a6f3f22fSchristos void
4674c290c01Schristos test_LeapYears2(void)
4684c290c01Schristos {
469f17b710fSchristos 	struct calendar dateIn, dateOut;
470f17b710fSchristos 
471f17b710fSchristos 	for (dateIn.year = 1; dateIn.year < 10000; ++dateIn.year) {
472f17b710fSchristos 		dateIn.month	= 3;
473f17b710fSchristos 		dateIn.monthday = 1;
474f17b710fSchristos 		dateIn.yearday	= 60 + leapdays(dateIn.year);
475f17b710fSchristos 
476f17b710fSchristos 		ntpcal_rd_to_date(&dateOut, ntpcal_date_to_rd(&dateIn));
477a6f3f22fSchristos 		TEST_ASSERT_TRUE(IsEqualDateCal(&dateIn, &dateOut));
478f17b710fSchristos 	}
4794c290c01Schristos 
4804c290c01Schristos 	return;
481f17b710fSchristos }
482f17b710fSchristos 
483067f5680Schristos /* check the 'is_leapyear()' implementation for 4400 years */
484067f5680Schristos void
485067f5680Schristos test_LeapYears3(void)
486067f5680Schristos {
487067f5680Schristos 	int32_t year;
488067f5680Schristos 	int     l1, l2;
489067f5680Schristos 
490067f5680Schristos 	for (year = -399; year < 4000; ++year) {
491067f5680Schristos 		l1 = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
492067f5680Schristos 		l2 = is_leapyear(year);
493067f5680Schristos 		snprintf(mbuf, sizeof(mbuf), "y=%d", year);
494067f5680Schristos 		TEST_ASSERT_EQUAL_MESSAGE(l1, l2, mbuf);
495067f5680Schristos 	}
496067f5680Schristos }
497067f5680Schristos 
498a6f3f22fSchristos /* Full roundtrip from 1601-01-01 to 2400-12-31
499a6f3f22fSchristos  * checks sequence of rata die numbers and validates date output
500a6f3f22fSchristos  * (since the input is all nominal days of the calendar in that range
501a6f3f22fSchristos  * and the result of the inverse calculation must match the input no
502a6f3f22fSchristos  * invalid output can occur.)
503a6f3f22fSchristos  */
504a6f3f22fSchristos void
5054c290c01Schristos test_RoundTripDate(void)
5064c290c01Schristos {
507f17b710fSchristos 	struct calendar truDate, expDate = { 1600, 0, 12, 31 };;
508f17b710fSchristos 	int	 leaps;
509a6f3f22fSchristos 	int32	 truRdn, expRdn	= ntpcal_date_to_rd(&expDate);
510f17b710fSchristos 
511f17b710fSchristos 	while (expDate.year < 2400) {
512f17b710fSchristos 		expDate.year++;
513f17b710fSchristos 		expDate.month	= 0;
514f17b710fSchristos 		expDate.yearday = 0;
515f17b710fSchristos 		leaps = leapdays(expDate.year);
516f17b710fSchristos 		while (expDate.month < 12) {
517f17b710fSchristos 			expDate.month++;
518f17b710fSchristos 			expDate.monthday = 0;
519f17b710fSchristos 			while (expDate.monthday < real_month_days[leaps][expDate.month]) {
520f17b710fSchristos 				expDate.monthday++;
521f17b710fSchristos 				expDate.yearday++;
522f17b710fSchristos 				expRdn++;
523f17b710fSchristos 
524f17b710fSchristos 				truRdn = ntpcal_date_to_rd(&expDate);
525f17b710fSchristos 				TEST_ASSERT_EQUAL(expRdn, truRdn);
526f17b710fSchristos 
527f17b710fSchristos 				ntpcal_rd_to_date(&truDate, truRdn);
528a6f3f22fSchristos 				TEST_ASSERT_TRUE(IsEqualDateCal(&expDate, &truDate));
529f17b710fSchristos 			}
530f17b710fSchristos 		}
531f17b710fSchristos 	}
5324c290c01Schristos 
5334c290c01Schristos 	return;
534f17b710fSchristos }
535f17b710fSchristos 
536a6f3f22fSchristos /* Roundtrip testing on calyearstart */
537a6f3f22fSchristos void
5384c290c01Schristos test_RoundTripYearStart(void)
5394c290c01Schristos {
540f17b710fSchristos 	static const time_t pivot = 0;
541f17b710fSchristos 	u_int32 ntp, expys, truys;
542f17b710fSchristos 	struct calendar date;
543f17b710fSchristos 
544f17b710fSchristos 	for (ntp = 0; ntp < 0xFFFFFFFFu - 30000000u; ntp += 30000000u) {
545f17b710fSchristos 		truys = calyearstart(ntp, &pivot);
546f17b710fSchristos 		ntpcal_ntp_to_date(&date, ntp, &pivot);
547f17b710fSchristos 		date.month = date.monthday = 1;
548f17b710fSchristos 		date.hour = date.minute = date.second = 0;
549f17b710fSchristos 		expys = ntpcal_date_to_ntp(&date);
550f17b710fSchristos 		TEST_ASSERT_EQUAL(expys, truys);
551f17b710fSchristos 	}
5524c290c01Schristos 
5534c290c01Schristos 	return;
554f17b710fSchristos }
555f17b710fSchristos 
556a6f3f22fSchristos /* Roundtrip testing on calmonthstart */
557a6f3f22fSchristos void
5584c290c01Schristos test_RoundTripMonthStart(void)
5594c290c01Schristos {
560f17b710fSchristos 	static const time_t pivot = 0;
561f17b710fSchristos 	u_int32 ntp, expms, trums;
562f17b710fSchristos 	struct calendar date;
563f17b710fSchristos 
564f17b710fSchristos 	for (ntp = 0; ntp < 0xFFFFFFFFu - 2000000u; ntp += 2000000u) {
565f17b710fSchristos 		trums = calmonthstart(ntp, &pivot);
566f17b710fSchristos 		ntpcal_ntp_to_date(&date, ntp, &pivot);
567f17b710fSchristos 		date.monthday = 1;
568f17b710fSchristos 		date.hour = date.minute = date.second = 0;
569f17b710fSchristos 		expms = ntpcal_date_to_ntp(&date);
570f17b710fSchristos 		TEST_ASSERT_EQUAL(expms, trums);
571f17b710fSchristos 	}
5724c290c01Schristos 
5734c290c01Schristos 	return;
574f17b710fSchristos }
575f17b710fSchristos 
576a6f3f22fSchristos /* Roundtrip testing on calweekstart */
577a6f3f22fSchristos void
5784c290c01Schristos test_RoundTripWeekStart(void)
5794c290c01Schristos {
580f17b710fSchristos 	static const time_t pivot = 0;
581f17b710fSchristos 	u_int32 ntp, expws, truws;
582f17b710fSchristos 	struct isodate date;
583f17b710fSchristos 
584f17b710fSchristos 	for (ntp = 0; ntp < 0xFFFFFFFFu - 600000u; ntp += 600000u) {
585f17b710fSchristos 		truws = calweekstart(ntp, &pivot);
586f17b710fSchristos 		isocal_ntp_to_date(&date, ntp, &pivot);
587f17b710fSchristos 		date.hour = date.minute = date.second = 0;
588f17b710fSchristos 		date.weekday = 1;
589f17b710fSchristos 		expws = isocal_date_to_ntp(&date);
590f17b710fSchristos 		TEST_ASSERT_EQUAL(expws, truws);
591f17b710fSchristos 	}
5924c290c01Schristos 
5934c290c01Schristos 	return;
594f17b710fSchristos }
595f17b710fSchristos 
596a6f3f22fSchristos /* Roundtrip testing on caldaystart */
597a6f3f22fSchristos void
5984c290c01Schristos test_RoundTripDayStart(void)
5994c290c01Schristos {
600f17b710fSchristos 	static const time_t pivot = 0;
601f17b710fSchristos 	u_int32 ntp, expds, truds;
602f17b710fSchristos 	struct calendar date;
603f17b710fSchristos 
604f17b710fSchristos 	for (ntp = 0; ntp < 0xFFFFFFFFu - 80000u; ntp += 80000u) {
605f17b710fSchristos 		truds = caldaystart(ntp, &pivot);
606f17b710fSchristos 		ntpcal_ntp_to_date(&date, ntp, &pivot);
607f17b710fSchristos 		date.hour = date.minute = date.second = 0;
608f17b710fSchristos 		expds = ntpcal_date_to_ntp(&date);
609f17b710fSchristos 		TEST_ASSERT_EQUAL(expds, truds);
610f17b710fSchristos 	}
6114c290c01Schristos 
6124c290c01Schristos 	return;
613f17b710fSchristos }
614f17b710fSchristos 
615a6f3f22fSchristos /* ---------------------------------------------------------------------
616a6f3f22fSchristos  * ISO8601 week calendar internals
617a6f3f22fSchristos  *
618a6f3f22fSchristos  * The ISO8601 week calendar implementation is simple in the terms of
619a6f3f22fSchristos  * the math involved, but the implementation of the calculations must
620a6f3f22fSchristos  * take care of a few things like overflow, floor division, and sign
621a6f3f22fSchristos  * corrections.
622a6f3f22fSchristos  *
623a6f3f22fSchristos  * Most of the functions are straight forward, but converting from years
624a6f3f22fSchristos  * to weeks and from weeks to years warrants some extra tests. These use
625a6f3f22fSchristos  * an independent reference implementation of the conversion from years
626a6f3f22fSchristos  * to weeks.
627a6f3f22fSchristos  * ---------------------------------------------------------------------
628a6f3f22fSchristos  */
629a6f3f22fSchristos 
630a6f3f22fSchristos /* helper / reference implementation for the first week of year in the
631a6f3f22fSchristos  * ISO8601 week calendar. This is based on the reference definition of
632a6f3f22fSchristos  * the ISO week calendar start: The Monday closest to January,1st of the
633a6f3f22fSchristos  * corresponding year in the Gregorian calendar.
634a6f3f22fSchristos  */
635a6f3f22fSchristos static int32_t
636a6f3f22fSchristos refimpl_WeeksInIsoYears(
637a6f3f22fSchristos 	int32_t years)
638a6f3f22fSchristos {
639a6f3f22fSchristos 	int32_t days, weeks;
6404c290c01Schristos 
641a6f3f22fSchristos 	days = ntpcal_weekday_close(
642a6f3f22fSchristos 		ntpcal_days_in_years(years) + 1,
643a6f3f22fSchristos 		CAL_MONDAY) - 1;
644a6f3f22fSchristos 	/* the weekday functions operate on RDN, while we want elapsed
645a6f3f22fSchristos 	 * units here -- we have to add / sub 1 in the midlle / at the
646a6f3f22fSchristos 	 * end of the operation that gets us the first day of the ISO
647a6f3f22fSchristos 	 * week calendar day.
648a6f3f22fSchristos 	 */
649a6f3f22fSchristos 	weeks = days / 7;
650a6f3f22fSchristos 	days  = days % 7;
651a6f3f22fSchristos 	TEST_ASSERT_EQUAL(0, days); /* paranoia check... */
6524c290c01Schristos 
653a6f3f22fSchristos 	return weeks;
654a6f3f22fSchristos }
655a6f3f22fSchristos 
656a6f3f22fSchristos /* The next tests loop over 5000yrs, but should still be very fast. If
657a6f3f22fSchristos  * they are not, the calendar needs a better implementation...
658a6f3f22fSchristos  */
659a6f3f22fSchristos void
6604c290c01Schristos test_IsoCalYearsToWeeks(void)
6614c290c01Schristos {
662a6f3f22fSchristos 	int32_t years;
663a6f3f22fSchristos 	int32_t wref, wcal;
6644c290c01Schristos 
665a6f3f22fSchristos 	for (years = -1000; years < 4000; ++years) {
666a6f3f22fSchristos 		/* get number of weeks before years (reference) */
667a6f3f22fSchristos 		wref = refimpl_WeeksInIsoYears(years);
668a6f3f22fSchristos 		/* get number of weeks before years (object-under-test) */
669a6f3f22fSchristos 		wcal = isocal_weeks_in_years(years);
670a6f3f22fSchristos 		TEST_ASSERT_EQUAL(wref, wcal);
671a6f3f22fSchristos 	}
6724c290c01Schristos 
6734c290c01Schristos 	return;
674a6f3f22fSchristos }
675a6f3f22fSchristos 
676a6f3f22fSchristos void
6774c290c01Schristos test_IsoCalWeeksToYearStart(void)
6784c290c01Schristos {
679a6f3f22fSchristos 	int32_t years;
680a6f3f22fSchristos 	int32_t wref;
681a6f3f22fSchristos 	ntpcal_split ysplit;
6824c290c01Schristos 
683a6f3f22fSchristos 	for (years = -1000; years < 4000; ++years) {
684a6f3f22fSchristos 		/* get number of weeks before years (reference) */
685a6f3f22fSchristos 		wref = refimpl_WeeksInIsoYears(years);
686a6f3f22fSchristos 		/* reverse split */
687a6f3f22fSchristos 		ysplit = isocal_split_eraweeks(wref);
688a6f3f22fSchristos 		/* check invariants: same year, week 0 */
689a6f3f22fSchristos 		TEST_ASSERT_EQUAL(years, ysplit.hi);
690a6f3f22fSchristos 		TEST_ASSERT_EQUAL(0, ysplit.lo);
691a6f3f22fSchristos 	}
6924c290c01Schristos 
6934c290c01Schristos 	return;
694a6f3f22fSchristos }
695a6f3f22fSchristos 
696a6f3f22fSchristos void
6974c290c01Schristos test_IsoCalWeeksToYearEnd(void)
6984c290c01Schristos {
699a6f3f22fSchristos 	int32_t years;
700a6f3f22fSchristos 	int32_t wref;
701a6f3f22fSchristos 	ntpcal_split ysplit;
7024c290c01Schristos 
703a6f3f22fSchristos 	for (years = -1000; years < 4000; ++years) {
704a6f3f22fSchristos 		/* get last week of previous year */
705a6f3f22fSchristos 		wref = refimpl_WeeksInIsoYears(years) - 1;
706a6f3f22fSchristos 		/* reverse split */
707a6f3f22fSchristos 		ysplit = isocal_split_eraweeks(wref);
708a6f3f22fSchristos 		/* check invariants: previous year, week 51 or 52 */
709a6f3f22fSchristos 		TEST_ASSERT_EQUAL(years-1, ysplit.hi);
710a6f3f22fSchristos 		TEST_ASSERT(ysplit.lo == 51 || ysplit.lo == 52);
711a6f3f22fSchristos 	}
7124c290c01Schristos 
7134c290c01Schristos 	return;
714a6f3f22fSchristos }
715a6f3f22fSchristos 
716a6f3f22fSchristos void
7174c290c01Schristos test_DaySecToDate(void)
7184c290c01Schristos {
719a6f3f22fSchristos 	struct calendar cal;
720a6f3f22fSchristos 	int32_t days;
721a6f3f22fSchristos 
722a6f3f22fSchristos 	days = ntpcal_daysec_to_date(&cal, -86400);
723a6f3f22fSchristos 	TEST_ASSERT_MESSAGE((days==-1 && cal.hour==0 && cal.minute==0 && cal.second==0),
724a6f3f22fSchristos 		"failed for -86400");
725a6f3f22fSchristos 
726a6f3f22fSchristos 	days = ntpcal_daysec_to_date(&cal, -86399);
727a6f3f22fSchristos 	TEST_ASSERT_MESSAGE((days==-1 && cal.hour==0 && cal.minute==0 && cal.second==1),
728a6f3f22fSchristos 		"failed for -86399");
729a6f3f22fSchristos 
730a6f3f22fSchristos 	days = ntpcal_daysec_to_date(&cal, -1);
731a6f3f22fSchristos 	TEST_ASSERT_MESSAGE((days==-1 && cal.hour==23 && cal.minute==59 && cal.second==59),
732a6f3f22fSchristos 		"failed for -1");
733a6f3f22fSchristos 
734a6f3f22fSchristos 	days = ntpcal_daysec_to_date(&cal, 0);
735a6f3f22fSchristos 	TEST_ASSERT_MESSAGE((days==0 && cal.hour==0 && cal.minute==0 && cal.second==0),
736a6f3f22fSchristos 		"failed for 0");
737a6f3f22fSchristos 
738a6f3f22fSchristos 	days = ntpcal_daysec_to_date(&cal, 1);
739a6f3f22fSchristos 	TEST_ASSERT_MESSAGE((days==0 && cal.hour==0 && cal.minute==0 && cal.second==1),
740a6f3f22fSchristos 		"failed for 1");
741a6f3f22fSchristos 
742a6f3f22fSchristos 	days = ntpcal_daysec_to_date(&cal, 86399);
743a6f3f22fSchristos 	TEST_ASSERT_MESSAGE((days==0 && cal.hour==23 && cal.minute==59 && cal.second==59),
744a6f3f22fSchristos 		"failed for 86399");
745a6f3f22fSchristos 
746a6f3f22fSchristos 	days = ntpcal_daysec_to_date(&cal, 86400);
747a6f3f22fSchristos 	TEST_ASSERT_MESSAGE((days==1 && cal.hour==0 && cal.minute==0 && cal.second==0),
748a6f3f22fSchristos 		"failed for 86400");
7494c290c01Schristos 
7504c290c01Schristos 	return;
751a6f3f22fSchristos }
7525645e8e7Schristos 
7535645e8e7Schristos /* --------------------------------------------------------------------
7545645e8e7Schristos  * unfolding of (truncated) NTP time stamps to full 64bit values.
7555645e8e7Schristos  *
7565645e8e7Schristos  * Note: These tests need a 64bit time_t to be useful.
7575645e8e7Schristos  */
7585645e8e7Schristos 
7595645e8e7Schristos void
7605645e8e7Schristos test_NtpToNtp(void)
7615645e8e7Schristos {
7625645e8e7Schristos #   if SIZEOF_TIME_T <= 4
7635645e8e7Schristos 
7645645e8e7Schristos 	TEST_IGNORE_MESSAGE("test only useful for sizeof(time_t) > 4, skipped");
7655645e8e7Schristos 
7665645e8e7Schristos #   else
7675645e8e7Schristos 
7685645e8e7Schristos 	static const uint32_t ntp_vals[6] = {
7695645e8e7Schristos 		UINT32_C(0x00000000),
7705645e8e7Schristos 		UINT32_C(0x00000001),
7715645e8e7Schristos 		UINT32_C(0x7FFFFFFF),
7725645e8e7Schristos 		UINT32_C(0x80000000),
7735645e8e7Schristos 		UINT32_C(0x80000001),
7745645e8e7Schristos 		UINT32_C(0xFFFFFFFF)
7755645e8e7Schristos 	};
7765645e8e7Schristos 
7775645e8e7Schristos 	static char	lbuf[128];
7785645e8e7Schristos 	vint64		hold;
7795645e8e7Schristos 	time_t		pivot, texp, diff;
7805645e8e7Schristos 	int		loops, iloop;
7815645e8e7Schristos 
7825645e8e7Schristos 	pivot = 0;
7835645e8e7Schristos 	for (loops = 0; loops < 16; ++loops) {
7845645e8e7Schristos 		for (iloop = 0; iloop < 6; ++iloop) {
7855645e8e7Schristos 			hold = ntpcal_ntp_to_ntp(
7865645e8e7Schristos 				ntp_vals[iloop], &pivot);
7875645e8e7Schristos 			texp = vint64_to_time(&hold);
7885645e8e7Schristos 
7895645e8e7Schristos 			/* constraint 1: texp must be in the
7905645e8e7Schristos 			 * (right-open) intervall [p-(2^31), p+(2^31)[,
7915645e8e7Schristos 			 * but the pivot 'p' must be taken in full NTP
7925645e8e7Schristos 			 * time scale!
7935645e8e7Schristos 			 */
7945645e8e7Schristos 			diff = texp - (pivot + JAN_1970);
7955645e8e7Schristos 			snprintf(lbuf, sizeof(lbuf),
7965645e8e7Schristos 				 "bounds check: piv=%lld exp=%lld dif=%lld",
7975645e8e7Schristos 				 (long long)pivot,
7985645e8e7Schristos 				 (long long)texp,
7995645e8e7Schristos 				 (long long)diff);
8005645e8e7Schristos 			TEST_ASSERT_MESSAGE((diff >= INT32_MIN) && (diff <= INT32_MAX),
8015645e8e7Schristos 					    lbuf);
8025645e8e7Schristos 
8035645e8e7Schristos 			/* constraint 2: low word must be equal to
8045645e8e7Schristos 			 * input
8055645e8e7Schristos 			 */
8065645e8e7Schristos 			snprintf(lbuf, sizeof(lbuf),
8075645e8e7Schristos 				 "low check: ntp(in)=$%08lu ntp(out[0:31])=$%08lu",
8085645e8e7Schristos 				 (unsigned long)ntp_vals[iloop],
8095645e8e7Schristos 				 (unsigned long)hold.D_s.lo);
8105645e8e7Schristos 			TEST_ASSERT_EQUAL_MESSAGE(ntp_vals[iloop], hold.D_s.lo, lbuf);
8115645e8e7Schristos 		}
8125645e8e7Schristos 		pivot += 0x20000000;
8135645e8e7Schristos 	}
8145645e8e7Schristos #   endif
8155645e8e7Schristos }
8165645e8e7Schristos 
8175645e8e7Schristos void
8185645e8e7Schristos test_NtpToTime(void)
8195645e8e7Schristos {
8205645e8e7Schristos #   if SIZEOF_TIME_T <= 4
8215645e8e7Schristos 
8225645e8e7Schristos 	TEST_IGNORE_MESSAGE("test only useful for sizeof(time_t) > 4, skipped");
8235645e8e7Schristos 
8245645e8e7Schristos #   else
8255645e8e7Schristos 
8265645e8e7Schristos 	static const uint32_t ntp_vals[6] = {
8275645e8e7Schristos 		UINT32_C(0x00000000),
8285645e8e7Schristos 		UINT32_C(0x00000001),
8295645e8e7Schristos 		UINT32_C(0x7FFFFFFF),
8305645e8e7Schristos 		UINT32_C(0x80000000),
8315645e8e7Schristos 		UINT32_C(0x80000001),
8325645e8e7Schristos 		UINT32_C(0xFFFFFFFF)
8335645e8e7Schristos 	};
8345645e8e7Schristos 
8355645e8e7Schristos 	static char	lbuf[128];
8365645e8e7Schristos 	vint64		hold;
8375645e8e7Schristos 	time_t		pivot, texp, diff;
8385645e8e7Schristos 	uint32_t	back;
8395645e8e7Schristos 	int		loops, iloop;
8405645e8e7Schristos 
8415645e8e7Schristos 	pivot = 0;
8425645e8e7Schristos 	for (loops = 0; loops < 16; ++loops) {
8435645e8e7Schristos 		for (iloop = 0; iloop < 6; ++iloop) {
8445645e8e7Schristos 			hold = ntpcal_ntp_to_time(
8455645e8e7Schristos 				ntp_vals[iloop], &pivot);
8465645e8e7Schristos 			texp = vint64_to_time(&hold);
8475645e8e7Schristos 
8485645e8e7Schristos 			/* constraint 1: texp must be in the
8495645e8e7Schristos 			 * (right-open) intervall [p-(2^31), p+(2^31)[
8505645e8e7Schristos 			 */
8515645e8e7Schristos 			diff = texp - pivot;
8525645e8e7Schristos 			snprintf(lbuf, sizeof(lbuf),
8535645e8e7Schristos 				 "bounds check: piv=%lld exp=%lld dif=%lld",
8545645e8e7Schristos 				 (long long)pivot,
8555645e8e7Schristos 				 (long long)texp,
8565645e8e7Schristos 				 (long long)diff);
8575645e8e7Schristos 			TEST_ASSERT_MESSAGE((diff >= INT32_MIN) && (diff <= INT32_MAX),
8585645e8e7Schristos 					    lbuf);
8595645e8e7Schristos 
8605645e8e7Schristos 			/* constraint 2: conversion from full time back
8615645e8e7Schristos 			 * to truncated NTP time must yield same result
8625645e8e7Schristos 			 * as input.
8635645e8e7Schristos 			*/
8645645e8e7Schristos 			back = (uint32_t)texp + JAN_1970;
8655645e8e7Schristos 			snprintf(lbuf, sizeof(lbuf),
8665645e8e7Schristos 				 "modulo check: ntp(in)=$%08lu ntp(out)=$%08lu",
8675645e8e7Schristos 				 (unsigned long)ntp_vals[iloop],
8685645e8e7Schristos 				 (unsigned long)back);
8695645e8e7Schristos 			TEST_ASSERT_EQUAL_MESSAGE(ntp_vals[iloop], back, lbuf);
8705645e8e7Schristos 		}
8715645e8e7Schristos 		pivot += 0x20000000;
8725645e8e7Schristos 	}
8735645e8e7Schristos #   endif
8745645e8e7Schristos }
875067f5680Schristos 
876067f5680Schristos /* --------------------------------------------------------------------
877067f5680Schristos  * GPS rollover
878067f5680Schristos  * --------------------------------------------------------------------
879067f5680Schristos  */
880067f5680Schristos void
881067f5680Schristos test_GpsRollOver(void)
882067f5680Schristos {
883067f5680Schristos 	/* we test on wednesday, noon, and on the border */
884067f5680Schristos 	static const int32_t wsec1 = 3*SECSPERDAY + SECSPERDAY/2;
885067f5680Schristos 	static const int32_t wsec2 = 7 * SECSPERDAY - 1;
886067f5680Schristos 	static const int32_t week0 = GPSNTP_WSHIFT + 2047;
887067f5680Schristos 	static const int32_t week1 = GPSNTP_WSHIFT + 2048;
888067f5680Schristos 	TCivilDate jd;
889067f5680Schristos 	TGpsDatum  gps;
890067f5680Schristos 	l_fp       fpz;
891067f5680Schristos 
892067f5680Schristos 	ZERO(fpz);
893067f5680Schristos 
894067f5680Schristos 	/* test on 2nd rollover, April 2019
895067f5680Schristos 	 * we set the base date properly one week *before the rollover, to
896067f5680Schristos 	 * check if the expansion merrily hops over the warp.
897067f5680Schristos 	 */
898067f5680Schristos 	basedate_set_day(2047 * 7 + NTP_TO_GPS_DAYS);
899067f5680Schristos 
900067f5680Schristos 	strToCal(&jd, "19-04-03T12:00:00");
901067f5680Schristos 	gps = gpscal_from_calendar(&jd, fpz);
902067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(week0, gps.weeks, "(week test 1))");
903067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(wsec1, gps.wsecs, "(secs test 1)");
904067f5680Schristos 
905067f5680Schristos 	strToCal(&jd, "19-04-06T23:59:59");
906067f5680Schristos 	gps = gpscal_from_calendar(&jd, fpz);
907067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(week0, gps.weeks, "(week test 2)");
908067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(wsec2, gps.wsecs, "(secs test 2)");
909067f5680Schristos 
910067f5680Schristos 	strToCal(&jd, "19-04-07T00:00:00");
911067f5680Schristos 	gps = gpscal_from_calendar(&jd, fpz);
912067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(week1, gps.weeks, "(week test 3)");
913067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(  0 , gps.wsecs, "(secs test 3)");
914067f5680Schristos 
915067f5680Schristos 	strToCal(&jd, "19-04-10T12:00:00");
916067f5680Schristos 	gps = gpscal_from_calendar(&jd, fpz);
917067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(week1, gps.weeks, "(week test 4)");
918067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(wsec1, gps.wsecs, "(secs test 4)");
919067f5680Schristos }
920067f5680Schristos 
921067f5680Schristos void
922067f5680Schristos test_GpsRemapFunny(void)
923067f5680Schristos {
924067f5680Schristos 	TCivilDate di, dc, de;
925067f5680Schristos 	TGpsDatum  gd;
926067f5680Schristos 
927067f5680Schristos 	l_fp       fpz;
928067f5680Schristos 
929067f5680Schristos 	ZERO(fpz);
930067f5680Schristos 	basedate_set_day(2048 * 7 + NTP_TO_GPS_DAYS);
931067f5680Schristos 
932067f5680Schristos 	/* expand 2digit year to 2080, then fold back into 3rd GPS era: */
933067f5680Schristos 	strToCal(&di, "80-01-01T00:00:00");
934067f5680Schristos 	strToCal(&de, "2021-02-15T00:00:00");
935067f5680Schristos 	gd = gpscal_from_calendar(&di, fpz);
936067f5680Schristos 	gpscal_to_calendar(&dc, &gd);
937067f5680Schristos 	TEST_ASSERT_TRUE(IsEqualCal(&de, &dc));
938067f5680Schristos 
939067f5680Schristos 	/* expand 2digit year to 2080, then fold back into 3rd GPS era: */
940067f5680Schristos 	strToCal(&di, "80-01-05T00:00:00");
941067f5680Schristos 	strToCal(&de, "2021-02-19T00:00:00");
942067f5680Schristos 	gd = gpscal_from_calendar(&di, fpz);
943067f5680Schristos 	gpscal_to_calendar(&dc, &gd);
944067f5680Schristos 	TEST_ASSERT_TRUE(IsEqualCal(&de, &dc));
945067f5680Schristos 
946067f5680Schristos 	/* remap days before epoch into 3rd era: */
947067f5680Schristos 	strToCal(&di, "1980-01-05T00:00:00");
948067f5680Schristos 	strToCal(&de, "2038-11-20T00:00:00");
949067f5680Schristos 	gd = gpscal_from_calendar(&di, fpz);
950067f5680Schristos 	gpscal_to_calendar(&dc, &gd);
951067f5680Schristos 	TEST_ASSERT_TRUE(IsEqualCal(&de, &dc));
952067f5680Schristos 
953067f5680Schristos 	/* remap GPS epoch: */
954067f5680Schristos 	strToCal(&di, "1980-01-06T00:00:00");
955067f5680Schristos 	strToCal(&de, "2019-04-07T00:00:00");
956067f5680Schristos 	gd = gpscal_from_calendar(&di, fpz);
957067f5680Schristos 	gpscal_to_calendar(&dc, &gd);
958067f5680Schristos 	TEST_ASSERT_TRUE(IsEqualCal(&de, &dc));
959067f5680Schristos }
960067f5680Schristos 
961067f5680Schristos void
962067f5680Schristos test_GpsNtpFixpoints(void)
963067f5680Schristos {
964067f5680Schristos 	basedate_set_day(NTP_TO_GPS_DAYS);
965067f5680Schristos 	TGpsDatum e1gps;
966067f5680Schristos 	TNtpDatum e1ntp, r1ntp;
967067f5680Schristos 	l_fp      lfpe , lfpr;
968067f5680Schristos 
969067f5680Schristos 	lfpe.l_ui = 0;
970067f5680Schristos 	lfpe.l_uf = UINT32_C(0x80000000);
971067f5680Schristos 
972067f5680Schristos 	ZERO(e1gps);
973067f5680Schristos 	e1gps.weeks = 0;
974067f5680Schristos 	e1gps.wsecs = SECSPERDAY;
975067f5680Schristos 	e1gps.frac  = UINT32_C(0x80000000);
976067f5680Schristos 
977067f5680Schristos 	ZERO(e1ntp);
978067f5680Schristos 	e1ntp.frac  = UINT32_C(0x80000000);
979067f5680Schristos 
980067f5680Schristos 	r1ntp = gpsntp_from_gpscal(&e1gps);
981067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(e1ntp.days, r1ntp.days, "gps -> ntp / days");
982067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(e1ntp.secs, r1ntp.secs, "gps -> ntp / secs");
983067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(e1ntp.frac, r1ntp.frac, "gps -> ntp / frac");
984067f5680Schristos 
985067f5680Schristos 	lfpr = ntpfp_from_gpsdatum(&e1gps);
986067f5680Schristos 	snprintf(mbuf, sizeof(mbuf), "gps -> l_fp: %s <=> %s",
987067f5680Schristos 		 lfptoa(&lfpe, 9), lfptoa(&lfpr, 9));
988067f5680Schristos 	TEST_ASSERT_TRUE_MESSAGE(L_ISEQU(&lfpe, &lfpr), mbuf);
989067f5680Schristos 
990067f5680Schristos 	lfpr = ntpfp_from_ntpdatum(&e1ntp);
991067f5680Schristos 	snprintf(mbuf, sizeof(mbuf), "ntp -> l_fp: %s <=> %s",
992067f5680Schristos 		 lfptoa(&lfpe, 9), lfptoa(&lfpr, 9));
993067f5680Schristos 	TEST_ASSERT_TRUE_MESSAGE(L_ISEQU(&lfpe, &lfpr), mbuf);
994067f5680Schristos }
995067f5680Schristos 
996067f5680Schristos void
997067f5680Schristos test_CalUMod7(void)
998067f5680Schristos {
999067f5680Schristos 	TEST_ASSERT_EQUAL(0, u32mod7(0));
1000067f5680Schristos 	TEST_ASSERT_EQUAL(1, u32mod7(INT32_MAX));
1001067f5680Schristos 	TEST_ASSERT_EQUAL(2, u32mod7(UINT32_C(1)+INT32_MAX));
1002067f5680Schristos 	TEST_ASSERT_EQUAL(3, u32mod7(UINT32_MAX));
1003067f5680Schristos }
1004067f5680Schristos 
1005067f5680Schristos void
1006067f5680Schristos test_CalIMod7(void)
1007067f5680Schristos {
1008067f5680Schristos 	TEST_ASSERT_EQUAL(5, i32mod7(INT32_MIN));
1009067f5680Schristos 	TEST_ASSERT_EQUAL(6, i32mod7(-1));
1010067f5680Schristos 	TEST_ASSERT_EQUAL(0, i32mod7(0));
1011067f5680Schristos 	TEST_ASSERT_EQUAL(1, i32mod7(INT32_MAX));
1012067f5680Schristos }
1013067f5680Schristos 
1014067f5680Schristos /* Century expansion tests. Reverse application of Zeller's congruence,
1015067f5680Schristos  * sort of... hence the name "Rellez", Zeller backwards. Just in case
1016067f5680Schristos  * you didn't notice ;)
1017067f5680Schristos  */
1018067f5680Schristos 
1019067f5680Schristos void
1020*eabc0478Schristos test_RellezCentury1_1(void)
1021067f5680Schristos {
1022067f5680Schristos 	/* 1st day of a century */
1023067f5680Schristos 	TEST_ASSERT_EQUAL(1901, ntpcal_expand_century( 1, 1, 1, CAL_TUESDAY  ));
1024067f5680Schristos 	TEST_ASSERT_EQUAL(2001, ntpcal_expand_century( 1, 1, 1, CAL_MONDAY   ));
1025067f5680Schristos 	TEST_ASSERT_EQUAL(2101, ntpcal_expand_century( 1, 1, 1, CAL_SATURDAY ));
1026067f5680Schristos 	TEST_ASSERT_EQUAL(2201, ntpcal_expand_century( 1, 1, 1, CAL_THURSDAY ));
1027067f5680Schristos 	/* bad/impossible cases: */
1028067f5680Schristos 	TEST_ASSERT_EQUAL(   0, ntpcal_expand_century( 1, 1, 1, CAL_WEDNESDAY));
1029067f5680Schristos 	TEST_ASSERT_EQUAL(   0, ntpcal_expand_century( 1, 1, 1, CAL_FRIDAY   ));
1030067f5680Schristos 	TEST_ASSERT_EQUAL(   0, ntpcal_expand_century( 1, 1, 1, CAL_SUNDAY   ));
1031067f5680Schristos }
1032067f5680Schristos 
1033067f5680Schristos void
1034*eabc0478Schristos test_RellezCentury3_1(void)
1035067f5680Schristos {
1036067f5680Schristos 	/* 1st day in March of a century (the tricky point) */
1037067f5680Schristos 	TEST_ASSERT_EQUAL(1901, ntpcal_expand_century( 1, 3, 1, CAL_FRIDAY   ));
1038067f5680Schristos 	TEST_ASSERT_EQUAL(2001, ntpcal_expand_century( 1, 3, 1, CAL_THURSDAY ));
1039067f5680Schristos 	TEST_ASSERT_EQUAL(2101, ntpcal_expand_century( 1, 3, 1, CAL_TUESDAY  ));
1040067f5680Schristos 	TEST_ASSERT_EQUAL(2201, ntpcal_expand_century( 1, 3, 1, CAL_SUNDAY   ));
1041067f5680Schristos 	/* bad/impossible cases: */
1042067f5680Schristos 	TEST_ASSERT_EQUAL(   0, ntpcal_expand_century( 1, 3, 1, CAL_MONDAY   ));
1043067f5680Schristos 	TEST_ASSERT_EQUAL(   0, ntpcal_expand_century( 1, 3, 1, CAL_WEDNESDAY));
1044067f5680Schristos 	TEST_ASSERT_EQUAL(   0, ntpcal_expand_century( 1, 3, 1, CAL_SATURDAY ));
1045067f5680Schristos }
1046067f5680Schristos 
1047067f5680Schristos void
1048*eabc0478Schristos test_RellezYearZero(void)
1049067f5680Schristos {
1050067f5680Schristos 	/* the infamous year zero */
1051067f5680Schristos 	TEST_ASSERT_EQUAL(1900, ntpcal_expand_century( 0, 1, 1, CAL_MONDAY   ));
1052067f5680Schristos 	TEST_ASSERT_EQUAL(2000, ntpcal_expand_century( 0, 1, 1, CAL_SATURDAY ));
1053067f5680Schristos 	TEST_ASSERT_EQUAL(2100, ntpcal_expand_century( 0, 1, 1, CAL_FRIDAY   ));
1054067f5680Schristos 	TEST_ASSERT_EQUAL(2200, ntpcal_expand_century( 0, 1, 1, CAL_WEDNESDAY));
1055067f5680Schristos 	/* bad/impossible cases: */
1056067f5680Schristos 	TEST_ASSERT_EQUAL(   0, ntpcal_expand_century( 0, 1, 1, CAL_TUESDAY  ));
1057067f5680Schristos 	TEST_ASSERT_EQUAL(   0, ntpcal_expand_century( 0, 1, 1, CAL_THURSDAY ));
1058067f5680Schristos 	TEST_ASSERT_EQUAL(   0, ntpcal_expand_century( 0, 1, 1, CAL_SUNDAY   ));
1059067f5680Schristos }
1060067f5680Schristos 
1061067f5680Schristos void test_RellezEra(void);
1062067f5680Schristos void test_RellezEra(void)
1063067f5680Schristos {
1064067f5680Schristos 	static const unsigned int mt[13] = { 0, 31,28,31,30,31,30,31,31,30,31,30,31 };
1065067f5680Schristos 	unsigned int yi, yo, m, d, wd;
1066067f5680Schristos 
1067067f5680Schristos 	/* last day before our era -- fold forward */
1068067f5680Schristos 	yi = 1899;
1069067f5680Schristos 	m  = 12;
1070067f5680Schristos 	d  = 31;
1071067f5680Schristos 	wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1;
1072067f5680Schristos 	yo = ntpcal_expand_century((yi%100), m, d, wd);
1073067f5680Schristos 	snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u",
1074067f5680Schristos 		 yi, m, d, wd);
1075067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(2299, yo, mbuf);
1076067f5680Schristos 
1077067f5680Schristos 	/* 1st day after our era -- fold back */
1078067f5680Schristos 	yi = 2300;
1079067f5680Schristos 	m  = 1;
1080067f5680Schristos 	d  = 1;
1081067f5680Schristos 	wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1;
1082067f5680Schristos 	yo = ntpcal_expand_century((yi%100), m, d, wd);
1083067f5680Schristos 	snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u",
1084067f5680Schristos 		 yi, m, d, wd);
1085067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(1900, yo, mbuf);
1086067f5680Schristos 
1087067f5680Schristos 	/* test every month in our 400y era */
1088067f5680Schristos 	for (yi = 1900; yi < 2300; ++yi) {
1089067f5680Schristos 		for (m = 1; m < 12; ++m) {
1090067f5680Schristos 			/* test first day of month */
1091067f5680Schristos 			d = 1;
1092067f5680Schristos 			wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1;
1093067f5680Schristos 			yo = ntpcal_expand_century((yi%100), m, d, wd);
1094067f5680Schristos 			snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u",
1095067f5680Schristos 				 yi, m, d, wd);
1096067f5680Schristos 			TEST_ASSERT_EQUAL_MESSAGE(yi, yo, mbuf);
1097067f5680Schristos 
1098067f5680Schristos 			/* test last day of month */
1099067f5680Schristos 			d = mt[m] + (m == 2 && is_leapyear(yi));
1100067f5680Schristos 			wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1;
1101067f5680Schristos 			yo = ntpcal_expand_century((yi%100), m, d, wd);
1102067f5680Schristos 			snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u",
1103067f5680Schristos 				 yi, m, d, wd);
1104067f5680Schristos 			TEST_ASSERT_EQUAL_MESSAGE(yi, yo, mbuf);
1105067f5680Schristos 		}
1106067f5680Schristos 	}
1107067f5680Schristos }
1108067f5680Schristos 
1109067f5680Schristos /* This is nearly a verbatim copy of the in-situ implementation of
1110067f5680Schristos  * Zeller's congruence in libparse/clk_rawdcf.c, so the algorithm
1111067f5680Schristos  * can be tested.
1112067f5680Schristos  */
1113067f5680Schristos static int
1114067f5680Schristos zeller_expand(
1115067f5680Schristos         unsigned int  y,
1116067f5680Schristos         unsigned int  m,
1117067f5680Schristos         unsigned int  d,
1118067f5680Schristos 	unsigned int  wd
1119067f5680Schristos 	)
1120067f5680Schristos {
1121067f5680Schristos 	unsigned int  c;
1122067f5680Schristos 
1123067f5680Schristos         if ((y >= 100u) || (--m >= 12u) || (--d >= 31u) || (--wd >= 7u))
1124067f5680Schristos 		return 0;
1125067f5680Schristos 
1126067f5680Schristos 	if ((m += 10u) >= 12u)
1127067f5680Schristos 		m -= 12u;
1128067f5680Schristos 	else if (--y >= 100u)
1129067f5680Schristos 		y += 100u;
1130067f5680Schristos 	d += y + (y >> 2) + 2u;
1131067f5680Schristos 	d += (m * 83u + 16u) >> 5;
1132067f5680Schristos 
1133067f5680Schristos 	c = (((252u + wd - d) * 0x6db6db6eU) >> 29) & 7u;
1134067f5680Schristos 	if (c > 3u)
1135067f5680Schristos 		return 0;
1136067f5680Schristos 
1137067f5680Schristos 	if ((m > 9u) && (++y >= 100u)) {
1138067f5680Schristos 		y -= 100u;
1139067f5680Schristos 		c = (c + 1) & 3u;
1140067f5680Schristos 	}
1141067f5680Schristos 	y += (c * 100u);
1142067f5680Schristos 	y += (y < 370u) ? 2000 : 1600;
1143067f5680Schristos 	return (int)y;
1144067f5680Schristos }
1145067f5680Schristos 
1146067f5680Schristos void test_zellerDirect(void);
1147067f5680Schristos void test_zellerDirect(void)
1148067f5680Schristos {
1149067f5680Schristos 	static const unsigned int mt[13] = { 0, 31,28,31,30,31,30,31,31,30,31,30,31 };
1150067f5680Schristos 	unsigned int yi, yo, m, d, wd;
1151067f5680Schristos 
1152067f5680Schristos 	/* last day before our era -- fold forward */
1153067f5680Schristos 	yi = 1969;
1154067f5680Schristos 	m  = 12;
1155067f5680Schristos 	d  = 31;
1156067f5680Schristos 	wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1;
1157067f5680Schristos 	yo = zeller_expand((yi%100), m, d, wd);
1158067f5680Schristos 	snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u",
1159067f5680Schristos 		 yi, m, d, wd);
1160067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(2369, yo, mbuf);
1161067f5680Schristos 
1162067f5680Schristos 	/* 1st day after our era -- fold back */
1163067f5680Schristos 	yi = 2370;
1164067f5680Schristos 	m  = 1;
1165067f5680Schristos 	d  = 1;
1166067f5680Schristos 	wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1;
1167067f5680Schristos 	yo = zeller_expand((yi%100), m, d, wd);
1168067f5680Schristos 	snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u",
1169067f5680Schristos 		 yi, m, d, wd);
1170067f5680Schristos 	TEST_ASSERT_EQUAL_MESSAGE(1970, yo, mbuf);
1171067f5680Schristos 
1172067f5680Schristos 	/* test every month in our 400y era */
1173067f5680Schristos 	for (yi = 1970; yi < 2370; ++yi) {
1174067f5680Schristos 		for (m = 1; m < 12; ++m) {
1175067f5680Schristos 			/* test first day of month */
1176067f5680Schristos 			d = 1;
1177067f5680Schristos 			wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1;
1178067f5680Schristos 			yo = zeller_expand((yi%100), m, d, wd);
1179067f5680Schristos 			snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u",
1180067f5680Schristos 				 yi, m, d, wd);
1181067f5680Schristos 			TEST_ASSERT_EQUAL_MESSAGE(yi, yo, mbuf);
1182067f5680Schristos 
1183067f5680Schristos 			/* test last day of month */
1184067f5680Schristos 			d = mt[m] + (m == 2 && is_leapyear(yi));
1185067f5680Schristos 			wd = ntpcal_edate_to_eradays(yi-1, m-1, d-1) % 7 + 1;
1186067f5680Schristos 			yo = zeller_expand((yi%100), m, d, wd);
1187067f5680Schristos 			snprintf(mbuf, sizeof(mbuf), "failed, di=%04u-%02u-%02u, wd=%u",
1188067f5680Schristos 				 yi, m, d, wd);
1189067f5680Schristos 			TEST_ASSERT_EQUAL_MESSAGE(yi, yo, mbuf);
1190067f5680Schristos 		}
1191067f5680Schristos 	}
1192067f5680Schristos }
1193067f5680Schristos 
1194067f5680Schristos void test_ZellerDirectBad(void);
1195067f5680Schristos void test_ZellerDirectBad(void)
1196067f5680Schristos {
1197067f5680Schristos 	unsigned int y, n, wd;
1198067f5680Schristos 	for (y = 2001; y < 2101; ++y) {
1199067f5680Schristos 		wd = ntpcal_edate_to_eradays(y-1, 0, 0) % 7 + 1;
1200067f5680Schristos 		/* move 4 centuries ahead */
1201067f5680Schristos 		wd = (wd + 5) % 7 + 1;
1202067f5680Schristos 		for (n = 0; n < 3; ++n) {
1203067f5680Schristos 			TEST_ASSERT_EQUAL(0, zeller_expand((y%100), 1, 1, wd));
1204067f5680Schristos 			wd = (wd + 4) % 7 + 1;
1205067f5680Schristos 		}
1206067f5680Schristos 	}
1207067f5680Schristos }
1208067f5680Schristos 
1209067f5680Schristos void test_zellerModInv(void);
1210067f5680Schristos void test_zellerModInv(void)
1211067f5680Schristos {
1212067f5680Schristos 	unsigned int i, r1, r2;
1213067f5680Schristos 
1214067f5680Schristos 	for (i = 0; i < 2048; ++i) {
1215067f5680Schristos 		r1 = (3 * i) % 7;
1216067f5680Schristos 		r2 = ((i * 0x6db6db6eU) >> 29) & 7u;
1217067f5680Schristos 		snprintf(mbuf, sizeof(mbuf), "i=%u", i);
1218067f5680Schristos 		TEST_ASSERT_EQUAL_MESSAGE(r1, r2, mbuf);
1219067f5680Schristos 	}
1220067f5680Schristos }
1221067f5680Schristos 
1222067f5680Schristos 
1223