Biorhythms

From Rosetta Code
Revision as of 12:29, 31 May 2022 by Depperm (talk | contribs)
Task
Biorhythms
You are encouraged to solve this task according to the task description, using any language you may know.

For a while in the late 70s, the pseudoscience of biorhythms was popular enough to rival astrology, with kiosks in malls that would give you your weekly printout. It was also a popular entry in "Things to Do with your Pocket Calculator" lists. You can read up on the history at Wikipedia, but the main takeaway is that unlike astrology, the math behind biorhythms is dead simple.

It's based on the number of days since your birth. The premise is that three cycles of unspecified provenance govern certain aspects of everyone's lives – specifically, how they're feeling physically, emotionally, and mentally. The best part is that not only do these cycles somehow have the same respective lengths for all humans of any age, gender, weight, genetic background, etc, but those lengths are an exact number of days. And the pattern is in each case a perfect sine curve. Absolutely miraculous!

To compute your biorhythmic profile for a given day, the first thing you need is the number of days between that day and your birth, so the answers in Days between dates are probably a good starting point. (Strictly speaking, the biorhythms start at 0 at the moment of your birth, so if you know time of day you can narrow things down further, but in general these operate at whole-day granularity.) Then take the residue of that day count modulo each of the the cycle lengths to calculate where the day falls on each of the three sinusoidal journeys.

The three cycles and their lengths are as follows:

Cycle Length
Physical 23 days
Emotional 28 days
Mental 33 days

The first half of each cycle is in "plus" territory, with a peak at the quarter-way point; the second half in "minus" territory, with a valley at the three-quarters mark. You can calculate a specific value between -1 and +1 for the kth day of an n-day cycle by computing sin( 2πk / n ). The days where a cycle crosses the axis in either direction are called "critical" days, although with a cycle value of 0 they're also said to be the most neutral, which seems contradictory.

The task: write a subroutine, function, or program that will, given a birthdate and a target date, output the three biorhythmic values for the day. You may optionally include a text description of the position and the trend (e.g. "up and rising", "peak", "up but falling", "critical", "down and falling", "valley", "down but rising"), an indication of the date on which the next notable event (peak, valley, or crossing) falls, or even a graph of the cycles around the target date. Demonstrate the functionality for dates of your choice.

Example run of my Raku implementation: <lang sh>raku br.raku 1943-03-09 1972-07-11</lang>

Output:
Day 10717:
Physical day 22: -27% (down but rising, next transition 1972-07-12)
Emotional day 21: valley
Mental day 25: valley

Double valley! This was apparently not a good day for Mr. Fischer to begin a chess tournament...

11l

Translation of: Python

<lang 11l>F biorhythms(birthdate_str, targetdate_str)

  ‘
   Print out biorhythm data for targetdate assuming you were
   born on birthdate.
   birthdate and targetdata are strings in this format:
   YYYY-MM-DD e.g. 1964-12-26
  ’
  print(‘Born: ’birthdate_str‘ Target: ’targetdate_str)
  V birthdate = time:strptime(birthdate_str, ‘%Y-%m-%d’)
  V targetdate = time:strptime(targetdate_str, ‘%Y-%m-%d’)
  V days = (targetdate - birthdate).days()
  print(‘Day: ’days)
  V cycle_labels = [‘Physical’, ‘Emotional’, ‘Mental’]
  V cycle_lengths = [23, 28, 33]
  V quadrants = [(‘up and rising’, ‘peak’), (‘up but falling’, ‘transition’), (‘down and falling’, ‘valley’), (‘down but rising’, ‘transition’)]
  L(i) 3
     V label = cycle_labels[i]
     V length = cycle_lengths[i]
     V position = days % length
     V quadrant = Int(floor((4 * position) / length))
     V percentage = Int(round(100 * sin(2 * math:pi * position / length), 0))
     V transition_date = (targetdate + TimeDelta(days' floor((quadrant + 1) / 4 * length) - position)).strftime(‘%Y-%m-%d’)
     V (trend, next) = quadrants[quadrant]
     String description
     I percentage > 95
        description = ‘peak’
     E I percentage < -95
        description = ‘valley’
     E I abs(percentage) < 5
        description = ‘critical transition’
     E
        description = percentage‘% (’trend‘, next ’next‘ ’transition_date‘)’
     print(label‘ day ’position‘: ’description)

biorhythms(‘2043-03-09’, ‘2072-07-11’)</lang>

Output:
Born: 2043-03-09 Target: 2072-07-11
Day: 10717
Physical day 22: -27% (down but rising, next transition 2072-07-12)
Emotional day 21: valley
Mental day 25: valley

C

Translation of: Locomotive Basic

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <math.h>

int day(int y, int m, int d) {

   return 367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9 + d - 730530;

}

void cycle(int diff, int l, char *t) {

   int p = round(100 * sin(2 * M_PI * diff / l));
   printf("%12s cycle: %3i%%", t, p);
   if (abs(p) < 15)
       printf(" (critical day)");
   printf("\n");

}

int main(int argc, char *argv[]) {

   int diff;
   if (argc < 7) {
       printf("Usage:\n");
       printf("cbio y1 m1 d1 y2 m2 d2\n");
       exit(1);
   }
   diff = abs(day(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]))
            - day(atoi(argv[4]), atoi(argv[5]), atoi(argv[6])));
   printf("Age: %u days\n", diff);
   cycle(diff, 23, "Physical");
   cycle(diff, 28, "Emotional");
   cycle(diff, 33, "Intellectual");

}</lang> Test: <lang bash>gcc -o cbio cbio.c -lm ./cbio 1972 7 11 1943 3 9 Age: 10717 days

   Physical cycle: -27%
  Emotional cycle: -100%

Intellectual cycle: -100%</lang>

Common Lisp

Translation of: Locomotive Basic

<lang lisp>;;;; Common Lisp biorhythms

Get the days to J2000
FNday only works between 1901 to 2099 - see Meeus chapter 7

(defun day (y m d)

   (+ (truncate (* -7 (+ y (truncate (+ m 9) 12))) 4) 
      (truncate (* 275 m) 9) d -730530 (* 367 y)))
Get the difference in days between two dates

(defun diffday (y1 m1 d1 y2 m2 d2)

   (abs (- (day y2 m2 d2) (day y1 m1 d1))))
Print state of a single cycle

(defun print-cycle (diff len nm)

   (let ((perc (round (* 100 (sin (* 2 pi diff (/ 1 len)))))))
         (format t "~A cycle: ~D% " nm perc)
         (if (< (abs perc) 15)
             (format t "(critical)~%")
             (format t "~%"))))
Print all cycles

(defun print-bio (y1 m1 d1 y2 m2 d2)

   (let ((diff (diffday y1 m1 d1 y2 m2 d2)))
         (format t "Age in days: ~D ~%" diff)
         (print-cycle diff 23 "physical")
         (print-cycle diff 28 "emotional")
         (print-cycle diff 33 "intellectual")))</lang>
Output:
* (print-bio 1972 7 11 1943 3 9)
Age in days: 10717 
physical cycle: -27% 
emotional cycle: -100% 
intellectual cycle: -100% 

Delphi

Library: System.Math
Translation of: Go

<lang Delphi> program Biorhythms;

{$APPTYPE CONSOLE}

uses

 System.SysUtils,
 System.Math;

var

 cycles: array[0..2] of string = ('Physical day ', 'Emotional day', 'Mental day   ');
 lengths: array[0..2] of Integer = (23, 28, 33);
 quadrants: array[0..3] of array[0..1] of string = (('up and rising', 'peak'),
   ('up but falling', 'transition'), ('down and falling', 'valley'), ('down but rising',
   'transition'));
 datePairs: array[0..2] of array[0..1] of string = (('1943-03-09', '1972-07-11'),
   ('1809-01-12', '1863-11-19'), ('1809-02-12', '1863-11-19') // correct DOB for Abraham Lincoln
   );

procedure Check(err: string); begin

 if not err.IsEmpty then
   raise Exception.Create(err);

end;

function ParseDate(sDate: string; var Date: TDateTime): string; var

 dtFormat: TFormatSettings;

begin

 Result := ;
 with dtFormat do
 begin
   DateSeparator := '-';
   ShortDateFormat := 'yyyy-mm-dd';
 end;
 try
   Date := StrtoDateTime(sDate, dtFormat);
 except
   on E: Exception do
     Result := E.Message;
 end;

end;

function DateToStr(dt: TDateTime): string; var

 dtFormat: TFormatSettings;

begin

 Result := ;
 with dtFormat do
 begin
   DateSeparator := '-';
   ShortDateFormat := 'yyyy-mm-dd';
 end;
 Result := DateTimeToStr(dt, dtFormat);

end;

// Parameters assumed to be in YYYY-MM-DD format. procedure CalcBiorhythms(birthDate, targetDate: string); var

 bd, td: TDateTime;
 days: Integer;

begin

 Check(ParseDate(birthDate, bd));
 Check(ParseDate(targetDate, td));
 days := Trunc(td - bd);
 writeln('Born ', birthDate, ', Target ', targetDate);
 Writeln('Days ', days);
 for var i := 0 to 2 do
 begin
   var len := lengths[i];
   var cycle := cycles[i];
   var position := days mod len;
   var quadrant: Integer := trunc(position * 4 / len);
   var percent := sin(2.0 * PI * position / len);
   percent := floor(percent * 1000) / 10;
   var descript := ;
   if percent > 95 then
     descript := ' peak'
   else if percent < -95 then
     descript := ' valley'
   else if abs(percent) < 5 then
     descript := ' critical transition'
   else
   begin
     var daysToAdd := trunc((quadrant + 1) * len / 4 - position);
     var transition := td + daysToAdd;
     var trend := quadrants[quadrant, 0];
     var next := quadrants[quadrant, 1];
     var transStr := DateToStr(transition);
     var percentRounded := percent;
     descript := format('%5.3f%% (%s, next %s %s)', [percentRounded, trend,
       next, transStr]);
   end;
   writeln(format('%s %2d : %s', [cycle, position, descript]));
 end;
 writeln;

end;

begin

 for var i := 0 to High(datePairs) do
   CalcBiorhythms(datePairs[i, 0], datePairs[i, 1]);
 readln;

end.</lang>

Emacs Lisp

For Emacs, it makes sense to implement this as an interactive command which is personalized to the user's birthdate and computes the biorhythm for today. So if you put this code into your .emacs file (note that the birthdate has to be in MDY order in Emacs!): <lang lisp>(require 'calendar)

(setq biorhythm-birthdate '(3 16 1953))

(defun biorhythm ()

 "Show today's biorhythm."
 (interactive)
 (let* ((diff (abs (- (string-to-number (calendar-astro-date-string
              biorhythm-birthdate)) (string-to-number
              (calendar-astro-date-string)))))
        (rhyt '(23 28 33))
        (perc (mapcar (lambda (x) (round (* 100 (sin
              (* 2 pi diff (/ 1.0 x)))))) rhyt)))
   (message "age: %i  physical: %i%%  emotional: %i%%  intellectual: %i%%"
            diff (car perc) (cadr perc) (caddr perc))))</lang>

Then typing "M-x biorhythm" will produce this output in the Emacs echo area on 2022-04-21:

age: 25238  physical: 94%  emotional: 78%  intellectual: -97%

Factor

<lang factor>USING: calendar calendar.parser formatting io kernel math math.constants math.functions ;

days-between ( ymd-str ymd-str -- n )
   [ ymd>timestamp ] bi@ time- duration>days abs ;
trend ( pos len -- str ) / 4 * floor 3 divisor? "↑" "↓" ? ;
percent ( pos len -- x ) [ 2pi * ] [ / sin 100 * ] bi* ;
.day ( days cycle-length day-type -- )
   write [ mod ] keep [ drop ] [ percent ] [ trend ] 2tri
   " day %d: %.1f%%%s\n" printf ;
.biorhythm ( ymd-str ymd-str -- )
   2dup "Born %s, Target %s\n" printf days-between dup
   "Day %d\n" printf
   [ 23 "Physical" .day ]
   [ 28 "Emotional" .day ]
   [ 33 "Mental" .day ] tri ;

"1809-02-12" "1863-11-19" .biorhythm</lang>

Output:
Born 1809-02-12, Target 1863-11-19
Day 20003
Physical day 16: -94.2%↓
Emotional day 11: 62.3%↓
Mental day 5: 81.5%↑

FOCAL

Translation of: Locomotive Basic

<lang FOCAL>1.01 T "Enter birthdate (y,m,d)",! 1.02 ASK Y,M,D 1.03 D 2; S BZ=Z 1.04 T "Enter today's date (y,m,d)",! 1.05 ASK Y,M,D 1.06 D 2; S DI=Z - BZ 1.07 T %6,"Age in days", DI,! 1.08 T "Physical cycle: " 1.09 S L=23; D 3 1.10 T "Emotional cycle: " 1.11 S L=28; D 3 1.12 T "Intellectual cycle: " 1.13 S L=33; D 3 1.14 Q

2.1 S QA = FITR((M + 9) / 12) 2.2 S QB = FITR(275 * M / 9) 2.3 S QC = FITR(7 * (Y + QA) / 4) 2.4 S Z = 367 * Y - QC + QB + D - 730530

3.1 S P = 100 * FSIN(2*3.1415926536*DI/L) 3.2 T %3,P,"%" 3.3 I (FABS(P)-15)4.1,5.1,5.1

4.1 T " CRITICAL",!

5.1 T !</lang>

Output:
*G
Enter birthdate (y,m,d)
:1943,:3,:9
Enter today's date (y,m,d)
:1972,:7,:11
Age in days=  10717
Physical cycle:     =- 27%
Emotional cycle:    =-100%
Intellectual cycle: =-100%

Fortran

<lang Fortran>C ------------------------------------------------------------------

     PROGRAM BIORHYTHM

C ------------------------------------------------------------------

     DOUBLE PRECISION GETJD
     CHARACTER*3 DOW
     
     DOUBLE PRECISION JD0, JD1, JD2, PI2, DIF
     
     INTEGER BYEAR, BMON, BDAY, TYEAR, TMON, TDAY
     INTEGER I, J, PHY, EMO, MEN, NDAY, DNUM, YR, DOY
     CHARACTER*3 DNAME
     CHARACTER*1 GRID, ROW(65)

C ------------------------------------------------------------------

     PI2 = ACOS(-1.0D0)*2.0D0
     
     WRITE(*,*) 'ENTER YOUR BIRTHDAY YYYY MM DD'
     READ(*,*) BYEAR, BMON, BDAY
     WRITE(*,*) 'ENTER START DATE YYYY MM DD'
     READ(*,*) TYEAR, TMON, TDAY
     WRITE(*,*) 'ENTER NUMBER OF DAYS TO PLOT'
     READ(*,*) NDAY
     JD0 = GETJD( TYEAR, 1,  1 )
     JD1 = GETJD( BYEAR, BMON, BDAY )
     JD2 = GETJD( TYEAR, TMON, TDAY )
     WRITE(*,1010)
     WRITE(*,1000) DOW(JD1), INT( JD2-JD1 )
     WRITE(*,1010)
     WRITE(*,1020)
     DO I=1,NDAY
        DIF = JD2 - JD1
        PHY = INT(3.3D1+3.2D1*SIN( PI2 * DIF / 2.3D1 ))
        EMO = INT(3.3D1+3.2D1*SIN( PI2 * DIF / 2.8D1 ))
        MEN = INT(3.3D1+3.2D1*SIN( PI2 * DIF / 3.3D1 ))
        IF ( PHY.LT.1  ) PHY = 1
        IF ( EMO.LT.1  ) EMO = 1
        IF ( MEN.LT.1  ) MEN = 1
        IF ( PHY.GT.65 ) PHY = 65
        IF ( EMO.GT.65 ) EMO = 65
        IF ( MEN.GT.65 ) MEN = 65
        
        DNAME = DOW(JD2)
        DOY = INT(JD2-JD0)+1
        IF ( DNAME.EQ.'SUN' ) THEN
           GRID = '.'
        ELSE
           GRID = ' '
        END IF
        DO J=1,65
           ROW(J) = GRID
        END DO
        ROW(1)  = '|'
        ROW(17) = ':'
        ROW(33) = '|'
        ROW(49) = ':'
        ROW(65) = '|'
        ROW(PHY) = 'P'
        ROW(EMO) = 'E'
        ROW(MEN) = 'M'
        IF ( PHY.EQ.EMO ) ROW(PHY) = '*'
        IF ( PHY.EQ.MEN ) ROW(PHY) = '*'
        IF ( EMO.EQ.MEN ) ROW(EMO) = '*'
        WRITE(*,1030) ROW,DNAME,DOY
        JD2 = JD2 + 1.0D0
     END DO
     WRITE(*,1010)

C ------------------------------------------------------------------

1000 FORMAT( 'YOU WERE BORN ON A (', A3, ') YOU WERE ',I0,
    $        ' DAYS OLD AT THE START.' )
1010 FORMAT( 75('=') )
1020 FORMAT( '-1',31X,'0',30X,'+1     DOY' )
1030 FORMAT( 1X,65A1, 1X, A3, 1X, I3 )
     STOP
     END

C ------------------------------------------------------------------

     FUNCTION DOW( JD )

C ------------------------------------------------------------------ C RETURN THE ABBREVIATION FOR THE DAY OF THE WEEK C JD JULIAN DATE - GREATER THAN 1721423.5 (JAN 1, 0001 SATURDAY) C ------------------------------------------------------------------

     DOUBLE PRECISION JD
     INTEGER IDX
     CHARACTER*3 DOW, NAMES(7)
     DATA NAMES/'SAT','SUN','MON','TUE','WED','THR','FRI'/
     IDX = INT(MODULO(JD-1.721423500D6,7.0D0)+1)
     DOW = NAMES(IDX)
     RETURN
     END
     

C ------------------------------------------------------------------

     FUNCTION ISGREG( Y, M, D )

C ------------------------------------------------------------------ C IS THIS DATE ON IN THE GREGORIAN CALENDAR C DATES BEFORE OCT 5 1582 ARE JULIAN C DATES AFTER OCT 14 1582 ARE GREGORIAN C DATES OCT 5-14 1582 INCLUSIVE DO NOT EXIST C ------------------------------------------------------------------ C YEAR 1-ANYTHING C MONTH 1-12 C DAY 1-31 C ------------------------------------------------------------------

     LOGICAL ISGREG
     INTEGER Y, M, D

C ------------------------------------------------------------------

     ISGREG=.TRUE.
     IF ( Y.LT.1582 ) GOTO 888
     IF ( Y.GT.1582 ) GOTO 999
     IF ( M.LT.10 )   GOTO 888
     IF ( M.GT.10 )   GOTO 999
     IF ( D.LT.5 )    GOTO 888
     IF ( D.GT.14 )   GOTO 999
     WRITE(*,*) Y,M,D,' DOES NOT EXIST'
     GOTO 999
888  CONTINUE
     ISGREG=.FALSE.
999  CONTINUE
     RETURN
     END
     

C ------------------------------------------------------------------

     FUNCTION GETJD( YEAR, MONTH, DAY )

C ------------------------------------------------------------------ C RETURN THE JULIAN DATE C YEAR 1-ANYTHING C MONTH 1-12 C DAY 1-31 C ------------------------------------------------------------------

     DOUBLE PRECISION GETJD
     INTEGER YEAR, MONTH, DAY
     INTEGER Y, M, D, A, B, P1, P2

C ------------------------------------------------------------------

     DOUBLE PRECISION TEMP
     LOGICAL ISGREG, IG
     IG = ISGREG( YEAR, MONTH, DAY )
     Y  = YEAR
     M  = MONTH
     D  = DAY
     IF (M.LT.3) THEN
        Y = Y - 1
        M = M + 12
     ENDIF
     IF (IG) THEN
        A =         FLOOR( DBLE(Y) * 1.0D-2 )
        B = 2 - A + FLOOR( DBLE(A) * 2.5D-1 )
     ELSE
        A = 0
        B = 0
     ENDIF
    
     P1 = FLOOR( 3.65250D2 * DBLE(Y + 4716) )
     P2 = FLOOR( 3.06001D1 * DBLE(M + 1) )
     GETJD = DBLE(P1 + P2 + D + B) - 1.5245D3
     
     RETURN
     END</lang>
Input:
 ENTER YOUR BIRTHDAY YYYY MM DD
1992 4 10
 ENTER START DATE YYYY MM DD
2021 02 14
 ENTER NUMBER OF DAYS TO PLOT
40
Output:
===========================================================================
YOU WERE BORN ON A (FRI) YOU WERE 10537 DAYS OLD AT THE START.
===========================================================================
-1                               0                              +1     DOY
 |...............:...............|...............:......P....E.M.| SUN  45
 |               :               |               :        E MP   | MON  46
 |               :               |               :  E    M      P| TUE  47
 |               :               |            E  :  M           P| WED  48
 |               :               |      E      M :             P | THR  49
 |               :               E        M      :         P     | FRI  50
 |               :       E       |  M            :   P           | SAT  51
 |...............:.E.........M...|...........P...:...............| SUN  52
 |           E   :     M         |   P           :               | MON  53
 |     E         :M         P    |               :               | TUE  54
 |  E        M   :  P            |               :               | WED  55
 E      M   P    :               |               :               | THR  56
 E   MP          :               |               :               | FRI  57
 E*              :               |               :               | SAT  58
 *..E............:...............|...............:...............| SUN  59
 *     E         :               |               :               | MON  60
 M  P        E   :               |               :               | TUE  61
 | M     P       : E             |               :               | WED  62
 |    M         P:       E       |               :               | THR  63
 |        M      :      P        E               :               | FRI  64
 |             M :              P|      E        :               | SAT  65
 |...............:...M...........|.......P....E..:...............| SUN  66
 |               :        M      |               P  E            | MON  67
 |               :              M|               :      P E      | TUE  68
 |               :               |     M         :           *   | WED  69
 |               :               |          M    :              *| THR  70
 |               :               |               :M             PE FRI  71
 |               :               |               :     M       PE| SAT  72
 |...............:...............|...............:.........*.E...| SUN  73
 |               :               |               :   P    E   M  | MON  74
 |               :               |           P   :  E           M| TUE  75
 |               :               |   P        E  :              M| WED  76
 |               :          P    |      E        :              M| THR  77
 |               :  P           E|               :             M | FRI  78
 |          P    :       E       |               :          M    | SAT  79
 |....P..........:.E.............|...............:.......M.......| SUN  80
 |P          E   :               |               :  M            | MON  81
 P     E         :               |             M :               | TUE  82
 P  E            :               |        M      :               | WED  83
 E  P            :               |  M            :               | THR  84
===========================================================================



FreeBASIC

Translation of: VBA

<lang freebasic>#define floor(x) ((x*2.0-0.5) Shr 1)

  1. define pi (4 * Atn(1))

Function Gregorian(db As String) As Integer

   Dim As Integer M, Y, D
   Y = Valint(Left(db,4)) :  M = Valint(Mid(db,6,2)) : D = Valint(Right(db,2))
   Dim As Integer N = (M+9) - Int((M+9)/12) * 12
   Dim As Integer W = Y - Int(N/10)
   Dim As Integer G = 365 * W + Int(W/4) - Int(W/100) + Int(W/400)
   G += Int((N*306+5)/10)+(D-1)
   Return G

End Function

Function Biorhythm(Birthdate As String, Targetdate As String) As String

   'Jagged Array
   Dim As String TextArray(4,2) = {{"up and rising", "peak"}, {"up but falling", "transition"}, {"down and falling", "valley"}, {"down but rising", "transition"}}
   
   Dim As Integer DaysBetween = Gregorian(Targetdate) - Gregorian(Birthdate)
   
   Dim As Integer positionP = DaysBetween Mod 23
   Dim As Integer positionE = DaysBetween Mod 28
   Dim As Integer positionM = DaysBetween Mod 33
   
   'return the positions - just to return something
   Biorhythm = Str(positionP) & "/" & Str(positionE) & "/" & Str(positionM)
   
   Dim As Integer quadrantP = Int(4 * positionP / 23)
   Dim As Integer quadrantE = Int(4 * positionE / 28)
   Dim As Integer quadrantM = Int(4 * positionM / 33)
   
   Dim As Single percentageP = Fix(100 * Sin(2 * pi * (positionP / 23)))
   Dim As Single percentageE = Fix(100 * Sin(2 * pi * (positionE / 28)))
   Dim As Single percentageM = Fix(100 * Sin(2 * pi * (positionM / 33)))
   
   Dim As Single transitionP = Val(Targetdate) + floor((quadrantP + 1) / 4 * 23) - positionP
   Dim As Single transitionE = Val(Targetdate) + floor((quadrantE + 1) / 4 * 28) - positionE
   Dim As Single transitionM = Val(Targetdate) + floor((quadrantM + 1) / 4 * 33) - positionM
   
   Dim As String textP, textE, textM, Header1Text, Header2Text
   Select Case percentageP
   Case Is > 95
       textP = "Physical day  " & positionP & " : " & "peak"
   Case Is < -95
       textP = "Physical day  " & positionP & " : " & "valley"
   Case -5 To 5
       textP = "Physical day  " & positionP & " : " & "critical transition"
   Case Else
       textP = "Physical day  " & positionP & " : " & percentageP & "% (" & TextArray(quadrantP,0) & ", next " & TextArray(quadrantP,1) & " " & transitionP & ")"
   End Select
   
   Select Case percentageE
   Case Is > 95
       textE = "Emotional day " & positionE & " : " & "peak"
   Case Is < -95
       textE = "Emotional day " & positionE & " : " & "valley"
   Case -5 To 5
       textE = "Emotional day " & positionE & " : " & "critical transition"
   Case Else
       textE = "Emotional day " & positionE & " : " & percentageE & "% (" & TextArray(quadrantE,0) & ", next " & TextArray(quadrantE,1) & " " & transitionE & ")"
   End Select
   
   Select Case percentageM
   Case Is > 95
       textM = "Mental day " & positionM & "    : " & "peak"
   Case Is < -95
       textM = "Mental day " & positionM & "    : " & "valley"
   Case -5 To 5
       textM = "Mental day " & positionM & "    : " & "critical transition"
   Case Else
       textM = "Mental day " & positionM & "    : " & percentageM & "% (" & TextArray(quadrantM,0) & ", next " & TextArray(quadrantM,1) & " " & transitionM & ")"
   End Select
   
   Header1Text = "Born " & Birthdate & ", Target " & Targetdate
   Header2Text = "Day " & DaysBetween
   
   Print Header1Text
   Print Header2Text
   Print textP
   Print textE
   Print textM
   Print

End Function

Biorhythm("1943-03-09", "1972-07-11") Biorhythm("1809-02-12", "1863-11-19") 'correct DOB for Abraham Lincoln Biorhythm("1809-01-12", "1863-11-19") </lang>


Go

Translation of: Wren

<lang go>package main

import (

   "fmt"
   "log"
   "math"
   "time"

)

const layout = "2006-01-02" // template for time.Parse

var cycles = [3]string{"Physical day ", "Emotional day", "Mental day "} var lengths = [3]int{23, 28, 33} var quadrants = [4][2]string{

   {"up and rising", "peak"},
   {"up but falling", "transition"},
   {"down and falling", "valley"},
   {"down but rising", "transition"},

}

func check(err error) {

   if err != nil {
       log.Fatal(err)
   }

}

// Parameters assumed to be in YYYY-MM-DD format. func biorhythms(birthDate, targetDate string) {

   bd, err := time.Parse(layout, birthDate)
   check(err)
   td, err := time.Parse(layout, targetDate)
   check(err)
   days := int(td.Sub(bd).Hours() / 24)
   fmt.Printf("Born %s, Target %s\n", birthDate, targetDate)
   fmt.Println("Day", days)
   for i := 0; i < 3; i++ {
       length := lengths[i]
       cycle := cycles[i]
       position := days % length
       quadrant := position * 4 / length
       percent := math.Sin(2 * math.Pi * float64(position) / float64(length))
       percent = math.Floor(percent*1000) / 10
       descript := ""
       if percent > 95 {
           descript = " peak"
       } else if percent < -95 {
           descript = " valley"
       } else if math.Abs(percent) < 5 {
           descript = " critical transition"
       } else {
           daysToAdd := (quadrant+1)*length/4 - position
           transition := td.Add(time.Hour * 24 * time.Duration(daysToAdd))
           trend := quadrants[quadrant][0]
           next := quadrants[quadrant][1]
           transStr := transition.Format(layout)
           descript = fmt.Sprintf("%5.1f%% (%s, next %s %s)", percent, trend, next, transStr)
       }
       fmt.Printf("%s %2d : %s\n", cycle, position, descript)
   }
   fmt.Println()

}

func main() {

   datePairs := [][2]string{
       {"1943-03-09", "1972-07-11"},
       {"1809-01-12", "1863-11-19"},
       {"1809-02-12", "1863-11-19"}, // correct DOB for Abraham Lincoln
   }
   for _, datePair := range datePairs {
       biorhythms(datePair[0], datePair[1])
   }

}</lang>

Output:
Born 1943-03-09, Target 1972-07-11
Day 10717
Physical day  22 : -27.0% (down but rising, next transition 1972-07-12)
Emotional day 21 :  valley
Mental day    25 :  valley

Born 1809-01-12, Target 1863-11-19
Day 20034
Physical day   1 :  26.9% (up and rising, next peak 1863-11-23)
Emotional day 14 :  critical transition
Mental day     3 :  54.0% (up and rising, next peak 1863-11-24)

Born 1809-02-12, Target 1863-11-19
Day 20003
Physical day  16 : -94.3% (down and falling, next valley 1863-11-20)
Emotional day 11 :  62.3% (up but falling, next transition 1863-11-22)
Mental day     5 :  81.4% (up and rising, next peak 1863-11-22)

J

Let's presume the content of file br.ijs : <lang J> use=: 'Use: ', (;:inv 2 {. ARGV) , ' YYYY-MM-DD YYYY-MM-DD'

3 :0 ::echo use

TAU=: 2p1  NB. tauday.com
require'plot ~addons/types/datetime/datetime.ijs'
span=: (i.7) + daysDiff&(1 0 0 0 1 0 1 0 ".;.1 -.&'-')~
Length=: 23 5 p. i. 3
arg=: Length *inv TAU * Length |/ span
brtable=: 1 o. arg
biorhythm=: 'title biorythms for the week ahead; key Physical Emotional Mental' plot (i.7) (j."1) brtable~
biorhythm/ 2 3 {::"0 1 ARGV
echo 'find new graph (plot.pdf) in directory ' , jpath '~temp/'
brs=. brtable/ 2 3 {::"0 1 ARGV
echo (a:,'values';'interpretation') ,: (4 10 $ 'days aheadPhysical  Emotional Mental     ') ; (1 3 # 6 6j1)&(":"0 1) L:0 (; |) brs ,~ i. 7

)

exit 0 </lang> Then bash commands could be

$ ijconsole /tmp/br.ijs help
Use: ijconsole /tmp/br.ijs YYYY-MM-DD YYYY-MM-DD
$ ijconsole  /tmp/br.ijs 1960-05-04 $( date '+%Y-%m-%d' )
find new graph (plot.pdf) in directory /home/username/j902-user/temp/
┌──────────┬──────────────────────────────────────────┬──────────────────────────────────────────┐
│          │values                                    │interpretation                            │
├──────────┼──────────────────────────────────────────┼──────────────────────────────────────────┤
│days ahead│     0     1     2     3     4     5     6│     0     1     2     3     4     5     6│
│Physical  │  _0.4  _0.6  _0.8  _0.9  _1.0  _1.0  _0.9│   0.4   0.6   0.8   0.9   1.0   1.0   0.9│
│Emotional │  _0.2   0.0   0.2   0.4   0.6   0.8   0.9│   0.2   0.0   0.2   0.4   0.6   0.8   0.9│
│Mental    │  _0.9  _0.9  _1.0  _1.0  _1.0  _0.9  _0.8│   0.9   0.9   1.0   1.0   1.0   0.9   0.8│
└──────────┴──────────────────────────────────────────┴──────────────────────────────────────────┘

Julia

Translation of: Raku

<lang julia>using Dates

const cycles = ["Physical" => 23, "Emotional" => 28,"Mental" => 33] const quadrants = [("up and rising", "peak"), ("up but falling", "transition"),

                  ("down and falling", "valley"), ("down but rising", "transition")]

function tellfortune(birthday::Date, date = today())

   days = (date - birthday).value
   target = (date - Date(0)).value
   println("Born $birthday, target date $date\nDay $days:")
   for (label, length) in cycles
       position = days % length
       quadrant = Int(floor((4 * position) / length)) + 1
       percentage = round(100 * sinpi(2 * position / length), digits=1)
       transition = target - position + (length * quadrant) ÷ 4
       trend, next = quadrants[quadrant]
       description = (percentage > 95) ? "peak" :
                     (percentage < -95) ? "valley" :
                     (abs(percentage) < 5) ? "critical transition" :
                     "$percentage% ($trend, next $next $(Date(0) + Dates.Day(transition)))"
       println("$label day $position: $description")
   end
   println()

end

tellfortune(Date("1943-03-09"), Date("1972-07-11")) tellfortune(Date("1809-01-12"), Date("1863-11-19")) tellfortune(Date("1809-02-12"), Date("1863-11-19"))

</lang>
Output:
Born 1943-03-09, target date 1972-07-11
Day 10717:
Physical day 22: -27.0% (down but rising, next transition 1972-07-12)
Emotional day 21: valley
Mental day 25: valley

Born 1809-01-12, target date 1863-11-19
Day 20034:
Physical day 1: 27.0% (up and rising, next peak 1863-11-23)
Emotional day 14: critical transition
Mental day 3: 54.1% (up and rising, next peak 1863-11-24)

Born 1809-02-12, target date 1863-11-19
Day 20003:
Physical day 16: -94.2% (down and falling, next valley 1863-11-20)
Emotional day 11: 62.3% (up but falling, next transition 1863-11-22)
Mental day 5: 81.5% (up and rising, next peak 1863-11-22)

Locomotive Basic

<lang locobasic>10 input "Birthday (y,m,d) ",y,m,d:gosub 3000 20 gosub 1000 30 bday = day 40 input "Today's date (y,m,d) ",y,m,d:gosub 3000 50 gosub 1000 60 diff = day - bday 70 print:print "Age in days:" tab(22) diff 80 t$ = "physical":l = 23:gosub 2000 90 t$ = "emotional":l = 28:gosub 2000 100 t$ = "intellectual":l = 33:gosub 2000 999 end 1000 ' Get the days to J2000 1010 ' FNday only works between 1901 to 2099 - see Meeus chapter 7 1020 day = 367 * y - 7 * (y + (m + 9) \ 12) \ 4 + 275 * m \ 9 + d - 730530 1030 return 2000 p = 100 * sin(2*pi*diff/l) 2010 print t$; " cycle: " tab(22) 2020 print using "+###"; int(p); 2030 print "%"; 2040 if abs(p) < 15 then print " (critical)" else print 2050 return 3000 if y < 1901 or y > 2099 then print "Year must be between 1901 and 2099!":run 3010 return</lang>

Output:
Birthday (y,m,d) 1943,3,9
Today's date (y,m,d) 1972,7,11

Age in days:          10717 
physical cycle:       -27%
emotional cycle:     -100%
intellectual cycle:  -100%

Lua

Translation of: Phix

<lang Lua>cycles = {"Physical day ", "Emotional day", "Mental day "} lengths = {23, 28, 33} quadrants = { {"up and rising", "peak"}, {"up but falling", "transition"}, {"down and falling", "valley"}, {"down but rising", "transition"}, }

function parse_date_string (birthDate) local year, month, day = birthDate:match("(%d+)-(%d+)-(%d+)") return {year=tonumber(year), month=tonumber(month), day=tonumber(day)} end

function days_diffeternce (d1, d2) if d1.year >= 1970 and d2.year >= 1970 then return math.floor(os.difftime(os.time(d2), os.time(d1))/(60*60*24)) else local t1 = math.max (1970-d1.year, 1970-d2.year) t1 = math.ceil(t1/4)*4 d1.year = d1.year + t1 d2.year = d2.year + t1 return math.floor(os.difftime(os.time(d2), os.time(d1))/(60*60*24)) end end

function biorhythms (birthDate, targetDate) local bd = parse_date_string(birthDate) local td = parse_date_string(targetDate) local days = days_diffeternce (bd, td)

print('Born: '.. birthDate .. ', Target: ' .. targetDate) print("Day: ", days) for i=1, #lengths do local len = lengths[i] local posn = days%len local quadrant = math.floor(posn/len*4)+1 local percent = math.floor(math.sin(2*math.pi*posn/len)*1000)/10 local cycle = cycles[i] local desc = percent > 95 and "peak" or percent < -95 and "valley" or math.abs(percent) < 5 and "critical transition" or "other" if desc == "other" then local t = math.floor(quadrant/4*len)-posn local qtrend, qnext = quadrants[quadrant][1], quadrants[quadrant][2] desc = percent .. '% (' .. qtrend .. ', next transition in ' .. t ..' days)' end print(cycle, posn..'/'..len, ': '.. desc) end print(' ') end

datePairs = { {"1943-03-09", "1972-07-11"}, {"1809-01-12", "1863-11-19"}, {"1809-02-12", "1863-11-19"}, {"2021-02-25", "2022-04-18"}, }

for i=1, #datePairs do biorhythms(datePairs[i][1], datePairs[i][2]) end</lang>

Output:
Born: 1943-03-09, Target: 1972-07-11
Day: 	10716
Physical day 	21/23	: -52.0% (down but rising, next transition in 2 days)
Emotional day	20/28	: valley
Mental day   	24/33	: valley
 
Born: 1809-01-12, Target: 1863-11-19
Day: 	20034
Physical day 	1/23	: 26.9% (up and rising, next transition in 4 days)
Emotional day	14/28	: critical transition
Mental day   	3/33	: 54.0% (up and rising, next transition in 5 days)
 
Born: 1809-02-12, Target: 1863-11-19
Day: 	20003
Physical day 	16/23	: -94.3% (down and falling, next transition in 1 days)
Emotional day	11/28	: 62.3% (up but falling, next transition in 3 days)
Mental day   	5/33	: 81.4% (up and rising, next transition in 3 days)
 
Born: 2021-02-25, Target: 2022-04-18
Day: 	416
Physical day 	2/23	: 51.9% (up and rising, next transition in 3 days)
Emotional day	24/28	: -78.2% (down but rising, next transition in 4 days)
Mental day   	20/33	: -61.9% (down and falling, next transition in 4 days)

Mathematica / Wolfram Language

<lang Mathematica>targetdate = "1972-07-11"; birthdate = "1943-03-09"; targetdate //= DateObject; birthdate //= DateObject;

cyclelabels = {"Physical", "Emotional", "Mental"}; cyclelengths = {23, 28, 33}; quadrants = {{"up and rising", "peak"}, {"up but falling",

   "transition"}, {"down and falling", "valley"}, {"down but rising",
    "transition"}};

d = QuantityMagnitude[DateDifference[birthdate, targetdate], "Days"]; Print["Day ", d, ":"]; Do[

label = cyclelabelsi;
length = cyclelengthsi;
position = Mod[d, length];
quadrant = Floor[4 (position + 1)/length];
percentage = Round[100*Sin[2*Pi*position/length]];
transitiondate = 
 DatePlus[targetdate, Floor[(quadrant + 1)/4*length] - position];
{trend, next} = quadrantsquadrant;
If[percentage > 95,
 description = "peak"
 ,
 If[percentage < -95,
  description = "valley"
  ,
  If[Abs[percentage] < 5,
   description = "critical transition"
   ,
   description = 
    ToString[percentage] <> "% (" <> trend <> ", next " <> next <> 
     " " <> DateString[transitiondate, "ISODate"] <> ")"
   ]
  ]
 ];
Print[label <> " day " <> ToString[position] <> ": " <> 
  description];
,
{i, 3}
]</lang>
Output:
Day 10717:
Physical day 22: -27% (down but rising, next transition 1972-07-17)
Emotional day 21: valley
Mental day 25: valley

Nim

Translation of: Go

<lang Nim>import math import strformat import times

type Cycle {.pure.} = enum Physical, Emotional, Mental

const

 Lengths: array[Cycle, int] = [23, 28, 33]
 Quadrants = [("up and rising", "peak"),
              ("up but falling", "transition"),
              ("down and falling", "valley"),
              ("down but rising", "transition")]
 DateFormat = "YYYY-MM-dd"
  1. ---------------------------------------------------------------------------------------------------

proc biorythms(birthDate: DateTime; targetDate: DateTime = now()) =

 ## Display biorythms data. Arguments are DateTime values.
 echo fmt"Born {birthDate.format(DateFormat)}, target date {targetDate.format(DateFormat)}"
 let days = (targetDate - birthDate).inDays
 echo "Day ", days
 for cycle, length in Lengths:
   let position = int(days mod length)
   let quadrant = int(4 * position / length)
   let percentage = round(100 * sin(2 * PI * (position / length)), 1)
   var description: string
   if percentage > 95:
     description = "peak"
   elif percentage < -95:
     description = "valley"
   elif abs(percentage) < 5:
     description = "critical transition"
   else:
     let (trend, next) = Quadrants[quadrant]
     let transition = targetDate + initDuration(days = (quadrant + 1) * length div 4 - position)
     description = fmt"{percentage}% ({trend}, next {next} {transition.format(DateFormat)})"
   echo fmt"{cycle} day {position}: {description}"
 echo ""
  1. ---------------------------------------------------------------------------------------------------

proc biorythms(birthDate, targetDate = "") =

 ## Display biorythms data. Arguments are strings in ISO format year-month-day.
 let date = if targetDate.len == 0: now() else: targetDate.parse(DateFormat)
 biorythms(birthDate.parse(DateFormat), date)
  1. ———————————————————————————————————————————————————————————————————————————————————————————————————

when isMainModule:

 biorythms("1943-03-09", "1972-07-11")
 biorythms("1809-01-12", "1863-11-19")
 biorythms("1809-02-12", "1863-11-19")</lang>
Output:
Born 1943-03-09, target date 1972-07-11
Day 10717
Physical day 22: -27.0% (down but rising, next transition 1972-07-12)
Emotional day 21: valley
Mental day 25: valley

Born 1809-01-12, target date 1863-11-19
Day 20034
Physical day 1: 27.0% (up and rising, next peak 1863-11-23)
Emotional day 14: critical transition
Mental day 3: 54.1% (up and rising, next peak 1863-11-24)

Born 1809-02-12, target date 1863-11-19
Day 20003
Physical day 16: -94.2% (down and falling, next valley 1863-11-20)
Emotional day 11: 62.3% (up but falling, next transition 1863-11-22)
Mental day 5: 81.5% (up and rising, next peak 1863-11-22)

Perl

Translation of: Raku

<lang perl>use strict; use warnings; use DateTime;

use constant PI => 2 * atan2(1, 0);

my %cycles = ( 'Physical' => 23, 'Emotional' => 28, 'Mental' => 33 ); my @Q = ( ['up and rising', 'peak'],

         ['up but falling',   'transition'],
         ['down and falling', 'valley'],
         ['down but rising',  'transition']
       );

my $target = DateTime->new(year=>1863, month=>11, day=>19); my $bday = DateTime->new(year=>1809, month=> 2, day=>12);

my $days = $bday->delta_days( $target )->in_units('days');

print "Day $days:\n"; for my $label (sort keys %cycles) {

   my($length) = $cycles{$label};
   my $position = $days % $length;
   my $quadrant = int $position / $length * 4;
   my $percentage = int(sin($position / $length * 2 * PI )*1000)/10;
   my $description;
   if    (    $percentage  >  95) { $description = 'peak' }
   elsif (    $percentage  < -95) { $description = 'valley' }
   elsif (abs($percentage) <   5) { $description = 'critical transition' }
   else {
       my $transition = $target->clone->add( days => (int(($quadrant + 1)/4 * $length) - $position))->ymd;
       my ($trend, $next) = @{$Q[$quadrant]};
       $description = sprintf "%5.1f%% ($trend, next $next $transition)", $percentage;
   }
   printf "%-13s %2d: %s", "$label day\n", $position, $description;

}</lang>

Output:
Day 20003:
Emotional day 11:  62.3% (up but falling, next transition 1863-11-22)
Mental day     5:  81.4% (up and rising, next peak 1863-11-22)
Physical day  16: -94.2% (down and falling, next valley 1863-11-20)

Phix

Translation of: Wren
with javascript_semantics
include timedate.e
 
constant cycles = {"Physical day ", "Emotional day", "Mental day   "},
         lengths = {23, 28, 33},
         quadrants = {{"up and rising",    "peak"},
                      {"up but falling",   "transition"},
                      {"down and falling", "valley"},
                      {"down but rising",  "transition"}}
 
procedure biorhythms(string birthDate, targetDate)
    timedate bd = parse_date_string(birthDate,{"YYYY-MM-DD"}),
             td = parse_date_string(targetDate,{"YYYY-MM-DD"})
    integer days = floor(timedate_diff(bd, td, DT_DAY)/(60*60*24))
    printf(1,"Born %s, Target %s\n",{birthDate,targetDate})
    printf(1,"Day %d\n",days)
    for i=1 to 3 do
        integer len = lengths[i],
                posn = remainder(days,len),
                quadrant = floor(posn/len*4)+1
        atom percent  = floor(sin(2*PI*posn/len)*1000)/10
        string cycle = cycles[i],
               desc = iff(percent>95 ? " peak" :
                      iff(percent<-95 ? " valley" :
                      iff(abs(percent)<5 ? " critical transition" : "other")))
        if desc == "other" then
            timedate t = adjust_timedate(td,timedelta(days:=floor(quadrant/4*len)-posn))
            string transition = format_timedate(t,"YYYY-MM-DD"),
                   {trend,next} = quadrants[quadrant]
            desc = sprintf("%5.1f%% (%s, next %s %s)", {percent, trend, next, transition})
        end if
        printf(1,"%s %2d : %s\n", {cycle, posn, desc})
    end for
    printf(1,"\n")
end procedure
 
constant datePairs = {
    {"1943-03-09", "1972-07-11"},
    {"1809-01-12", "1863-11-19"},
    {"1809-02-12", "1863-11-19"}  // correct DOB for Abraham Lincoln
}
for i=1 to length(datePairs) do biorhythms(datePairs[i][1], datePairs[i][2]) end for
Output:
Born 1943-03-09, Target 1972-07-11
Day 10717
Physical day  22 : -27.0% (down but rising, next transition 1972-07-12)
Emotional day 21 :  valley
Mental day    25 :  valley

Born 1809-01-12, Target 1863-11-19
Day 20034
Physical day   1 :  26.9% (up and rising, next peak 1863-11-23)
Emotional day 14 :  critical transition
Mental day     3 :  54.0% (up and rising, next peak 1863-11-24)

Born 1809-02-12, Target 1863-11-19
Day 20003
Physical day  16 : -94.3% (down and falling, next valley 1863-11-20)
Emotional day 11 :  62.3% (up but falling, next transition 1863-11-22)
Mental day     5 :  81.4% (up and rising, next peak 1863-11-22)


Python

<lang python> """

Python implementation of

http://rosettacode.org/wiki/Biorhythms

"""

from datetime import date, timedelta from math import floor, sin, pi

def biorhythms(birthdate,targetdate):

   """
   Print out biorhythm data for targetdate assuming you were
   born on birthdate.
   
   birthdate and targetdata are strings in this format:
   
   YYYY-MM-DD e.g. 1964-12-26
   """
   
   # print dates
   
   print("Born: "+birthdate+" Target: "+targetdate)    
   
   # convert to date types - Python 3.7 or later
   
   birthdate = date.fromisoformat(birthdate)
   targetdate = date.fromisoformat(targetdate)
   
   # days between
   
   days = (targetdate - birthdate).days
   
   print("Day: "+str(days))
   
   # cycle logic - mostly from Julia example
   
   cycle_labels = ["Physical", "Emotional", "Mental"]
   cycle_lengths = [23, 28, 33]
   quadrants = [("up and rising", "peak"), ("up but falling", "transition"),
                  ("down and falling", "valley"), ("down but rising", "transition")]
   
   for i in range(3):
       label = cycle_labels[i]
       length = cycle_lengths[i]
       position = days % length
       quadrant = int(floor((4 * position) / length))
       percentage = int(round(100 * sin(2 * pi * position / length),0))
       transition_date = targetdate + timedelta(days=floor((quadrant + 1)/4 * length) - position)
       trend, next = quadrants[quadrant]
       
       if percentage > 95:
           description = "peak"
       elif percentage < -95:
            description = "valley"
       elif abs(percentage) < 5:
            description = "critical transition"
       else:
            description = str(percentage)+"% ("+trend+", next "+next+" "+str(transition_date)+")"
       print(label+" day "+str(position)+": "+description)
   
   

biorhythms("1943-03-09","1972-07-11") </lang>

Output:
Born: 1943-03-09 Target: 1972-07-11
Day: 10717
Physical day 22: -27% (down but rising, next transition 1972-07-12)
Emotional day 21: valley
Mental day 25: valley

R

Also creates a plot showing past and future 30-day cycles

<lang R>bioR <- function(bDay, targetDay) {

   bDay <- as.Date(bDay)
   targetDay <- as.Date(targetDay)
   n <- as.numeric(targetDay - bDay)
   
   cycles <- c(23, 28, 33)
   mods <- n %% cycles
   bioR <- c(sin(2 * pi * mods / cycles))
   loc <- mods / cycles
   current <- ifelse(bioR > 0, ': Up', ': Down')
   current <- paste(current, ifelse(loc < 0.25 | loc > 0.75,
                                    "and rising",
                                    "and falling"))
   
   df <- data.frame(dates = seq.Date(from = targetDay - 30, 
                                     to = targetDay + 30,
                                     by = 1))
   df$n <- as.numeric(df$dates - bDay)
   df$P <- sin(2 * pi * (df$n %% cycles[1]) / cycles[1])
   df$E <- sin(2 * pi * (df$n %% cycles[2]) / cycles[2])
   df$M <- sin(2 * pi * (df$n %% cycles[3]) / cycles[3])
   
   plot(df$dates, df$P, col = 'blue', 
        main = paste(targetDay, 'Biorhythm for Birthday on', bDay),
        xlab = "",
        ylab = "Intensity")
   points(df$dates, df$E, col = 'green')
   points(df$dates, df$M, col = 'red')
   abline(v = targetDay)
   legend('topleft', legend = c("Phys", "Emot", "Ment"),
          col =c("blue", "green", "red"),
          cex = 0.8,
          pch = 21)
   
   cat(paste0('Birthday = ', as.character(bDay),
              '\nTarget Date = ', as.character(targetDay),
              '\n', n, ' days',
              '\nPhysical = ', mods[1], current[1],
              '\nEmotional = ', mods[2], current[2],
              '\nMental = ', mods[3], current[3]))

}

bioR('1943-03-09', '1972-07-11')</lang>

Output:
Birthday = 1943-03-09
Target Date = 1972-07-11
10717 days
Physical = 22: Down and rising
Emotional = 21: Down and falling
Mental = 25: Down and rising

Raku

<lang perl6>#!/usr/bin/env raku unit sub MAIN($birthday=%*ENV<BIRTHDAY>, $date = Date.today()) {

my %cycles = ( :23Physical, :28Emotional, :33Mental ); my @quadrants = [ ('up and rising', 'peak'),

                 ('up but falling',   'transition'),
                 ('down and falling', 'valley'),
                 ('down but rising',  'transition') ];

if !$birthday {

   die "Birthday not specified.\n" ~
       "Supply --birthday option or set \$BIRTHDAY in environment.\n";

}

my ($bday, $target) = ($birthday, $date).map: { Date.new($_) }; my $days = $target - $bday;

say "Day $days:"; for %cycles.sort(+*.value)».kv -> ($label, $length) {

   my $position = $days % $length;
   my $quadrant = floor($position / $length * 4);
   my $percentage = floor(sin($position / $length * 2 * π )*1000)/10;
   my $description;
   if $percentage > 95 {
       $description = 'peak';
   } elsif $percentage < -95 {
       $description = 'valley'; 
   } elsif abs($percentage) < 5 {
       $description = 'critical transition'
   } else {
       my $transition = $target + floor(($quadrant + 1)/4 * $length) - $position;
       my ($trend, $next) = @quadrants[$quadrant];
       $description = "$percentage% ($trend, next $next $transition)";
   }
   say "$label day $position: $description";
 }

}</lang>

Output:
$ br 1809-01-12 1863-11-19
Day 20034:
Physical day 1: 26.9% (up and rising, next peak 1863-11-23)
Emotional day 14: critical transition
Mental day 3: 54% (up and rising, next peak 1863-11-24)

REXX

The   daysbet2   (REXX program) is used   (invoked on line 3 of this program)   to calculate the number of days between two dates,
where the dates can be in several formats:

       ,            (a comma)        indicates today's date
       *            (an asterisk)    indicates today's date 
       yyyy-mm-dd   where yyyy may be a 2- or 4-digit year, mm may be a 1- or 2-digit month, dd may be a 1- or 2-digit day of month
       mm/dd/yyyy   (as above)
       mm/dd        (as above),  but the current year is assumed
       dd\mm\yyyy   (as above)
       dd\mm        (as above),  but the current year is assumed

It is usual to use the birth date of a person. <lang>/*REXX pgm shows the states of a person's biorhythms (physical, emotional, intellectual)*/ parse arg birthdate targetDate . /*obtain one or two dates from the C.L.*/ days= daysbet2(birthdate targetDate) /*invoke the 2nd version of a REXX pgm.*/ if days==0 then do; say; say 'The two dates specified are exacty the same.'; exit 1

                end

cycles= 'physical emotional intellectual' /*the names of each biorhythm cycle*/ cycle = 'negative neutral positive' /* " states of " " " */ @.1= 23; @.2= 28; @.3= 33 /* " # of days in " " " */ pid2= pi() * 2 * days /*calculate pi * t * number─of─days. */

      do j=1  for 3
      state= 2   +   sign( sin( pid2 / @.j) )   /*obtain state for each biorhythm cycle*/
      say 'biorhythm for the'  right(word(cycles,j),12)   "cycle is"   word(cycle, state)
      end   /*j*/                               /* [↑]   get state for each biorhythm. */

exit 0 /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ pi: pi= 3.1415926535897932384626433832795028841971693993751058209749445923078; return pi r2r: return arg(1) // (pi() * 2) /*normalize radians ──► a unit circle. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ sin: procedure; parse arg x; x= r2r(x); _= x; numeric fuzz min(5, max(1, digits() -3))

     if x=pi * .5         then return 1;         if x==pi*1.5  then return -1
     if abs(x)=pi | x=0   then return 0;         q= x*x;       z= x
       do k=2  by 2  until p=z;   p= z;   _= -_ *q/(k*k+k);    z= z+_;   end;    return z</lang>
output   when using the input of:     6/9/1946

Note:   today's date is being used,   today is the 6th of September, 2020.

biorhythm for the     physical cycle is positive
biorhythm for the    emotional cycle is neutral
biorhythm for the intellectual cycle is negative

Ruby

<lang Ruby>require 'date' CYCLES = {physical: 23, emotional: 28, mental: 33}

def biorhythms(date_of_birth, target_date = Date.today.to_s)

 days_alive = Date.parse(target_date) - Date.parse(date_of_birth) 
 CYCLES.each do |name, num_days|
   cycle_day = days_alive % num_days
   state = case cycle_day
     when 0, num_days/2 then "neutral"
     when (1.. num_days/2) then "positive"
     when (num_days/2+1..num_days) then "negative"
   end
   puts "%-10s: cycle day %2s, %s" % [name, cycle_day.to_i, state]    
 end

end

biorhythms("1943-03-09", "1972-07-11") </lang>

Output:
physical  : cycle day 22, negative
emotional : cycle day 21, negative
mental    : cycle day 25, negative

Tcl

Works with: Wish

A graphing version using Tcl+Tk: <lang tcl>#!/usr/bin/env wish

  1. Biorhythm calculator

set today [clock format [clock seconds] -format %Y-%m-%d ] proc main [list birthday [list target $today]] {

 set day [days-between $birthday $target]
 array set cycles { 
   Physical  {23 red}
   Emotional {28 green}
   Mental    {33 blue}
 }
 set pi [expr atan2(0,-1)]
 canvas .c -width 306 -height 350 -bg black
 .c create rectangle 4 49 306 251 -outline grey
 .c create line 5 150 305 150 -fill grey
 .c create line 145 50 145 250 -fill cyan
 .c create text 145 15 -text "$target" -fill cyan 
 .c create text 145 30 -text "(Day $day)" -fill cyan 
 set ly 305
 foreach {name data} [array get cycles] {
   lassign $data length color
   .c create text 60 $ly -anchor nw -text $name -fill $color
   set pos [expr $day % $length]
   for {set dd -14} {$dd <= 16} {incr dd} {
     set d [expr $pos + $dd]
     set x [expr 145 + 10 * $dd]
     .c create line $x 145 $x 155 -fill grey
     set v [expr sin(2*$pi*$d/$length)]
     set y [expr 150 - 100 * $v]
     if {$dd == 0} {
       .c create text 10 $ly -anchor nw \
           -text "[format %+04.1f%% [expr $v * 100]]" -fill $color
     }
     if [info exists ox] {
       .c create line $ox $oy $x $y -fill $color
     }
     set ox $x
     set oy $y
   }
   unset ox oy
   set ly [expr $ly - 25]
 }
 pack .c

}

proc days-between {from to} {

 expr int([rd $to] - [rd $from])

}

  1. parse an (ISO-formatted) date into a day number

proc rd {date} {

 lassign [scan $date %d-%d-%d] year month day
 set elapsed [expr $year - 1]
 expr {$elapsed * 365 +
      floor($elapsed/4) -
      floor($elapsed/100) +
      floor($elapsed/400) +
      floor( (367*$month-362)/12 ) +
      ($month < 3 ? 0 : ([is-leap $year] ? -1 : -2)) +
      $day}

}

proc is-leap {year} {

 expr {$year % 4 == 0 && ($year % 100 || $year % 400 == 0)}

}

main {*}$argv</lang>

Output:

Output of <lang sh>wish br.wish 1809-02-12 1863-11-19</lang> - Lincoln's biorhythms at Gettysburg: https://i.imgur.com/U2izZOM.png

VBA

Translation of: Wren

<lang vb>Function Biorhythm(Birthdate As Date, Targetdate As Date) As String

'Jagged Array TextArray = Array(Array("up and rising", "peak"), Array("up but falling", "transition"), Array("down and falling", "valley"), Array("down but rising", "transition"))

DaysBetween = Targetdate - Birthdate

positionP = DaysBetween Mod 23 positionE = DaysBetween Mod 28 positionM = DaysBetween Mod 33

'return the positions - just to return something Biorhythm = CStr(positionP) & "/" & CStr(positionE) & "/" & CStr(positionM)

quadrantP = Int(4 * positionP / 23) quadrantE = Int(4 * positionE / 28) quadrantM = Int(4 * positionM / 33)

percentageP = Round(100 * Sin(2 * WorksheetFunction.Pi * (positionP / 23)), 1) percentageE = Round(100 * Sin(2 * WorksheetFunction.Pi * (positionE / 28)), 1) percentageM = Round(100 * Sin(2 * WorksheetFunction.Pi * (positionM / 33)), 1)

transitionP = Targetdate + WorksheetFunction.Floor((quadrantP + 1) / 4 * 23, 1) - positionP transitionE = Targetdate + WorksheetFunction.Floor((quadrantE + 1) / 4 * 28, 1) - positionE transitionM = Targetdate + WorksheetFunction.Floor((quadrantM + 1) / 4 * 33, 1) - positionM

Select Case True

   Case percentageP > 95
       textP = "Physical day " & positionP & " : " & "peak"
   Case percentageP < -95
       textP = "Physical day " & positionP & " : " & "valley"
   Case percentageP < 5 And percentageP > -5
       textP = "Physical day " & positionP & " : " & "critical transition"
   Case Else
       textP = "Physical day " & positionP & " : " & percentageP & "% (" & TextArray(quadrantP)(0) & ", next " & TextArray(quadrantP)(1) & " " & transitionP & ")"

End Select

Select Case True

   Case percentageE > 95
       textE = "Emotional day " & positionE & " : " & "peak"
   Case percentageE < -95
       textE = "Emotional day " & positionE & " : " & "valley"
   Case percentageE < 5 And percentageE > -5
       textE = "Emotional day " & positionE & " : " & "critical transition"
   Case Else
       textE = "Emotional day " & positionE & " : " & percentageE & "% (" & TextArray(quadrantE)(0) & ", next " & TextArray(quadrantE)(1) & " " & transitionE & ")"

End Select

Select Case True

   Case percentageM > 95
       textM = "Mental day " & positionM & " : " & "peak"
   Case percentageM < -95
       textM = "Mental day " & positionM & " : " & "valley"
   Case percentageM < 5 And percentageM > -5
       textM = "Mental day " & positionM & " : " & "critical transition"
   Case Else
       textM = "Mental day " & positionM & " : " & percentageM & "% (" & TextArray(quadrantM)(0) & ", next " & TextArray(quadrantM)(1) & " " & transitionM & ")"

End Select

Header1Text = "Born " & Birthdate & ", Target " & Targetdate Header2Text = "Day " & DaysBetween

'Print Result Debug.Print Header1Text Debug.Print Header2Text Debug.Print textP Debug.Print textE Debug.Print textM Debug.Print ""

End Function</lang>

Output:
Born 09.03.1943, Target 11.07.1972
Day 10717
Physical day 22 : -27% (down but rising, next transition 12.07.1972)
Emotional day 21 : valley
Mental day 25 : valley

Born 12.02.1809, Target 19.11.1863
Day 20003
Physical day 16 : -94.2% (down and falling, next valley 20.11.1863)
Emotional day 11 : 62.3% (up but falling, next transition 22.11.1863)
Mental day 5 : 81.5% (up and rising, next peak 22.11.1863)

Born 12.01.1809, Target 19.11.1863
Day 20034
Physical day 1 : 27% (up and rising, next peak 23.11.1863)
Emotional day 14 : critical transition
Mental day 3 : 54.1% (up and rising, next peak 24.11.1863)

Vlang

Translation of: Go

<lang vlang>import time import math

const cycles = ["Physical day ", "Emotional day", "Mental day "] const lengths = [23, 28, 33] const quadrants = [

   ["up and rising", "peak"],
   ["up but falling", "transition"],
   ["down and falling", "valley"],
   ["down but rising", "transition"],

]

// Parameters assumed to be in YYYY-MM-DD format. fn biorhythms(birth_date string, target_date string) ? {

   bd := time.parse_iso8601(birth_date)?
   td := time.parse_iso8601(target_date)?
   days := int((td-bd).hours() / 24)
   println("Born $birth_date, Target $target_date")
   println("Day $days")
   for i in 0..3 {
       length := lengths[i]
       cycle := cycles[i]
       position := days % length
       quadrant := position * 4 / length
       mut percent := math.sin(2 * math.pi * f64(position) / f64(length))
       percent = math.floor(percent*1000) / 10
       mut descript := ""
       if percent > 95 {
           descript = " peak"
       } else if percent < -95 {
           descript = " valley"
       } else if math.abs(percent) < 5 {
           descript = " critical transition"
       } else {
           days_to_add := (quadrant+1)*length/4 - position
           transition := td.add(time.hour * 24 * time.Duration(days_to_add))
           trend := quadrants[quadrant][0]
           next := quadrants[quadrant][1]
           trans_str := transition.custom_format('YYYY-MM-DD')
           descript = "${percent:5.1f}% ($trend, next $next $trans_str)"
       }
       println("$cycle ${position:2} : $descript")
   }
   println()

}

fn main() {

   date_pairs := [
       ["1943-03-09", "1972-07-11"],
       ["1809-01-12", "1863-11-19"],
       ["1809-02-12", "1863-11-19"], // correct DOB for Abraham Lincoln

]

   for date_pair in date_pairs {
       biorhythms(date_pair[0], date_pair[1])?
   }

}</lang>

Output:
Born 1943-03-09, Target 1972-07-11
Day 10717
Physical day  22 : -27.0% (down but rising, next transition 1972-07-12)
Emotional day 21 :  valley
Mental day    25 :  valley

Born 1809-01-12, Target 1863-11-19
Day 20034
Physical day   1 :  26.9% (up and rising, next peak 1863-11-23)
Emotional day 14 :  critical transition
Mental day     3 :  54.0% (up and rising, next peak 1863-11-24)

Born 1809-02-12, Target 1863-11-19
Day 20003
Physical day  16 : -94.3% (down and falling, next valley 1863-11-20)
Emotional day 11 :  62.3% (up but falling, next transition 1863-11-22)
Mental day     5 :  81.4% (up and rising, next peak 1863-11-22)

Wren

Translation of: Raku
Library: Wren-date
Library: Wren-fmt

<lang ecmascript>import "/date" for Date import "/fmt" for Fmt

var cycles = ["Physical day ", "Emotional day", "Mental day "] var lengths = [23, 28, 33] var quadrants = [

   ["up and rising",    "peak"],
   ["up but falling",   "transition"],
   ["down and falling", "valley"],
   ["down but rising",  "transition"]

]

var biorhythms = Fn.new { |birthDate, targetDate|

   var bd = Date.parse(birthDate)
   var td = Date.parse(targetDate)
   var days = (td - bd).days
   Date.default = Date.isoDate
   System.print("Born %(birthDate), Target %(targetDate)")
   System.print("Day %(days)")
   for (i in 0..2) {
       var length   = lengths[i]
       var cycle    = cycles[i]
       var position = days % length
       var quadrant = (position / length * 4).floor
       var percent  = ((2 * Num.pi * position / length).sin * 1000).floor / 10
       var descript = (percent > 95)    ? " peak" :
                      (percent < -95)   ? " valley" :
                      (percent.abs < 5) ? " critical transition" : "other"
       if (descript == "other") {
           var transition  = td.addDays(((quadrant + 1) / 4 * length).floor - position)
           var tn = quadrants[quadrant]
           var trend = tn[0]
           var next = tn[1]
           descript = Fmt.swrite("$5.1f\% ($s, next $s $s)", percent, trend, next, transition)
       }
       Fmt.print("$s $2d : $s", cycle, position, descript)
   }
   System.print()

}

var datePairs = [

   ["1943-03-09", "1972-07-11"],
   ["1809-01-12", "1863-11-19"],
   ["1809-02-12", "1863-11-19"]  // correct DOB for Abraham Lincoln

] for (datePair in datePairs) biorhythms.call(datePair[0], datePair[1])</lang>

Output:
Born 1943-03-09, Target 1972-07-11
Day 10717
Physical day  22 : -27.0% (down but rising, next transition 1972-07-12)
Emotional day 21 :  valley
Mental day    25 :  valley

Born 1809-01-12, Target 1863-11-19
Day 20034
Physical day   1 :  26.9% (up and rising, next peak 1863-11-23)
Emotional day 14 :  critical transition
Mental day     3 :  54.0% (up and rising, next peak 1863-11-24)

Born 1809-02-12, Target 1863-11-19
Day 20003
Physical day  16 : -94.3% (down and falling, next valley 1863-11-20)
Emotional day 11 :  62.3% (up but falling, next transition 1863-11-22)
Mental day     5 :  81.4% (up and rising, next peak 1863-11-22)