1 /* $Id: biorythm.c,v 1.5 2011-10-22 07:57:00 sverrehu Exp $ */
2 /*------------------------------------------------------------------------
3  |  FILE            biorythm.c
4  |  MODULE OF       biorythm - simple biorythm calculation program
5  |
6  |  DESCRIPTION
7  |
8  |  WRITTEN BY      Sverre H. Huseby <shh@thathost.com>
9  +----------------------------------------------------------------------*/
10 
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <math.h>
14 
15 #include <shhmsg.h>
16 #include <shhopt.h>
17 
18 #include "date.h"
19 
20 #ifndef M_PI
21 #  define M_PI 3.141592654
22 #endif
23 
24 /* Periods: Physical, Emotional and Intellectual */
25 #define PHY_PERIOD 23
26 #define EMO_PERIOD 28
27 #define INT_PERIOD 33
28 
29 #define PLOT_PHY 1
30 #define PLOT_EMO 2
31 #define PLOT_INT 4
32 #define PLOT_AVG 8
33 
34 /*-----------------------------------------------------------------------+
35 |  PRIVATE FUNCTIONS                                                     |
36 +-----------------------------------------------------------------------*/
37 
38 /*------------------------------------------------------------------------
39  |  NAME          f
40  |
41  |  FUNCTION      Calculate a curve sample value.
42  |
43  |  INPUT         birth   date of birth.
44  |                day     the day to get sample from.
45  |                period  the period of the curve in days.
46  |
47  |  RETURNS       A value in [-1, 1].
48  */
49 static double
f(Date birth,Date day,int period)50 f(Date birth, Date day, int period)
51 {
52     int n;
53 
54     n = dateGetNumDays(birth, day) % period;
55     return sin((double) n * 2.0 * M_PI / period);
56 }
57 
58 /*------------------------------------------------------------------------
59  |  NAME          nearestInt
60  |
61  |  FUNCTION      Round a double to the nearest integer.
62  |
63  |  INPUT         x       the double to round.
64  |
65  |  RETURNS       The nearest integer.
66  */
67 static int
nearestInt(double x)68 nearestInt(double x)
69 {
70     return (int) floor(x + 0.5);
71 }
72 
73 /*------------------------------------------------------------------------
74  |  NAME          bioTextPlot
75  |
76  |  FUNCTION      Make a biorythm graph using text characters.
77  |
78  |  INPUT         birth   date of birth.
79  |                from    date of first day of plot.
80  |                ndays   number of days to plot.
81  |                what    what to plot. (PLOT_PHY | PLOT_EMO ...)
82  */
83 static void
bioTextPlot(Date birth,Date from,int ndays,int what)84 bioTextPlot(Date birth, Date from, int ndays, int what)
85 {
86 #define WIDTH 53
87     int q;
88     char s[WIDTH + 2];
89     double val, sum;
90 
91     printf("Day of birth: %s\n", dateStr(birth));
92     if (what & PLOT_PHY)
93 	printf("[P]hysical   ");
94     if (what & PLOT_EMO)
95 	printf("[E]motional   ");
96     if (what & PLOT_INT)
97 	printf("[I]ntellectual   ");
98     if (what & PLOT_AVG)
99 	printf("[A]verage");
100     printf("\n");
101 
102     for (q = 0; q < 73; q++)
103 	printf("-");
104     printf("\n");
105     printf("YYYY-MM-DD  down%57s\n", "up    age");
106     for (q = 0; q < 73; q++)
107 	printf("-");
108     printf("\n");
109 
110     while (--ndays >= 0) {
111 	for (q = 0; q <= WIDTH; q++)
112 	    s[q] = ' ';
113 	s[q] = '\0';
114 	s[0] = s[WIDTH] = '|';
115 	s[WIDTH / 2] = '.';
116 	printf("%-10.10s  ", dateStr(from));
117 
118 	sum = val = f(birth, from, PHY_PERIOD);
119 	if (what & PLOT_PHY)
120 	    s[nearestInt(((val + 1.0) / 2.0) * WIDTH)] = 'P';
121 
122 	val = f(birth, from, EMO_PERIOD);
123 	sum += val;
124 	if (what & PLOT_EMO)
125 	    s[nearestInt(((val + 1.0) / 2.0) * WIDTH)] = 'E';
126 
127 	val = f(birth, from, INT_PERIOD);
128 	sum += val;
129 	if (what & PLOT_INT)
130 	    s[nearestInt(((val + 1.0) / 2.0) * WIDTH)] = 'I';
131 
132 	if (what & PLOT_AVG)
133 	    s[nearestInt((((sum / 3.0) + 1.0) / 2.0) * WIDTH)] = 'A';
134 
135 	printf("%s  %5d\n", s, dateGetNumDays(birth, from));
136 
137 	dateInc(&from);
138     }
139 }
140 
141 /*------------------------------------------------------------------------
142  |  NAME          version
143  |
144  |  FUNCTION      Show the version of this program, and exit.
145  */
146 static void
version(void)147 version(void)
148 {
149     printf(
150       "biorythm " VERSION ", by Sverre H. Huseby\n"
151     );
152     exit(0);
153 }
154 
155 /*------------------------------------------------------------------------
156  |  NAME          usage
157  |
158  |  FUNCTION      Show the usage of this program, and exit.
159  */
160 static void
usage(void)161 usage(void)
162 {
163     printf(
164       "\n"
165       "usage: %s [option] date-of-birth first-day-of-plot number-of-days\n"
166       "\n"
167       "  -a, --average        plot the average of all three curves\n"
168       "  -e, --emotional      plot the emotional curve (default)\n"
169       "  -h, --help           display this help and exit\n"
170       "  -i, --intellectual   plot the intellectual curve (default)\n"
171       "  -p, --physical       plot the physical curve (default)\n"
172       "  -V, --version        output version information and exit\n"
173       "\n"
174       "Dates must be specified as YYYY-MM-DD (ISO date format).\n"
175       "\n",
176       msgGetName()
177     );
178     exit(0);
179 }
180 
181 /*-----------------------------------------------------------------------+
182 |  PUBLIC FUNCTIONS                                                      |
183 +-----------------------------------------------------------------------*/
184 
185 int
main(int argc,char * argv[])186 main(int argc, char *argv[])
187 {
188     int  ndays = 0, what = 0;
189     int  average = 0, emotional = 0, intellectual = 0, physical = 0;
190     Date birth, from;
191     optStruct opt[] = {
192       /* short long            type        var/func       special       */
193         { 'h', "help",         OPT_FLAG,   usage,         OPT_CALLFUNC },
194         { 'a', "average",      OPT_FLAG,   &average,      0            },
195         { 'e', "emotional",    OPT_FLAG,   &emotional,    0            },
196         { 'i', "intellectual", OPT_FLAG,   &intellectual, 0            },
197         { 'p', "physical",     OPT_FLAG,   &physical,     0            },
198         { 'V', "version",      OPT_FLAG,   version,       OPT_CALLFUNC },
199         { 0, 0, OPT_END, 0, 0 }  /* no more options */
200     };
201 
202     msgSetName(argv[0]);
203     optParseOptions(&argc, argv, opt, 0);
204     if (argc != 4)
205 	usage();
206 
207     if (average)
208 	what |= PLOT_AVG;
209     if (emotional)
210 	what |= PLOT_EMO;
211     if (intellectual)
212 	what |= PLOT_INT;
213     if (physical)
214 	what |= PLOT_PHY;
215     if (!what)
216 	what = PLOT_EMO | PLOT_INT | PLOT_PHY;
217 
218     birth = dateFromStr(argv[1]);
219     from = dateFromStr(argv[2]);
220     ndays = atoi(argv[3]);
221     if (ndays < 1 || ndays > 500)
222 	msgFatal("number of days must be in [1, 500]\n");
223 
224     bioTextPlot(birth, from, ndays, what);
225 
226     return 0;
227 }
228