#include<stdio.h>

typedef struct{
	int num,den;
	}fraction;

fraction examples[] = {{1,2}, {3,1}, {23,8}, {13,11}, {22,7}, {-151,77}};
fraction sqrt2[] = {{14142,10000}, {141421,100000}, {1414214,1000000}, {14142136,10000000}};
fraction pi[] = {{31,10}, {314,100}, {3142,1000}, {31428,10000}, {314285,100000}, {3142857,1000000}, {31428571,10000000}, {314285714,100000000}};

int r2cf(int *numerator,int *denominator)
{
	int quotient=0,temp;

	if(denominator != 0)
	{
		quotient = *numerator / *denominator;

		temp = *numerator;

		*numerator = *denominator;

		*denominator = temp % *denominator;
	}

	return quotient;
}

int main()
{
	int i;

	printf("Running the examples :");

	for(i=0;i<sizeof(examples)/sizeof(fraction);i++)
	{
		printf("\nFor N = %d, D = %d :",examples[i].num,examples[i].den);

		while(examples[i].den != 0){
			printf(" %d ",r2cf(&examples[i].num,&examples[i].den));
			}
	}

	printf("\n\nRunning for %c2 :",251); /* 251 is the ASCII code for the square root symbol */

	for(i=0;i<sizeof(sqrt2)/sizeof(fraction);i++)
	{
		printf("\nFor N = %d, D = %d :",sqrt2[i].num,sqrt2[i].den);

		while(sqrt2[i].den != 0){
			printf(" %d ",r2cf(&sqrt2[i].num,&sqrt2[i].den));
			}
	}

	printf("\n\nRunning for %c :",227); /* 227 is the ASCII code for Pi's symbol */

	for(i=0;i<sizeof(pi)/sizeof(fraction);i++)
	{
		printf("\nFor N = %d, D = %d :",pi[i].num,pi[i].den);

		while(pi[i].den != 0){
			printf(" %d ",r2cf(&pi[i].num,&pi[i].den));
			}
	}

	return 0;
}


integer pn = 1, -- prime numb
        lp = 2, -- last prime
        lg = 0, -- last gap
        pd = 0  -- prev d
sequence cr = {0,0},    -- curr run [a,d]
         mr = {{0},{0}} -- max runs  ""
while true do
    pn += 1
    integer p = get_prime(pn), gap = p-lp,
            d = compare(gap,lg)
    if p>1e6 then exit end if
    if d then
        integer i = (3-d)/2
        cr[i] = iff(d=pd?cr[i]:lp!=2)+1
        if cr[i]>mr[i][1] then mr[i] = {cr[i],pn} end if
    end if
    {pd,lp,lg} = {d,p,gap}
end while

for run=1 to 2 do
    integer {l,e} = mr[run]
    sequence p = apply(tagset(e,e-l),get_prime),
             g = sq_sub(p[2..$],p[1..$-1])
    printf(1,"longest %s run length %d: %v gaps: %v\n",
       {{"ascending","descending"}[run],length(p),p,g})
end for


from sympy import sieve

primelist = list(sieve.primerange(2,1000000))

listlen = len(primelist)

# ascending

pindex = 1
old_diff = -1
curr_list=[primelist[0]]
longest_list=[]

while pindex < listlen:

    diff = primelist[pindex] - primelist[pindex-1]
    if diff > old_diff:
        curr_list.append(primelist[pindex])
        if len(curr_list) > len(longest_list):
            longest_list = curr_list
    else:
        curr_list = [primelist[pindex-1],primelist[pindex]]

    old_diff = diff
    pindex += 1

print(longest_list)

# descending

pindex = 1
old_diff = -1
curr_list=[primelist[0]]
longest_list=[]

while pindex < listlen:

    diff = primelist[pindex] - primelist[pindex-1]
    if diff < old_diff:
        curr_list.append(primelist[pindex])
        if len(curr_list) > len(longest_list):
            longest_list = curr_list
    else:
        curr_list = [primelist[pindex-1],primelist[pindex]]

    old_diff = diff
    pindex += 1

print(longest_list)


using System;
using System.Collections.Generic;

class Program
{
    static IEnumerable<int> r2cf(int n1, int n2)
    {
        while (Math.Abs(n2) > 0)
        {
            int t1 = n1 / n2;
            int t2 = n2;
            n2 = n1 - t1 * n2;
            n1 = t2;
            yield return t1;
        }
    }

    static void spit(IEnumerable<int> f)
    {
        foreach (int n in f) Console.Write(" {0}", n);
        Console.WriteLine();
    }

    static void Main(string[] args)
    {
        spit(r2cf(1, 2));
        spit(r2cf(3, 1));
        spit(r2cf(23, 8));
        spit(r2cf(13, 11));
        spit(r2cf(22, 7));
        spit(r2cf(-151, 77));
        for (int scale = 10; scale <= 10000000; scale *= 10)
        {
            spit(r2cf((int)(Math.Sqrt(2) * scale), scale));
        }
        spit(r2cf(31, 10));
        spit(r2cf(314, 100));
        spit(r2cf(3142, 1000));
        spit(r2cf(31428, 10000));
        spit(r2cf(314285, 100000));
        spit(r2cf(3142857, 1000000));
        spit(r2cf(31428571, 10000000));
        spit(r2cf(314285714, 100000000));
    }
}


use Math::Primesieve;
use Lingua::EN::Numbers;

my $sieve = Math::Primesieve.new;

my $limit = 1000000;

my @primes = $sieve.primes($limit);

sub runs (&op) {
    my $diff = 1;
    my $run = 1;

    my @diff = flat 1, (1..^@primes).map: {
        my $next = @primes[$_] - @primes[$_ - 1];
        if &op($next, $diff) { ++$run } else { $run = 1 }
        $diff = $next;
        $run;
    }

    my $max = max @diff;
    my @runs = @diff.grep: * == $max, :k;

    @runs.map( {
        my @run = (0..$max).reverse.map: -> $r { @primes[$_ - $r] }
        flat roundrobin(@run».&comma, @run.rotor(2 => -1).map({[R-] $_})».fmt('(%d)'));
    } ).join: "\n"
}

say "Longest run(s) of ascending prime gaps up to {comma $limit}:\n" ~ runs(&infix:«>»);

say "\nLongest run(s) of descending prime gaps up to {comma $limit}:\n" ~ runs(&infix:«<»);


/*REXX program finds the longest sequence of consecutive primes where the differences   */
/*──────────── between the primes are strictly ascending;  also for strictly descending.*/
parse arg hi cols .                              /*obtain optional argument from the CL.*/
if   hi=='' |   hi==","  then   hi= 1000000      /* "      "         "   "   "     "    */
if cols=='' | cols==","  then cols=      10      /* "      "         "   "   "     "    */
call genP                                        /*build array of semaphores for primes.*/
w= 10                                            /*width of a number in any column.     */
call fRun 1;  call show 1                        /*find runs with ascending prime diffs.*/
call fRun 0;  call show 0                        /*  "    "    " descending   "     "   */
exit 0                                           /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
commas: parse arg ?;  do jc=length(?)-3  to 1  by -3; ?=insert(',', ?, jc); end;  return ?
/*──────────────────────────────────────────────────────────────────────────────────────*/
fRun: parse arg ?;    mxrun=0;     seq.=         /*max run length;  lists of prime runs.*/
                                                 /*search for consecutive primes  <  HI.*/
        do j=2  for #-2;   cp= @.j;   jn= j+1    /*CP: current prime;  JN:  next j      */
        diff= @.jn - cp                          /*get difference between last 2 primes.*/
        cnt= 1;                       run=       /*initialize the   CNT   and   RUN.    */
               do k= jn+1  to #-2;    km= k-1    /*look for more primes in this run.    */
               if ?  then if @.[email protected].km<=diff  then leave  /*Diff. too small? Stop looking*/
                                             else nop
                     else if @.[email protected].km>=diff  then leave  /*  "    "  large?   "     "   */
               run= run  @.k;         cnt= cnt+1 /*append a prime to the run; bump count*/
               diff= @.k - @.km                  /*calculate difference for next prime. */
               end   /*k*/
        if cnt<=mxrun  then iterate              /*This run too short? Then keep looking*/
        mxrun= max(mxrun, cnt)                   /*define a new maximum run (seq) length*/
        seq.mxrun= cp  @.jn  run                 /*full populate the sequence (RUN).    */
        end   /*j*/;                   return
/*──────────────────────────────────────────────────────────────────────────────────────*/
genP: @.1=2; @.2=3; @.3=5; @.4=7; @.5=11; @.6=13; @.7=17; @.8=19    /*define low primes.*/
                           #=8;   sq.#= @.# ** 2 /*number of primes so far; prime sqiare*/
                                                 /* [↓]  generate more  primes  ≤  high.*/
        do [email protected].#+2  by 2  to hi;  parse var j '' -1 _    /*find odd primes from here on.*/
        if     _==5  then iterate;  if j// 3==0  then iterate   /*J ÷  5?    J ÷ by  3? */
        if j// 7==0  then iterate;  if j//11==0  then iterate   /*" "  7?    " "  " 11? */
        if j//13==0  then iterate;  if j//17==0  then iterate   /*" " 13?    " " "  17? */
               do k=8  while sq.k<=j             /* [↓]  divide by the known odd primes.*/
               if j // @.k == 0  then iterate j  /*Is  J ÷ X?  Then not prime.     ___  */
               end   /*k*/                       /* [↑]  only process numbers  ≤  √ J   */
        #= #+1;             @.#= j;    sq.#= j*j /*bump # of Ps; assign next P; P square*/
        end          /*j*/;            return
/*──────────────────────────────────────────────────────────────────────────────────────*/
show: parse arg ?;  if ?  then AorD= 'ascending'     /*choose which literal for display.*/
                          else AorD= 'descending'    /*   "     "      "     "     "    */
      title= ' longest run of consecutive primes whose differences between primes are'  ,
                              'strictly'         AorD         "and  < "         commas(hi)
      say;    say;    say
      if cols>0  then say ' index │'center(title,   1 + cols*(w+1)     )
      if cols>0  then say '───────┼'center(""   ,   1 + cols*(w+1), '─')
      found= 0;                idx= 1            /*initialize # of consecutive primes.  */
      $=                                         /*a list of consecutive primes (so far)*/
         do o=1  for words(seq.mxrun)            /*show all consecutive primes in seq.  */
         c= commas( word(seq.mxrun, o) )         /*obtain the next prime in the sequence*/
         found= found + 1                        /*bump the number of consecutive primes*/
         if cols<=0            then iterate      /*build the list  (to be shown later)? */
         $= $  right(c, max(w, length(c) ) )     /*add a nice prime ──► list, allow big#*/
         if found//cols\==0    then iterate      /*have we populated a line of output?  */
         say center(idx, 7)'│'  substr($, 2)     /*display what we have so far  (cols). */
         idx= idx + cols;              $=        /*bump the  index  count for the output*/
         end   /*o*/
      if $\==''  then say center(idx, 7)"│"  substr($, 2)   /*maybe show residual output*/
      if cols>0  then say '───────┴'center(""   ,   1 + cols*(w+1), '─')
      say;            say commas(Cprimes)  ' was the'title;        return


F duration(=sec)
   [Int] t
   L(dm) [60, 60, 24, 7]
      Int m
      (sec, m) = (sec I/ dm, sec % dm)
      t.insert(0, m)
   t.insert(0, sec)
   R zip(t, [‘wk’, ‘d’, ‘hr’, ‘min’, ‘sec’]).filter(num_unit -> num_unit[0] > 0).map(num_unit -> num_unit[0]‘ ’num_unit[1]).join(‘, ’)

print(duration(7259))
print(duration(86400))
print(duration(6000000))


#include <iostream>
/* Interface for all Continued Fractions
   Nigel Galloway, February 9th., 2013.
*/
class ContinuedFraction {
	public:
	virtual const int nextTerm(){};
	virtual const bool moreTerms(){};
};
/* Create a continued fraction from a rational number
   Nigel Galloway, February 9th., 2013.
*/
class r2cf : public ContinuedFraction {
	private: int n1, n2;
	public:
	r2cf(const int numerator, const int denominator): n1(numerator), n2(denominator){}
	const int nextTerm() {
		const int thisTerm = n1/n2;
		const int t2 = n2; n2 = n1 - thisTerm * n2; n1 = t2;
		return thisTerm;
	}
	const bool moreTerms() {return fabs(n2) > 0;}
};
/* Generate a continued fraction for sqrt of 2
   Nigel Galloway, February 9th., 2013.
*/
class SQRT2 : public ContinuedFraction {
	private: bool first=true;
	public:
	const int nextTerm() {if (first) {first = false; return 1;} else return 2;}
	const bool moreTerms() {return true;}
};


require "prime"
limit = 1_000_000

puts "First found longest run of ascending prime gaps up to #{limit}:"
p  Prime.each(limit).each_cons(2).chunk_while{|(i1,i2), (j1,j2)| j1-i1 < j2-i2 }.max_by(&:size).flatten.uniq
puts  "\nFirst found longest run of descending prime gaps up to #{limit}:"
p  Prime.each(limit).each_cons(2).chunk_while{|(i1,i2), (j1,j2)| j1-i1 > j2-i2 }.max_by(&:size).flatten.uniq


// [dependencies]
// primal = "0.3"

fn print_diffs(vec: &[usize]) {
    for i in 0..vec.len() {
        if i > 0 {
            print!(" ({}) ", vec[i] - vec[i - 1]);
        }
        print!("{}", vec[i]);
    }
    println!();
}

fn main() {
    let limit = 1000000;
    let mut asc = Vec::new();
    let mut desc = Vec::new();
    let mut max_asc = Vec::new();
    let mut max_desc = Vec::new();
    let mut max_asc_len = 0;
    let mut max_desc_len = 0;
    for p in primal::Sieve::new(limit)
        .primes_from(2)
        .take_while(|x| *x < limit)
    {
        let alen = asc.len();
        if alen > 1 && p - asc[alen - 1] <= asc[alen - 1] - asc[alen - 2] {
            asc = asc.split_off(alen - 1);
        }
        asc.push(p);
        if asc.len() >= max_asc_len {
            if asc.len() > max_asc_len {
                max_asc_len = asc.len();
                max_asc.clear();
            }
            max_asc.push(asc.clone());
        }
        let dlen = desc.len();
        if dlen > 1 && p - desc[dlen - 1] >= desc[dlen - 1] - desc[dlen - 2] {
            desc = desc.split_off(dlen - 1);
        }
        desc.push(p);
        if desc.len() >= max_desc_len {
            if desc.len() > max_desc_len {
                max_desc_len = desc.len();
                max_desc.clear();
            }
            max_desc.push(desc.clone());
        }
    }
    println!("Longest run(s) of ascending prime gaps up to {}:", limit);
    for v in max_asc {
        print_diffs(&v);
    }
    println!("\nLongest run(s) of descending prime gaps up to {}:", limit);
    for v in max_desc {
        print_diffs(&v);
    }
}


func runs(f, arr) {

    var run = 0
    var diff = 0
    var diffs = []

    arr.each_cons(2, {|p1,p2|
        var curr_diff = (p2 - p1)
        f(curr_diff, diff) ? ++run : (run = 1)
        diff = curr_diff
        diffs << run
    })

    var max  = diffs.max
    var runs = []

    diffs.indices_by { _ == max }.each {|i|
        runs << arr.slice(i - max + 1, i + 1)
    }

    return runs
}

var limit = 1e6
var primes = limit.primes

say "Longest run(s) of ascending prime gaps up to #{limit.commify}:"
say runs({|a,b| a > b }, primes).join("\n")

say "\nLongest run(s) of descending prime gaps up to #{limit.commify}:"
say runs({|a,b| a < b }, primes).join("\n")


import "/math" for Int

var LIMIT = 999999
var primes = Int.primeSieve(LIMIT)

var longestSeq = Fn.new { |dir|
    var pd = 0
    var longSeqs = [[2]]
    var currSeq = [2]
    for (i in 1...primes.count) {
        var d = primes[i] - primes[i-1]
        if ((dir == "ascending" && d <= pd) || (dir == "descending" && d >= pd)) {
            if (currSeq.count > longSeqs[0].count) {
                longSeqs = [currSeq]
            } else if (currSeq.count == longSeqs[0].count) longSeqs.add(currSeq)
            currSeq = [primes[i-1], primes[i]]
        } else {
            currSeq.add(primes[i])
        }
        pd = d
    }
    if (currSeq.count > longSeqs[0].count) {
        longSeqs = [currSeq]
    } else if (currSeq.count == longSeqs[0].count) longSeqs.add(currSeq)
    System.print("Longest run(s) of primes with %(dir) differences is %(longSeqs[0].count):")
    for (ls in longSeqs) {
        var diffs = []
        for (i in 1...ls.count) diffs.add(ls[i] - ls[i-1])
        for (i in 0...ls.count-1) System.write("%(ls[i]) (%(diffs[i])) ")
        System.print(ls[-1])
    }
    System.print()
}

System.print("For primes < 1 million:\n")
for (dir in ["ascending", "descending"]) longestSeq.call(dir)


INCLUDE "H6:REALMATH.ACT"
DEFINE PTR="CARD"

TYPE Time=[BYTE s,m,h,d,w]
CARD ARRAY units(5)

PROC Convert(REAL POINTER seconds Time POINTER t)
  BYTE ARRAY b,duration=[60 60 24 7]
  BYTE i
  REAL r,n

  b=t
  FOR i=0 TO 3
  DO
    IntToReal(duration(i),n)
    RealMod(seconds,n,r)
    b(i)=RealToInt(r)
    RealDivInt(seconds,n,r)
    RealAssign(r,seconds)
  OD
  b(4)=RealToInt(seconds)
RETURN

PROC PrintTime(Time POINTER t)
  INT i
  BYTE first,n
  BYTE ARRAY b

  b=t i=4 first=1
  WHILE i>=0
  DO
    n=b(i)
    IF n>0 THEN
      IF first=0 THEN
        Print(", ")
      ELSE
        first=0
      FI
      PrintF("%B %S",n,units(i))
    FI
    i==-1
  OD
RETURN

PROC Test(CHAR ARRAY s)
  REAL seconds
  Time t

  ValR(s,seconds)
  PrintR(seconds) Print(" -> ")
  Convert(seconds,t)
  PrintTime(t) PutE()
RETURN

PROC Main()
  Put(125) PutE() ;clear the screen
  MathInit()
  units(0)="sec" units(1)="min"
  units(2)="hr"  units(3)="d"
  units(4)="wk"
  Test("7259")
  Test("86400")
  Test("6000000")
RETURN


with Ada.Text_IO;

procedure Convert is

   type Time is range 0 .. 10_000*356*20*60*60; -- at most 10_000 years
   subtype Valid_Duration is Time range 1  .. 10_000*356*20*60*60;
   type Units is (WK, D, HR, MIN, SEC);

   package IO renames Ada.Text_IO;

   Divide_By: constant array(Units) of Time := (1_000*53, 7, 24, 60, 60);
   Split: array(Units) of Time;
   No_Comma: Units;
   X: Time;

   Test_Cases: array(Positive range <>) of Valid_Duration :=
     (6, 60, 3659, 7_259, 86_400, 6_000_000, 6_001_200, 6_001_230, 600_000_000);

begin
  for Test_Case of Test_Cases loop
     IO.Put(Time'Image(Test_Case) & " SECONDS =");
     X := Test_Case;

     -- split X up into weeks, days, ..., seconds
     No_Comma := Units'First;
     for Unit in reverse Units loop -- Unit = SEC, ..., WK (in that order)
	Split(Unit) := X mod Divide_By(Unit);
	X := X / Divide_By(Unit);
	if Unit > No_Comma and Split(Unit)>0 then
	   No_Comma := Unit;
	end if;
     end loop;

     -- ouput weeks, days, ..., seconds
     for Unit in Units loop -- Unit =  WK, .., SEC (in that order)
	if Split(Unit) > 0 then
	   IO.Put(Time'Image(Split(Unit)) & " " & Units'Image(Unit) &
		    (if No_Comma > Unit then "," else ""));
	end if;
     end loop;

  IO.New_Line;
  end loop;
end Convert;


func IsPrime(N);        \Return 'true' if N > 2 is a prime number
int  N, I;
[if (N&1) = 0 \even number\ then return false;
for I:= 3 to sqrt(N) do
    [if rem(N/I) = 0 then return false;
    I:= I+1;
    ];
return true;
];

proc ShowSeq(Dir, Str); \Show longest sequence of distances between primes
int  Dir, Str;
int  Count, MaxCount, N, P, P0, D, D0, I, AP(1000), MaxAP(1000);
[Count:= 0;  MaxCount:= 0;
P0:= 2;  D0:= 0;                \preceding prime and distance
AP(Count):= P0;  Count:= Count+1;
for N:= 3 to 1_000_000-1 do
    if IsPrime(N) then
        [P:= N;                 \got a prime number
        D:= P - P0;             \distance from preceding prime
        if D*Dir > D0*Dir then
            [AP(Count):= P;  Count:= Count+1;
            if Count > MaxCount then        \save best sequence
                [MaxCount:= Count;
                for I:= 0 to MaxCount-1 do
                    MaxAP(I):= AP(I);
                ];
            ]
        else
            [Count:= 0;     \restart sequence
            AP(Count):= P0;  Count:= Count+1; \possible beginning
            AP(Count):= P;   Count:= Count+1;
            ];
        P0:= P;  D0:= D;
        ];
Text(0, "Longest sequence of ");  Text(0, Str);
Text(0, " distances between primes: ");  IntOut(0, MaxCount);  CrLf(0);
for I:= 0 to MaxCount-2 do
    [IntOut(0, MaxAP(I));
    Text(0, " (");
    IntOut(0, MaxAP(I+1) - MaxAP(I));
    Text(0, ") ");
    ];
IntOut(0, MaxAP(I));  CrLf(0);
];

[ShowSeq(+1,  "ascending");     \main
 ShowSeq(-1, "descending");
]


# MODE to hold the compound duration #
MODE DURATION = STRUCT( INT weeks, days, hours, minutes, seconds );

# returns seconds converted to a DURATION #
OP TODURATION = ( LONG INT seconds )DURATION:
   BEGIN
       LONG INT time     := seconds;
       DURATION result   := DURATION( 0, 0, 0, 0, 0 );
       seconds OF result := SHORTEN ( time MOD  60 );
       time OVERAB 60;
       minutes OF result := SHORTEN ( time MOD  60 );
       time OVERAB 60;
       hours   OF result := SHORTEN ( time MOD  24 );
       time OVERAB 24;
       days    OF result := SHORTEN ( time MOD   7 );
       time OVERAB 7;
       weeks   OF result := SHORTEN   time;
       result
   END # DURATION # ;# returns seconds converted to a DURATION #
OP TODURATION = ( INT seconds )DURATION: TODURATION LENG seconds;

# returns a readable form of the DURATION #
OP TOSTRING = ( DURATION t )STRING:
   BEGIN
       STRING result    := "";
       STRING separator := "";
       IF weeks   OF t /= 0 THEN result +:= separator + whole( weeks   OF t, 0 ) + " wk";  separator := ", " FI;
       IF days    OF t /= 0 THEN result +:= separator + whole( days    OF t, 0 ) + " d";   separator := ", " FI;
       IF hours   OF t /= 0 THEN result +:= separator + whole( hours   OF t, 0 ) + " hr";  separator := ", " FI;
       IF minutes OF t /= 0 THEN result +:= separator + whole( minutes OF t, 0 ) + " min"; separator := ", " FI;
       IF seconds OF t /= 0 THEN result +:= separator + whole( seconds OF t, 0 ) + " sec"; separator := ", " FI;
       IF result = ""
       THEN
           # duration is 0 #
           result := "0 sec"
       FI;
       result
   END # TOSTRING # ;

# test cases #
print( ( TOSTRING TODURATION    7259, newline ) );
print( ( TOSTRING TODURATION   86400, newline ) );
print( ( TOSTRING TODURATION 6000000, newline ) )


import std.concurrency;
import std.stdio;

struct Pair {
    int first, second;
}

auto r2cf(Pair frac) {
    return new Generator!int({
        auto num = frac.first;
        auto den = frac.second;
        while (den != 0) {
            auto div = num / den;
            auto rem = num % den;
            num = den;
            den = rem;
            div.yield();
        }
    });
}

void iterate(Generator!int seq) {
    foreach(i; seq) {
        write(i, " ");
    }
    writeln();
}

void main() {
    auto fracs = [
        Pair(   1,  2),
        Pair(   3,  1),
        Pair(  23,  8),
        Pair(  13, 11),
        Pair(  22,  7),
        Pair(-151, 77),
    ];
    foreach(frac; fracs) {
        writef("%4d / %-2d = ", frac.first, frac.second);
        frac.r2cf.iterate;
    }
    writeln;

    auto root2 = [
        Pair(    14_142,     10_000),
        Pair(   141_421,    100_000),
        Pair( 1_414_214,  1_000_000),
        Pair(14_142_136, 10_000_000),
    ];
    writeln("Sqrt(2) ->");
    foreach(frac; root2) {
        writef("%8d / %-8d = ", frac.first, frac.second);
        frac.r2cf.iterate;
    }
    writeln;

    auto pi = [
        Pair(         31,          10),
        Pair(        314,         100),
        Pair(      3_142,       1_000),
        Pair(     31_428,      10_000),
        Pair(    314_285,     100_000),
        Pair(  3_142_857,   1_000_000),
        Pair( 31_428_571,  10_000_000),
        Pair(314_285_714, 100_000_000),
    ];
    writeln("Pi ->");
    foreach(frac; pi) {
        writef("%9d / %-9d = ", frac.first, frac.second);
        frac.r2cf.iterate;
    }
}


begin
    % record structure to hold a compound duration %
    record Duration ( integer weeks, days, hours, minutes, seconds );

    % returns seconds converted to a Duration %
    reference(Duration) procedure toDuration( integer value secs ) ;
    begin
        integer  time;
        reference(Duration) d;
        time := secs;
        d    := Duration( 0, 0, 0, 0, 0 );
        seconds(d) := time rem 60;
        time       := time div 60;
        minutes(d) := time rem 60;
        time       := time div 60;
        hours(d)   := time rem 24;
        time       := time div 24;
        days(d)    := time rem  7;
        time       := time div  7;
        weeks(d)   := time;
        d
    end toDuration ;

    % returns a readable form of the DURATION %
    string(80) procedure durationToString ( reference(Duration) value d ) ;
    begin
        % appends an element of the compound duration to text %
        procedure add ( integer   value componentValue
                      ; string(6) value componentName
                      ; integer   value nameLength
                      ) ;
        begin
            string(9) vStr;
            integer   v, vPos;
            if needSeparator then begin
                % must separate this component from the previous %
                text( textPos // 2 ) := ", ";
                textPos:= textPos + 2
             end if_needSepartator ;
             % add the value %
             % construct a string representaton of the value with the digits reversed %
             % as this routine isn't called if componentValue is 0 or -ve, we don't need to handle %
             % the componentVaue <= 0 case %
             v    := componentValue;
             vStr := "";
             vPos := 0;
             while v > 0 do begin
                 vStr( vPos // 1 ) := code( decode( "0" ) + ( v rem 10 ) );
                 vPos              := vPos + 1;
                 v                 := v div 10
             end while_v_gt_0 ;
             % add the digits in the correct order %
             while vPos > 0 do begin
                 vPos                 := vPos - 1;
                 text( textPos // 1 ) := vStr( vPos // 1 );
                 textPos              := textPos + 1
             end while_vPos_gt_0 ;
             % add the component name %
             text( textPos // 6 ) := componentName;
             textPos := textPos + nameLength;
             % if there is another component, we'll need a separator %
             needSeparator := true
        end add ;

        string(80) text;
        logical    needSeparator;
        integer    textPos;
        textPos       := 0;
        text          := "";
        needSeparator := false;
        if   weeks(d) not = 0 then add(   weeks(d), " wk",  3 );
        if    days(d) not = 0 then add(    days(d), " d",   2 );
        if   hours(d) not = 0 then add(   hours(d), " hr",  3 );
        if minutes(d) not = 0 then add( minutes(d), " min", 4 );
        if seconds(d) not = 0 then add( seconds(d), " sec", 4 );
        if text = "" then begin
            % duration is 0 %
            text := "0 sec"
        end if_text_is_blank ;
        text
   end % durationToString % ;

    % test cases %
    write( durationToString( toDuration(    7259 ) ) );
    write( durationToString( toDuration(   86400 ) ) );
    write( durationToString( toDuration( 6000000 ) ) )
end.


duration←{
    names←'wk' 'd' 'hr' 'min' 'sec'
    parts←0 7 24 60 60⊤⍵
    fmt←⍕¨(parts≠0)/parts,¨names
    ¯2↓∊fmt,¨⊂', '
}


-------------------- COMPOUND DURATIONS ------------------

-- weekParts Int -> [Int]
on weekParts(intSeconds)
    unitParts(intSeconds, [missing value, 7, 24, 60, 60])
end weekParts

-- localCompoundDuration :: Int -> String
on localCompoundDuration(localNames, intSeconds)

    -- [String] -> (Int, String) -> [String]
    script formatted
        on |λ|(lstPair, a)
            set q to item 1 of lstPair
            if q > 0 then
                {(q as string) & space & item 2 of lstPair} & a
            else
                a
            end if
        end |λ|
    end script

    intercalate(", ", ¬
        foldr(formatted, [], ¬
            zip(weekParts(intSeconds), localNames)))
end localCompoundDuration

------------------ INTEGER DECOMPOSITION -----------------

-- unitParts :: Int -> [maybe Int] -> [Int]
on unitParts(intTotal, unitList)
    -- partList :: Record -> Int -> Record
    script partList
        on |λ|(x, a)
            set intRest to remaining of a

            if x is not missing value then
                set intMod to intRest mod x
                set d to x
            else
                set intMod to intRest
                set d to 1
            end if

            {remaining:(intRest - intMod) div d, parts:{intMod} & parts of a}
        end |λ|
    end script

    parts of foldr(partList, ¬
        {remaining:intTotal, parts:[]}, unitList)
end unitParts

--------------------------- TEST -------------------------
on run
    script angloNames
        on |λ|(n)
            (n as string) & "     ->    " & ¬
                localCompoundDuration(["wk", "d", "hr", "min", "sec"], n)
        end |λ|
    end script

    unlines(map(angloNames, [7259, 86400, 6000000]))
end run

-------------------- GENERIC FUNCTIONS -------------------

-- foldr :: (a -> b -> b) -> b -> [a] -> b
on foldr(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with i from lng to 1 by -1
            set v to |λ|(item i of xs, v, i, xs)
        end repeat
        return v
    end tell
end foldr

-- intercalate :: String -> [String] -> String
on intercalate(delim, xs)
    set {dlm, my text item delimiters} to ¬
        {my text item delimiters, delim}
    set s to xs as text
    set my text item delimiters to dlm
    s
end intercalate

-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    -- The list obtained by applying f
    -- to each element of xs.
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, i, xs)
        end repeat
        return lst
    end tell
end map

-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min

-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    -- 2nd class handler function lifted into 1st class script wrapper.
    if script is class of f then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

-- unlines :: [String] -> String
on unlines(xs)
    -- A single string formed by the intercalation
    -- of a list of strings with the newline character.
    set {dlm, my text item delimiters} to ¬
        {my text item delimiters, linefeed}
    set s to xs as text
    set my text item delimiters to dlm
    s
end unlines

-- zip :: [a] -> [b] -> [(a, b)]
on zip(xs, ys)
    -- A list of step-wise pairs drawn from xs and ys
    -- up to the length of the shorter of those lists.
    set lng to min(length of xs, length of ys)
    set zs to {}
    repeat with i from 1 to lng
        set end of zs to {item i of xs, item i of ys}
    end repeat
    return zs
end zip


 [Continued fractions from rationals.
  EDSAC program, Initial Orders 2.]

 [Memory usage:
   56..109  Print subroutine, modified from the EDSAC library
  110..146  Division subroutine for long positive integers
  148..196  Continued fraction subroutine, as specified by Rosetta Code
  200..260  Main routine
  262..     List of rationals, variable number of items]

 [Define where to store the list of rationals.]
          T  45 K [store address in location 45;
                   values are then accessed by code letter H (*)]
          P 262 F [<------ address here]
 [(*) Arbitrary choice. We could equally well use 46 and N, 47 and M, etc.]

 [Library subroutine R2. Reads positive integers during input of orders,
    and is then overwritten (so doesn't take up any memory).
  Negative numbers can be input by adding 2^35.
  Each integer is followed by 'F', except the last is followed by '#TZ'.]
  [email protected]@[email protected]@E13Z
          T    #H  [Tell R2 the storage location defined above]

 [Rationals to be read by R2. First item is count, then num/den pairs.]
  8F 1F2F 3F1F 33F8F 13F11F 22F7F 34359738217F77F
  141421356F100000000F 314285714F100000000#TZ

 [----------------------------------------------------------------------
  Modification of library subroutine P7.
  Prints signed integer up to 10 digits, left-justified.
  54 storage locations; working position 4D.
  Must be loaded at an even address.
  Input: Number is at 0D.]
          T  56 K
  [email protected]@[email protected]@[email protected]@[email protected]#@[email protected]
  [email protected]@[email protected]@[email protected]@[email protected]
  [email protected]@[email protected]@[email protected]@[email protected]

 [----------------------------------------------------------------------
  Division subroutine for long positive integers.
  35-bit dividend and divisor (max 2^34 - 1)
    returning quotient and remainder.
  Input:  dividend at 4D, divisor at 6D
  Output: remainder at 4D, quotient at 6D.
  Working locations 0D, 8D.]
          T 110 K
          G     K
          A   3 F [plant link]
          T  35 @
          A   6 D [load divisor]
          U   8 D [save at 8D (6D is required for quotient)]
    [4]   T     D [initialize shifted divisor]
          A   4 D [load dividend]
          R     D [shift 1 right]
          S     D [shifted divisor > dividend/2 yet?]
          G  13 @ [yes, start subtraction]
          T  36 @ [no, clear acc]
          A     D [shift divisor 1 more]
          L     D
          E   4 @ [loop back (always, since acc >= 0)]
   [13]   T  36 @ [clear acc]
          T   6 D [initialize quotient to 0]
   [15]   A   4 D [load remainder (initially = dividend)]
          S     D [trial subtraction]
          G  23 @ [skip if can't subtract]
          T   4 D [update remainder]
          A   6 D [load quotient]
          Y     F [add 1 by rounding twice (*)]
          Y     F
          T   6 D
   [23]   T  36 @ [clear acc]
          A   8 D [load original divisor]
          S     D [is shifted divisor back to original?]
          E  35 @ [yes, exit (with accumulator = 0,
                   in accordance with EDSAC convention)]
          T  36 @ [no, clear acc]
          A     D [shift divisor 1 right]
          R     D
          T     D
          A   6 D [shift quotient 1 left]
          L     D
          T   6 D
          E  15 @ [loop back (always, since acc = 0)]
   [35]   E     F [return; order set up at runtime]
   [36]   P     F [junk word, to clear accumulator]

 [(*) This saves the bother of defining a double-word constant 1
      and making sure that it's at an even address.]

 [----------------------------------------------------------------------
  Subroutine for lazy evaluation of continued fraction.
  Must be loaded at an even address.
  Locations relative to start of subroutine:
  0:    Entry point
  1:    Flag, < 0 if c.f. is finished, >= 0 if there's another term
  2, 3: Next term of c.f., provided the flag (location 1) is >= 0
  4, 5: Caller places numerator here before first call
  6, 7: Caller places denominator here before first call; must be > 0

  After setting up the numerator and denominator of the rational number,
    the caller should repeatedly call location 0, reading the result
    from location 1 and double location 2.
  Locations 4..7 are maintained by the subroutine and should not be changed
    by the caller until a new continued fraction is required.]

          T  46 K [place address of subroutine in location 46]
          P 148 F
          E  25 K [load the code below to that address (WWG page 18)]
          T     N
          G     K
    [0]   G   8 @ [entry point]
    [1]   P     F [flag returned here]
    [2]   P F P F [term returned here, if flag >= 0;
                   also used as temporary store]
    [4]   P F P F [caller puts numerator here]
    [6]   P F P F [caller puts denominator here]
    [8]   A   3 F [plant link]
          T  28 @
          S   6#@ [load negative of denominator]
          E  44 @ [if denom <= 0, no more terms]
          T     F [clear acc]
          A   4#@ [load numerator]
          T   2#@ [save before overwriting]
          A   6#@ [load denominator]
          U   4#@ [make it numerator for next call]
          T   6 D [also to 6D for division]
          A   2#@ [load numerator]
          G  29 @ [special action if negative]
          T   4 D [to 4D for division]
   [21]   A  21 @ [for return from next]
          G 110 F [call the above division subroutine]
          A   4 D [load remainder]
          T   6#@ [make it denominator for next call]
          A   6 D [load quotient]
   [26]   T   2#@ [return it as next term]
   [27]   T   1 @ [flag >= 0 means term is valid]
   [28]   E     F [exit with acc = 0]

         [Here if rational = -n/d where n, d > 0. Principle is:
          if  n + d - 1 = qd + r  then  -n = -qd + (d - 1 - r)]
   [29]   T   4 D [save numerator in 4D]
          S   6 D [acc := -den]
          Y     F [add 1 by rounding twice]
          Y     F
          T   2#@ [save (1 - den) for later]
          S   4 D [load abs(num)]
          S   2#@ [add (den - 1)]
          T   4 D [to 4D for division]
   [37]   A  37 @ [for return from next]
          G 110 F [call the above division subroutine]
          S   2#@ [load (den - 1)]
          S   4 D [subtract remainder]
          T   6#@ [result is new denominator]
          S   6 D [load negated quotient]
          G  26 @ [join common code]

         [Here if there are no more terms of the c.f.]
   [44]   T     F [clear acc]
          A   8 @ [this is negative since 'A' = -4]
          G  27 @ [exit with negative flag]

 [----------------------------------------------------------------------
  Main routine]
          T 200 K
          G     K
    [Variables]
    [0]   P     F [negative counter of continued fractions]
    [1]   P     F [character before term, first '=' then ',']
    [Constants]
    [2]   P     D [single-word 1]
    [3]   A   2#H [order to load first numerator]
    [4]   P   2 F [to inc addresses by 2]
    [5]   #     F [teleprinter figures shift]
    [6]   X     F [slash (in figures mode)]
    [7]   V     F [equals sign (in figures mode)]
    [8]   N     F [comma (in figures mode)]
    [9]   !     F [space]
   [10]   @     F [carriage return]
   [11]   &     F [line feed]
   [12]   K4096 F [teleprinter null]

          [Enter with acc = 0]
   [13]   O   5 @ [set teleprinter to figures]
          S     H [negative of number of c.f.s]
          T     @ [initialize counter]
          A   3 @ [initial load order]
   [17]   U  22 @ [plant order to load numerator]
          A   4 @ [inc address by 2]
          T  28 @ [plant order to load denominator]
          A   7 @ [set to print '=' before first term]
          T   1 @

          [Demonstrate the subroutine above.
          Since its address was placed in location 46,
          we can use code letter N to refer to it.]
   [22]   A    #H [load numerator (order set up at runtime)]
          U   4#N [pass to subroutine]
          T     D [also to 0D for printing]
   [25]   A  25 @ [for return from print subroutine]
          G  56 F [print numerator]
          O   6 @ [followed by slash]
   [28]   A    #H [load denominator (order set up at runtime)]
          U   6#N [pass to subroutine]
          T     D [also to 0D for printing]
   [31]   A  31 @ [for return from print subroutine]
          G  56 F [print denominator]
          O   9 @ [followed by space]
   [34]   A  34 @ [for return from subroutine]
          G     N [call subroutine for next term]
          A   1 N [load flag]
          G  48 @ [if < 0, c.f. is finished, jump out]
          O   1 @ [print equals or comma]
          O   9 @ [print space]
          T     F [clear acc]
          A   2#N [load term]
          T     D [to 0D for printing]
   [43]   A  43 @ [for return from print subroutine]
          G  56 F [print term; clears acc]
          A   8 @ [set to print ',' before subsequent terms]
          T   1 @
          E  34 @ [loop back for next term]

         [On to next continued fraction]
   [48]   O  10 @ [print new line]
          O  11 @
          T     F [clear acc]
          A     @ [load negative count of c.f.s]
          A   2 @ [add 1]
          E  59 @ [exit if count = 0]
          T     @ [store back]
          A  22 @ [order to load numerator]
          A   4 @ [inc address by 4 for next c.f.]
          A   4 @
          G  17 @ [loop back (always, since 'A' < 0)]

   [59]   O  12 @ [print null to flush teleprinter buffer]
          Z     F [stop]

          E  13 Z [define entry point]
          P     F [acc = 0 on entry]


let rec r2cf n d =
    if d = LanguagePrimitives.GenericZero then []
    else let q = n / d in q :: (r2cf d (n - q * d))

[<EntryPoint>]
let main argv =
    printfn "%A" (r2cf 1 2)
    printfn "%A" (r2cf 3 1)
    printfn "%A" (r2cf 23 8)
    printfn "%A" (r2cf 13 11)
    printfn "%A" (r2cf 22 7)
    printfn "%A" (r2cf -151 77)
    printfn "%A" (r2cf 141 100)
    printfn "%A" (r2cf 1414 1000)
    printfn "%A" (r2cf 14142 10000)
    printfn "%A" (r2cf 141421 100000)
    printfn "%A" (r2cf 1414214 1000000)
    printfn "%A" (r2cf 14142136 10000000)
    0


Units: [" wk", " d", " hr", " min", " sec"]
Quantities: @[7 * 24 * 60 * 60, 24 * 60 * 60, 60 * 60, 60, 1]

durationString: function [d][
    dur: d
    idx: 0
    result: new []
    while [not? zero? dur][
        q: dur / Quantities\[idx]
        if not? zero? q [
            dur: dur % Quantities\[idx]
            'result ++ ~{|q||Units\[idx]|}
        ]
        idx: idx +1
    ]
    return join.with:", " result
]

loop [7259 86400 6000000] 't [
    print [t "s => " durationString t]
]


'with some other constants
data 1,2, 21,7, 21,-7, 7,21, -7,21
data 23,8, 13,11, 22,7, 3035,5258, -151,-77
data -151,77, 77,151, 77,-151, -832040,1346269
data 63018038201,44560482149, 14142,10000
data 31,10, 314,100, 3142,1000, 31428,10000, 314285,100000
data 3142857,1000000, 31428571,10000000, 314285714,100000000
data 139755218526789,44485467702853
data 534625820200,196677847971, 0,0

const Inf = -(clngint(1) shl 63)

type frc
declare sub init (byval a as longint, byval b as longint)
declare function digit () as longint
   as longint n, d
end type

'member functions
sub frc.init (byval a as longint, byval b as longint)
if b < 0 then b = -b: a = -a
   n = a: d = b
end sub

function frc.digit as longint
dim as longint q, r
digit = Inf

if d then
   q = n \ d
   r = n - q * d
   'floordiv
   if r < 0 then q -= 1: r += d
   n = d: d = r

digit = q
end if
end function

'main
dim as longint a, b
dim r2cf as frc
do
   print
   read a, b
   if b = 0 then exit do

   r2cf.init(a, b)
   do
      'lazy evaluation
      a = r2cf.digit
      if a = Inf then exit do
      print a;
   loop
loop
sleep
system


# syntax: GAWK -f CONVERT_SECONDS_TO_COMPOUND_DURATION.AWK
BEGIN {
    n = split("7259 86400 6000000 0 1 60 3600 604799 604800 694861",arr," ")
    for (i=1; i<=n; i++) {
      printf("%9s %s\n",arr[i],howlong(arr[i]))
    }
    exit(0)
}
function howlong(seconds,  n_day,n_hour,n_min,n_sec,n_week,str,x) {
    if (seconds >= (x = 60*60*24*7)) {
      n_week = int(seconds / x)
      seconds = seconds % x
    }
    if (seconds >= (x = 60*60*24)) {
      n_day = int(seconds / x)
      seconds = seconds % x
    }
    if (seconds >= (x = 60*60)) {
      n_hour = int(seconds / x)
      seconds = seconds % x
    }
    if (seconds >= (x = 60)) {
      n_min = int(seconds / x)
      seconds = seconds % x
    }
    n_sec = int(seconds)
    str = (n_week > 0) ? (str n_week " wk, ") : str
    str = (n_day > 0) ? (str n_day " d, ") : str
    str = (n_hour > 0) ? (str n_hour " hr, ") : str
    str = (n_min > 0) ? (str n_min " min, ") : str
    str = (n_sec > 0) ? (str n_sec " sec") : str
    sub(/, $/,"",str)
    return(str)
}


package cf

import (
	"fmt"
	"strings"
)

// ContinuedFraction is a regular continued fraction.
type ContinuedFraction func() NextFn

// NextFn is a function/closure that can return
// a posibly infinite sequence of values.
type NextFn func() (term int64, ok bool)

// String implements fmt.Stringer.
// It formats a maximum of 20 values, ending the
// sequence with ", ..." if the sequence is longer.
func (cf ContinuedFraction) String() string {
	var buf strings.Builder
	buf.WriteByte('[')
	sep := "; "
	const maxTerms = 20
	next := cf()
	for n := 0; ; n++ {
		t, ok := next()
		if !ok {
			break
		}
		if n > 0 {
			buf.WriteString(sep)
			sep = ", "
		}
		if n >= maxTerms {
			buf.WriteString("...")
			break
		}
		fmt.Fprint(&buf, t)
	}
	buf.WriteByte(']')
	return buf.String()
}

// Sqrt2 is the continued fraction for √2, [1; 2, 2, 2, ...].
func Sqrt2() NextFn {
	first := true
	return func() (int64, bool) {
		if first {
			first = false
			return 1, true
		}
		return 2, true
	}
}

// Phi is the continued fraction for ϕ, [1; 1, 1, 1, ...].
func Phi() NextFn {
	return func() (int64, bool) { return 1, true }
}

// E is the continued fraction for e,
// [2; 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8, 1, 1, 10, 1, 1, 12, ...].
func E() NextFn {
	var i int
	return func() (int64, bool) {
		i++
		switch {
		case i == 1:
			return 2, true
		case i%3 == 0:
			return int64(i/3) * 2, true
		default:
			return 1, true
		}
	}
}


@echo off
::The Main Thing...
for %%d in (7259 86400 6000000) do call :duration %%d
exit/b 0
::/The Main Thing.

::The Function...
:duration
	set output=
	set /a "wk=%1/604800,rem=%1%%604800"
	if %wk% neq 0 set "output= %wk% wk,"

	set /a "d=%rem%/86400,rem=%rem%%%86400"
	if %d% neq 0 set "output=%output% %d% d,"

	set /a "hr=%rem%/3600,rem=%rem%%%3600"
	if %hr% neq 0 set "output=%output% %hr% hr,"

	set /a "min=%rem%/60,rem=%rem%%%60"
	if %min% neq 0 set "output=%output% %min% min,"

	if %rem% neq 0 set "output=%output% %rem% sec,"

	if %1 gtr 0 echo %1 sec = %output:~1,-1%
	goto :EOF
::/The Function.


#>%f# #>%f#  #f%<##>%f#
pq":X~7~ :[email protected][email protected]:X:[email protected][email protected]_
    ##    ##        ####          #`K0[`}`D2[`}BF3<         <
>{` wk, `>g?"p{` d, `>g?"p{` hr, `>g?"p{` min, `>g"b{` sec, `b
   >     d   >       d   >        d   >         d


import Data.Ratio ((%))

real2cf :: (RealFrac a, Integral b) => a -> [b]
real2cf x =
  let (i, f) = properFraction x
  in i :
     if f == 0
       then []
       else real2cf (1 / f)

main :: IO ()
main =
  mapM_
    print
    [ real2cf (13 % 11)
    , take 20 $ real2cf (sqrt 2)
    ]


import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class ConstructFromRationalNumber {
    private static class R2cf implements Iterator<Integer> {
        private int num;
        private int den;

        R2cf(int num, int den) {
            this.num = num;
            this.den = den;
        }

        @Override
        public boolean hasNext() {
            return den != 0;
        }

        @Override
        public Integer next() {
            int div = num / den;
            int rem = num % den;
            num = den;
            den = rem;
            return div;
        }
    }

    private static void iterate(R2cf generator) {
        generator.forEachRemaining(n -> System.out.printf("%d ", n));
        System.out.println();
    }

    public static void main(String[] args) {
        List<Map.Entry<Integer, Integer>> fracs = List.of(
                Map.entry(1, 2),
                Map.entry(3, 1),
                Map.entry(23, 8),
                Map.entry(13, 11),
                Map.entry(22, 7),
                Map.entry(-151, 77)
        );
        for (Map.Entry<Integer, Integer> frac : fracs) {
            System.out.printf("%4d / %-2d = ", frac.getKey(), frac.getValue());
            iterate(new R2cf(frac.getKey(), frac.getValue()));
        }

        System.out.println("\nSqrt(2) ->");
        List<Map.Entry<Integer, Integer>> root2 = List.of(
                Map.entry(    14_142,     10_000),
                Map.entry(   141_421,    100_000),
                Map.entry( 1_414_214,  1_000_000),
                Map.entry(14_142_136, 10_000_000)
        );
        for (Map.Entry<Integer, Integer> frac : root2) {
            System.out.printf("%8d / %-8d = ", frac.getKey(), frac.getValue());
            iterate(new R2cf(frac.getKey(), frac.getValue()));
        }

        System.out.println("\nPi ->");
        List<Map.Entry<Integer, Integer>> pi = List.of(
                Map.entry(         31,        10),
                Map.entry(        314,       100),
                Map.entry(      3_142,      1_000),
                Map.entry(     31_428,     10_000),
                Map.entry(    314_285,    100_000),
                Map.entry(  3_142_857,   1_000_000),
                Map.entry( 31_428_571,  10_000_000),
                Map.entry(314_285_714, 100_000_000)
        );
        for (Map.Entry<Integer, Integer> frac : pi) {
            System.out.printf("%9d / %-9d = ", frac.getKey(), frac.getValue());
            iterate(new R2cf(frac.getKey(), frac.getValue()));
        }
    }
}


/*
 * Program seconds2string, C89 version.
 *
 * Read input from argv[1] or stdin, write output to stdout.
 */

#define _CRT_SECURE_NO_WARNINGS /* unlocks printf in Microsoft Visual Studio */

#include <stdio.h>
#include <stdlib.h>

/*
 * Converting the number of seconds in a human-readable string.
 * It is worth noting that direct output to stdout would be even simpler.
 */
char* seconds2string(unsigned long seconds)
{
    int i;

    const unsigned long s =  1;
    const unsigned long m = 60 * s;
    const unsigned long h = 60 * m;
    const unsigned long d = 24 * h;
    const unsigned long w =  7 * d;

    const unsigned long coeff[5] = { w, d, h, m, s };
    const char units[5][4] = { "wk", "d", "hr", "min", "sec" };

    static char buffer[256];
    char* ptr = buffer;

    for ( i = 0; i < 5; i++ )
    {
        unsigned long value;
        value   = seconds / coeff[i];
        seconds = seconds % coeff[i];
        if ( value )
        {
            if ( ptr != buffer )
                ptr += sprintf(ptr, ", ");
            ptr += sprintf(ptr,"%lu %s",value,units[i]);
        }
    }

    return buffer;
}

/*
 * Main function for seconds2string program.
 */
int main(int argc, char argv[])
{
    unsigned long seconds;

    if ( (argc <  2) && scanf( "%lu", &seconds )
    ||   (argc >= 2) && sscanf( argv[1], "%lu", & seconds ) )
    {
        printf( "%s\n", seconds2string(seconds) );
        return EXIT_SUCCESS;
    }

    return EXIT_FAILURE;
}


# It'st most appropriate to define a Julia iterable object for this task
# Julia doesn't have Python'st yield, the closest to it is produce/consume calls with Julia tasks
# but for various reasons they don't work out for this task
# This solution works with two integers, a Julia rational or a real

mutable struct ContinuedFraction{T<:Integer}
    n1::T # numerator or real
    n2::T # denominator or 1 if real
    t1::T # generated coefficient
end

# Constructors for all possible input types
ContinuedFraction{T<:Integer}(n1::T, n2::T) = ContinuedFraction(n1, n2, 0)
ContinuedFraction(n::Rational) = ContinuedFraction(numerator(n), denominator(n))
ContinuedFraction(n::AbstractFloat) = ContinuedFraction(Rational(n))

# Methods to make our object iterable
Base.start(::ContinuedFraction) = nothing
# Returns true if we've prepared the continued fraction
Base.done(cf::ContinuedFraction, st) = cf.n2 == 0
# Generates the next coefficient
function Base.next(cf::ContinuedFraction, st)
    cf.n1, (cf.t1, cf.n2) = cf.n2, divrem(cf.n1, cf.n2)
    return cf.t1, nothing
end

# Tell Julia that this object always returns ints (all coeffs are integers)
Base.eltype{T}(::Type{ContinuedFraction{T}}) = T

# Overload the default collect function so that we can collect the first maxiter coeffs of infinite continued fractions
# array slicing doesn't work as Julia crashes before the slicing due to our infinitely long array
function Base.collect(itr::ContinuedFraction, maxiter::Integer = 100)
    r = Array{eltype(itr)}(maxiter)
    i = 1
    for v in itr
        r[i] = v
        i += 1
        if i > maxiter break end
    end
    return r[1:i-1]
end

# Test cases according to task description with outputs in comments
println(collect(ContinuedFraction(1, 2)))       # => [0, 2]
println(collect(ContinuedFraction(3, 1)))       # => [3]
println(collect(ContinuedFraction(23, 8)))      # => [2, 1, 7]
println(collect(ContinuedFraction(13, 11)))     # => [1, 5, 2]
println(collect(ContinuedFraction(22, 7)))      # => [3, 7]
println(collect(ContinuedFraction(14142, 10000)))       # => [1, 2, 2, 2, 2, 2, 1, 1, 29]
println(collect(ContinuedFraction(141421, 100000)))     # => [1, 2, 2, 2, 2, 2, 2, 3, 1, 1, 3, 1, 7, 2]
println(collect(ContinuedFraction(1414214, 1000000)))   # => [1, 2, 2, 2, 2, 2, 2, 2, 3, 6, 1, 2, 1, 12]
println(collect(ContinuedFraction(14142136, 10000000))) # => [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 1, 2, 4, 1, 1, 2]

println(collect(ContinuedFraction(13 // 11)))   # => [1, 5, 2]
println(collect(ContinuedFraction(√2), 20))     # => [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]


// version 1.1.2
// compile with -Xcoroutines=enable flag from command line

import kotlin.coroutines.experimental.buildSequence

fun r2cf(frac: Pair<Int, Int>) =
    buildSequence {
        var num = frac.first
        var den = frac.second
        while (Math.abs(den) != 0) {
            val div = num / den
            val rem = num % den
            num = den
            den = rem
            yield(div)
        }
    }

fun iterate(seq: Sequence<Int>) {
    for (i in seq) print("$i ")
    println()
}

fun main(args: Array<String>) {
    val fracs = arrayOf(1 to 2, 3 to 1, 23 to 8, 13 to 11, 22 to 7, -151 to 77)
    for (frac in fracs) {
        print("${"%4d".format(frac.first)} / ${"%-2d".format(frac.second)} = ")
        iterate(r2cf(frac))
    }
    val root2 = arrayOf(14142 to 10000, 141421 to 100000,
                        1414214 to 1000000, 14142136 to 10000000)
    println("\nSqrt(2) ->")
    for (frac in root2) {
        print("${"%8d".format(frac.first)} / ${"%-8d".format(frac.second)} = ")
        iterate(r2cf(frac))
    }
    val pi = arrayOf(31 to 10, 314 to 100, 3142 to 1000, 31428 to 10000,
                     314285 to 100000, 3142857 to 1000000,
                     31428571 to 10000000, 314285714 to 100000000)
    println("\nPi ->")
    for (frac in pi) {
        print("${"%9d".format(frac.first)} / ${"%-9d".format(frac.second)} = ")
        iterate(r2cf(frac))
    }
}


using System;
using System.Collections.Generic;
using System.Linq;

namespace ConvertSecondsToCompoundDuration
{
  class Program
  {
    static void Main( string[] args )
    {
      foreach ( string arg in args )
      {
        int duration ;
        bool isValid = int.TryParse( arg , out duration ) ;

        if ( !isValid     ) { Console.Error.WriteLine( "ERROR: Not an integer: {0}"           , arg ) ; }
        if ( duration < 0 ) { Console.Error.WriteLine( "ERROR: duration must be non-negative" , arg ) ; }

        Console.WriteLine();
        Console.WriteLine( "{0:#,##0} seconds ==> {1}" , duration , FormatAsDuration(duration) ) ;

      }
    }

    private static string FormatAsDuration( int duration )
    {
      if ( duration < 0 ) throw new ArgumentOutOfRangeException("duration") ;
      return string.Join( ", " , GetDurationParts(duration)  ) ;
    }

    private static IEnumerable<string> GetDurationParts( int duration )
    {
      var parts = new[]
      {
        new { Name="wk" , Length = 7*24*60*60*1 , } ,
        new { Name="d"  , Length =   24*60*60*1 , } ,
        new { Name="h"  , Length =      60*60*1 , } ,
        new { Name="m"  , Length =         60*1 , } ,
        new { Name="s"  , Length =            1 , } ,
      } ;

      foreach ( var part in parts )
      {
        int n = Math.DivRem( duration , part.Length , out duration ) ;
        if ( n > 0 ) yield return string.Format( "{0} {1}" , n , part.Name ) ;
      }

    }

  }

}


ContinuedFraction[1/2]
ContinuedFraction[3]
ContinuedFraction[23/8]
ContinuedFraction[13/11]
ContinuedFraction[22/7]
ContinuedFraction[-151/77]
ContinuedFraction[14142/10000]
ContinuedFraction[141421/100000]
ContinuedFraction[1414214/1000000]
ContinuedFraction[14142136/10000000]


MODULE ConstructFromrationalNumber;
FROM FormatString IMPORT FormatString;
FROM Terminal IMPORT WriteString,WriteLn,ReadChar;

TYPE R2cf = RECORD
    num,den : INTEGER;
END;

PROCEDURE HasNext(self : R2cf) : BOOLEAN;
BEGIN
    RETURN self.den # 0;
END HasNext;

PROCEDURE Next(VAR self : R2cf) : INTEGER;
VAR div,rem : INTEGER;
BEGIN
    div := self.num / self.den;
    rem := self.num REM self.den;
    self.num := self.den;
    self.den := rem;
    RETURN div;
END Next;

PROCEDURE Iterate(self : R2cf);
VAR buf : ARRAY[0..64] OF CHAR;
BEGIN
    WHILE HasNext(self) DO
        FormatString("%i ", buf, Next(self));
        WriteString(buf);
    END;
    WriteLn;
END Iterate;

PROCEDURE Print(num,den : INTEGER);
VAR frac : R2cf;
VAR buf : ARRAY[0..64] OF CHAR;
BEGIN
    FormatString("%9i / %-9i = ", buf, num, den);
    WriteString(buf);

    frac.num := num;
    frac.den := den;
    Iterate(frac);
END Print;

VAR frac : R2cf;
BEGIN
    Print(1,2);
    Print(3,1);
    Print(23,8);
    Print(13,11);
    Print(22,7);
    Print(-151,77);

    WriteLn;
    WriteString("Sqrt(2) ->");
    WriteLn;
    Print(14142,10000);
    Print(141421,100000);
    Print(1414214,1000000);
    Print(14142136,10000000);

    WriteLn;
    WriteString("Pi ->");
    WriteLn;
    Print(31,10);
    Print(314,100);
    Print(3142,1000);
    Print(31428,10000);
    Print(314285,100000);
    Print(3142857,1000000);
    Print(31428571,10000000);
    Print(314285714,100000000);

    ReadChar;
END ConstructFromrationalNumber.


#include <iostream>
#include <vector>

using entry = std::pair<int, const char*>;

void print(const std::vector<entry>& entries, std::ostream& out = std::cout)
{
    bool first = true;
    for(const auto& e: entries) {
        if(!first) out << ", ";
        first = false;
        out << e.first << " " << e.second;
    }
    out << '\n';
}

std::vector<entry> convert(int seconds)
{
    static const entry time_table[] = {
        {7*24*60*60, "wk"}, {24*60*60, "d"}, {60*60, "hr"}, {60, "min"}, {1, "sec"}
    };
    std::vector<entry> result;
    for(const auto& e: time_table) {
        int time = seconds / e.first;
        if(time != 0) result.emplace_back(time, e.second);
        seconds %= e.first;
    }
    return result;
}

int main()
{
    std::cout << "   7259 sec is "; print(convert(   7259));
    std::cout << "  86400 sec is "; print(convert(  86400));
    std::cout << "6000000 sec is "; print(convert(6000000));
}


(require '[clojure.string :as string])

(def seconds-in-minute 60)
(def seconds-in-hour (* 60 seconds-in-minute))
(def seconds-in-day (* 24 seconds-in-hour))
(def seconds-in-week (* 7 seconds-in-day))

(defn seconds->duration [seconds]
  (let [weeks   ((juxt quot rem) seconds seconds-in-week)
        wk      (first weeks)
        days    ((juxt quot rem) (last weeks) seconds-in-day)
        d       (first days)
        hours   ((juxt quot rem) (last days) seconds-in-hour)
        hr      (first hours)
        min     (quot (last hours) seconds-in-minute)
        sec     (rem (last hours) seconds-in-minute)]
    (string/join ", "
                 (filter #(not (string/blank? %))
                         (conj []
                               (when (> wk 0) (str wk " wk"))
                               (when (> d 0) (str d " d"))
                               (when (> hr 0) (str hr " hr"))
                               (when (> min 0) (str min " min"))
                               (when (> sec 0) (str sec " sec")))))))

(seconds->duration 7259)
(seconds->duration 86400)
(seconds->duration 6000000)


with javascript_semantics
function r2cf(atom num, denom)
    atom quot = 0
    if denom!=0 then
        quot = trunc(num/denom)
        {num,denom} = {denom,num-quot*denom}
    end if
    return {quot,{num,denom}}
end function

procedure test(string txt, sequence tests)
    printf(1,"Running %s :\n",{txt})
    for i=1 to length(tests) do
        atom {num,denom} = tests[i], quot
        string sep = ";"
        printf(1,"%d/%d ==> [",{num,denom})
        while denom!=0 do
            {quot,{num,denom}} = r2cf(num,denom)
            printf(1,"%d",quot)
            if denom=0 then exit end if
            printf(1,"%s",sep)
            sep = ","
        end while
        printf(1,"]\n")
    end for
    printf(1,"\n")
end procedure

constant examples = {{1,2}, {3,1}, {23,8}, {13,11}, {22,7}, {-151,77}},
         sqrt2 = {{14142,10000}, {141421,100000}, {1414214,1000000}, {14142136,10000000}},
         pi = {{31,10}, {314,100}, {3142,1000}, {31428,10000}, {314285,100000}, {3142857,1000000},
               {31428571,10000000}, {314285714,100000000}, {3141592653589793,1000000000000000}}

test("the examples",examples)
test("for sqrt(2)",sqrt2)
test("for pi",pi)


duration = proc (s: int) returns (string)
    own units: array[string] := array[string]$["wk","d","hr","min","sec"]
    own sizes: array[int] := array[int]$[2:7,24,60,60]

    d: string := ""
    r: int
    for i: int in int$from_to_by(5,1,-1) do
        begin
            r := s // sizes[i]
            s := s / sizes[i]
        end except when bounds:
            r := s
        end
        if r ~= 0 then
            d := ", " || int$unparse(r) || " " || units[i] || d
        end
    end
    return(string$rest(d,3))
end duration

start_up = proc ()
    po: stream := stream$primary_output()
    tests: array[int] := array[int]$[7259,86400,6000000]

    for test: int in array[int]$elements(tests) do
        stream$putl(po, int$unparse(test) || " => " || duration(test))
    end
end start_up


       identification division.
       program-id. fmt-dura.
       data division.
       working-storage section.
       1 input-seconds pic 9(8).
       1 formatted-duration pic x(30) global.
       1 fractions.
        2 weeks pic z(3)9.
        2 days pic z(3)9.
        2 hours pic z(3)9.
        2 minutes pic z(3)9.
        2 seconds pic z(3)9.
       1 .
        2 weeks-str pic x(4) value "wk".
        2 days-str pic x(4) value "d".
        2 hours-str pic x(4) value "hr".
        2 minutes-str pic x(4) value "min".
        2 seconds-str pic x(4) value "sec".
       1 work binary global.
        2 str-pos pic 9(4).
        2 chars-transferred pic 9(4).
       procedure division.
       begin.
           display "Enter duration (seconds): " no advancing
           accept input-seconds
           divide input-seconds by 60 giving input-seconds
               remainder seconds
           divide input-seconds by 60 giving input-seconds
               remainder minutes
           divide input-seconds by 24 giving input-seconds
               remainder hours
           divide input-seconds by 7 giving weeks
               remainder days
           move 1 to str-pos
           call "fmt" using weeks weeks-str
           call "fmt" using days days-str
           call "fmt" using hours hours-str
           call "fmt" using minutes minutes-str
           call "fmt" using seconds seconds-str
           display formatted-duration
           stop run
           .

       identification division.
       program-id. fmt.
       data division.
       working-storage section.
       77 nothing pic x.
       linkage section.
       1 formatted-value pic x(4).
       1 duration-size pic x(4).
       procedure division using formatted-value duration-size.
       begin.
           if function numval (formatted-value) not = 0
               perform insert-comma-space
               unstring formatted-value delimited all space
                   into nothing formatted-duration (str-pos:)
                   count chars-transferred
               add chars-transferred to str-pos
               string space delimited size
                   duration-size delimited space
                   into formatted-duration pointer str-pos
           end-if
           exit program
           .

       insert-comma-space.
           if str-pos > 1
               move ", " to formatted-duration (str-pos:)
               add 2 to str-pos
           end-if
           .
       end program fmt.
       end program fmt-dura.


(defconstant +seconds-in-minute* 60)
(defconstant +seconds-in-hour* (* 60 +seconds-in-minute*))
(defconstant +seconds-in-day* (* 24 +seconds-in-hour*))
(defconstant +seconds-in-week* (* 7 +seconds-in-day*))

(defun seconds->duration (seconds)
  (multiple-value-bind (weeks wk-remainder) (floor seconds +seconds-in-week*)
    (multiple-value-bind (days d-remainder) (floor wk-remainder +seconds-in-day*)
      (multiple-value-bind (hours hr-remainder) (floor d-remainder +seconds-in-hour*)
        (multiple-value-bind (minutes secs) (floor hr-remainder +seconds-in-minute*)
          (let ((chunks nil))
            (unless (zerop secs)    (push (format nil "~D sec" secs) chunks))
            (unless (zerop minutes) (push (format nil "~D min" minutes) chunks))
            (unless (zerop hours)   (push (format nil "~D hr" hours) chunks))
            (unless (zerop days)    (push (format nil "~D d" days) chunks))
            (unless (zerop weeks)   (push (format nil "~D wk" weeks) chunks))
            (format t "~{~A~^, ~}~%" chunks)))))))

(seconds->duration 7259)
(seconds->duration 86400)
(seconds->duration 6000000)


import std.stdio, std.conv, std.algorithm;

immutable uint SECSPERWEEK = 604_800;
immutable uint SECSPERDAY = 86_400;
immutable uint SECSPERHOUR = 3_600;
immutable uint SECSPERMIN = 60;

string ConvertSeconds(in uint seconds)
{
    uint rem = seconds;

    uint weeks = rem / SECSPERWEEK;
    rem %= SECSPERWEEK;
    uint days = rem / SECSPERDAY;
    rem %= SECSPERDAY;
    uint hours = rem / SECSPERHOUR;
    rem %= SECSPERHOUR;
    uint mins = rem / SECSPERMIN;
    rem %= SECSPERMIN;

    string formatted = "";

    (weeks != 0) ? formatted ~= (weeks.to!string ~ " wk, ") : formatted;
    (days != 0) ? formatted ~= (days.to!string ~ " d, ") : formatted;
    (hours != 0) ? formatted ~= (hours.to!string ~ " hr, ") : formatted;
    (mins != 0) ? formatted ~= (mins.to!string ~ " min, ") : formatted;
    (rem != 0) ? formatted ~= (rem.to!string ~ " sec") : formatted;

    if (formatted.endsWith(", ")) return formatted[0..$-2];

    return formatted;
}

void main()
{
    7_259.ConvertSeconds.writeln;
    86_400.ConvertSeconds.writeln;
    6_000_000.ConvertSeconds.writeln;
}


#lang racket

(define ((r2cf n d))
  (or (zero? d)
      (let-values ([(q r) (quotient/remainder n d)])
        (set! n d)
        (set! d r)
        q)))

(define (r->cf n d)
  (for/list ([i (in-producer (r2cf n d) #t)]) i))

(define (real->cf x places)
  (define d (expt 10 places))
  (define n (exact-floor (* x d)))
  (r->cf n d))

(map r->cf
     '(1 3 23 13 22 -151)
     '(2 1  8 11  7   77))
(real->cf (sqrt 2) 10)
(real->cf pi 10)


sub r2cf(Rat $x is copy) {
    gather loop {
	$x -= take $x.floor;
	last unless $x > 0;
	$x = 1 / $x;
    }
}

say r2cf(.Rat) for <1/2 3 23/8 13/11 22/7 1.41 1.4142136>;


/*REXX program converts a  decimal  or  rational fraction  to a  continued fraction.    */
numeric digits 230                               /*determines how many terms to be gened*/
say '              1/2  ──► CF: '   r2cf( '1/2'      )
say '               3   ──► CF: '   r2cf(   3        )
say '             23/8  ──► CF: '   r2cf( '23/8'     )
say '             13/11 ──► CF: '   r2cf( '13/11'    )
say '             22/7  ──► CF: '   r2cf( '22/7 '    )
say '                       ___'
say '───────── attempts at √ 2.'
say '14142/1e4          ──► CF: '   r2cf( '14142/1e4 '          )
say '141421/1e5         ──► CF: '   r2cf( '141421/1e5 '         )
say '1414214/1e6        ──► CF: '   r2cf( '1414214/1e6 '        )
say '14142136/1e7       ──► CF: '   r2cf( '14142136/1e7 '       )
say '141421356/1e8      ──► CF: '   r2cf( '141421356/1e8 '      )
say '1414213562/1e9     ──► CF: '   r2cf( '1414213562/1e9 '     )
say '14142135624/1e10   ──► CF: '   r2cf( '14142135624/1e10 '   )
say '141421356237/1e11  ──► CF: '   r2cf( '141421356237/1e11 '  )
say '1414213562373/1e12 ──► CF: '   r2cf( '1414213562373/1e12 ' )
say '√2                 ──► CF: '   r2cf(  sqrt(2)              )
say
say '───────── an attempt at pi'
say 'pi                 ──► CF: '   r2cf(  pi() )
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
$maxFact: procedure;  parse arg x 1 _x,y;   y=10**(digits()-1);   b=0;  h=1;  a=1;     g=0
            do while a<=y & g<=y;  n=trunc(_x);  _=a;  a=n*a+b;   b=_;  _=g;  g=n*g+h; h=_
            if n=_x | a/g=x  then do; if a>y | g>y  then iterate; b=a;  h=g;  leave;   end
            _x=1/(_x-n);  end;                           return  b'/'h
/*──────────────────────────────────────────────────────────────────────────────────────*/
pi: return 3.1415926535897932384626433832795028841971693993751058209749445923078164062862,
           || 089986280348253421170679821480865132823066470938446095505822317253594081284,
           || 811174502841027019385211055596446229489549303819644288109756659334461284756,
           || 48233786783165271                        /* ··· should  ≥  NUMERIC DIGITS */
/*──────────────────────────────────────────────────────────────────────────────────────*/
r2cf: procedure; parse arg g 1 s 2;  $=;     if s=='-'  then g=substr(g, 2)
                                                        else s=
      if pos(., g)\==0  then do;  if \datatype(g, 'N')  then call serr 'not numeric:'   g
                                  g=$maxfact(g)
                             end
      if pos('/', g)==0      then g=g"/"1
      parse var  g   n  '/'  d
      if \datatype(n, 'W')   then call serr    "a numerator isn't an integer:"    n
      if \datatype(d, 'W')   then call serr  "a denominator isn't an integer:"    d
      if d=0                 then call serr  'a denominator is zero'
      n=abs(n)                                         /*ensure numerator is positive.  */
                         do  while  d\==0;      _=d    /*where the rubber meets the road*/
                         $=$  s || (n%d)               /*append another number to list. */
                         d=n // d;              n=_    /* %  is int div,  // is modulus.*/
                         end   /*while*/
      return strip($)
/*──────────────────────────────────────────────────────────────────────────────────────*/
serr: say;    say '***error***';    say;    say arg(1);     say;    exit 13
/*──────────────────────────────────────────────────────────────────────────────────────*/
sqrt: procedure; parse arg x;  if x=0  then return 0;  d=digits();   h=d+6;   numeric form
      m.=9; numeric digits; parse value format(x,2,1,,0) 'E0' with g 'E' _ .; g=g*.5'e'_%2
                     do j=0  while h>9;      m.j=h;               h=h%2+1;       end /*j*/
                     do k=j+5  to 0  by -1;  numeric digits m.k;  g=(g+x/g)*.5;  end /*k*/
      numeric digits d;                      return g/1


func split sec . s$ .
  divs[] = [ 60 60 24 7 ]
  n$[] = [ "sec" "min" "hr" "d" "wk" ]
  len r[] 5
  for i range 4
    r[i] = sec mod divs[i]
    sec = sec div divs[i]
  .
  r[4] = sec
  s$ = ""
  for i = 4 downto 0
    if r[i] <> 0
      if s$ <> ""
        s$ &= ", "
      .
      s$ &= r[i] & " " & n$[i]
    .
  .
.
call split 7259 s$
print s$
call split 86400 s$
print s$
call split 6000000 s$
print s$


defmodule Convert do
  @minute 60
  @hour   @minute*60
  @day    @hour*24
  @week   @day*7
  @divisor [@week, @day, @hour, @minute, 1]

  def sec_to_str(sec) do
    {_, [s, m, h, d, w]} =
        Enum.reduce(@divisor, {sec,[]}, fn divisor,{n,acc} ->
          {rem(n,divisor), [div(n,divisor) | acc]}
        end)
    ["#{w} wk", "#{d} d", "#{h} hr", "#{m} min", "#{s} sec"]
    |> Enum.reject(fn str -> String.starts_with?(str, "0") end)
    |> Enum.join(", ")
  end
end

Enum.each([7259, 86400, 6000000], fn sec ->
  :io.fwrite "~10w sec : ~s~n", [sec, Convert.sec_to_str(sec)]
end)


-module(convert_seconds).

-export([test/0]).

test() ->
	lists:map(fun convert/1, [7259, 86400, 6000000]),
	ok.

convert(Seconds) ->
	io:format(
		"~7s seconds = ~s\n",
		[integer_to_list(Seconds), compoundDuration(Seconds)] ).

% Compound duration of t seconds.  The argument is assumed to be positive.
compoundDuration(Seconds) ->
	intercalate(
		", ",
		lists:map(
			fun({D,L}) -> io_lib:format("~p ~s",[D, L]) end,
			compdurs(Seconds) ) ).

% Time broken down into non-zero durations and their labels.
compdurs(T) ->
	Ds =
		reduceBy(
			T,
			lists:map(
				fun(Dl) -> element(1,Dl) end,
				tl(durLabs()) ) ),
	lists:filter(
			fun(Dl) -> element(1,Dl) /= 0 end,
			lists:zip(
				Ds,
				lists:map(
					fun(Dl) -> element(2,Dl) end,
					durLabs() ) ) ).

% Duration/label pairs.
durLabs() ->
	[
		{undefined, "wk"},
		{7, "d"},
		{24, "hr"},
		{60, "min"},
		{60, "sec"}
	].

reduceBy(N, Xs) ->
	{N_, Ys} = mapaccumr(fun quotRem/2, N, Xs),
	[N_ | Ys].

quotRem(X1, X2) ->
	{X1 div X2, X1 rem X2}.

% **************************************************
% Adapted from http://lpaste.net/edit/47875
% **************************************************

mapaccuml(_,I,[]) ->
	{I, []};
mapaccuml(F,I,[H|T]) ->
	{Accum, NH} = F(I,H),
	{FAccum, NT} = mapaccuml(F,Accum,T),
	{FAccum, [NH | NT]}.

mapaccumr(_,I,[]) ->
	{I, []};
mapaccumr(F,I,L) ->
	{Acc, Ys} = mapaccuml(F,I,lists:reverse(L)),
	{Acc, lists:reverse(Ys)}.

% **************************************************

% **************************************************
% Copied from https://github.com/tim/erlang-oauth/blob/master/src/oauth.erl
% **************************************************

intercalate(Sep, Xs) ->
  lists:concat(intersperse(Sep, Xs)).

intersperse(_, []) ->
  [];
intersperse(_, [X]) ->
  [X];
intersperse(Sep, [X | Xs]) ->
  [X, Sep | intersperse(Sep, Xs)].

% **************************************************


# Generate a continued fraction from a rational number

def r2cf(n1,n2)
  while n2 > 0
    n1, (t1, n2) = n2, n1.divmod(n2)
    yield t1
  end
end


; Create a terminating Continued Fraction generator for the given rational number.
; Returns one term per call; returns #f when no more terms remaining.
(define make-continued-fraction-gen
  (lambda (rat)
    (let ((num (numerator rat)) (den (denominator rat)))
      (lambda ()
        (if (= den 0)
          #f
          (let ((ret (quotient num den))
                (rem (modulo num den)))
            (set! num den)
            (set! den rem)
            ret))))))

; Return the continued fraction representation of a rational number as a string.
(define rat->cf-string
  (lambda (rat)
    (let* ((cf (make-continued-fraction-gen rat))
           (str (string-append "[" (format "~a" (cf))))
           (sep ";"))
      (let loop ((term (cf)))
        (when term
          (set! str (string-append str (format "~a ~a" sep term)))
          (set! sep ",")
          (loop (cf))))
      (string-append str "]"))))

; Return the continued fraction representation of a rational number as a list of terms.
(define rat->cf-list
  (lambda (rat)
    (let ((cf (make-continued-fraction-gen rat))
          (lst '()))
      (let loop ((term (cf)))
        (when term
          (set! lst (append lst (list term)))
          (loop (cf))))
      lst)))


open System

let convert seconds =
    let span = TimeSpan.FromSeconds(seconds |> float)
    let (wk, day) = Math.DivRem(span.Days, 7)
    let parts =
        [(wk, "wk"); (day, "day"); (span.Hours, "hr"); (span.Minutes, "min"); (span.Seconds, "sec")]
    let result =
        List.foldBack (fun (n, u) acc ->
                    (if n > 0 then n.ToString() + " " + u else "")
                    + (if n > 0 && acc.Length > 0 then ", " else "")
                    + acc
                  ) parts ""
    if result.Length > 0 then result else "0 sec"

[<EntryPoint>]
let main argv =
    argv
    |> Seq.map (fun str -> let sec = UInt32.Parse str in (sec, convert sec))
    |> Seq.iter (fun (s, v) -> printfn "%10i = %s" s v)
    0


USING: assocs io kernel math math.parser qw sequences
sequences.generalizations ;

: mod/ ( x y -- w z ) /mod swap ;

: convert ( n -- seq )
    60 mod/ 60 mod/ 24 mod/ 7 mod/ 5 narray reverse ;

: .time ( n -- )
    convert [ number>string ] map qw{ wk d hr min sec } zip
    [ first "0" = ] reject [ " " join ] map ", " join print ;

7259 86400 6000000 [ .time ] [email protected]


package require Tcl 8.6

proc r2cf {n1 {n2 1}} {
    # Convert a decimal fraction (e.g., 1.23) into a form we can handle
    if {$n1 != int($n1) && [regexp {\.(\d+)} $n1 -> suffix]} {
	set pow [string length $suffix]
	set n1 [expr {int($n1 * 10**$pow)}]
	set n2 [expr {$n2 * 10**$pow}]
    }
    # Construct the continued fraction as a coroutine that yields the digits in sequence
    coroutine cf\#[incr ::cfcounter] apply {{n1 n2} {
	yield [info coroutine]
	while {$n2 > 0} {
	    yield [expr {$n1 / $n2}]
	    set n2 [expr {$n1 % [set n1 $n2]}]
	}
	return -code break
    }} $n1 $n2
}


CREATE C 0 ,
: .,   C @ IF ." , " THEN  1 C ! ;
: .TIME ( n --)
   [ 60 60 24 7 * * * ]L /MOD ?DUP-IF ., . ." wk" THEN
   [ 60 60 24   * *   ]L /MOD ?DUP-IF ., . ." d" THEN
   [ 60 60      *     ]L /MOD ?DUP-IF ., . ." hr" THEN
   [ 60               ]L /MOD ?DUP-IF ., . ." min" THEN
                              ?DUP-IF ., . ." sec" THEN  0 C ! ;


      SUBROUTINE PROUST(T)	!Remembrance of time passed.
       INTEGER T		!The time, in seconds. Positive only, please.
       INTEGER NTYPES		!How many types of time?
       PARAMETER (NTYPES = 5)	!This should do.
       INTEGER USIZE(NTYPES)	!Size of the time unit.
       CHARACTER*3 UNAME(NTYPES)!Name of the time unit.
       PARAMETER (USIZE = (/7*24*60*60, 24*60*60, 60*60,   60,    1/))	!The compiler does some arithmetic.
       PARAMETER (UNAME = (/      "wk",      "d",  "hr","min","sec"/))	!Approved names, with trailing spaces.
       CHARACTER*28 TEXT	!A scratchpad.
       INTEGER I,L,N,S		!Assistants.
        S = T			!A copy I can mess with.
        L = 0			!No text has been generated.
        DO I = 1,NTYPES		!Step through the types to do so.
          N = S/USIZE(I)	!Largest first.
          IF (N.GT.0) THEN	!Above the waterline?
            S = S - N*USIZE(I)		!Yes! Remove its contribution.
            IF (L.GT.0) THEN		!Is this the first text to be rolled?
              L = L + 2				!No.
              TEXT(L - 1:L) = ", "		!Cough forth some punctuation.
            END IF			!Now ready for this count.
            WRITE (TEXT(L + 1:),1) N,UNAME(I)	!Place, with the unit name.
    1       FORMAT (I0,1X,A)		!I0 means I only: variable-length, no leading spaces.
            L = LEN_TRIM(TEXT)		!Find the last non-blank resulting.
          END IF			!Since I'm not keeping track.
        END DO			!On to the next unit.
Cast forth the result.
        WRITE (6,*) T,">",TEXT(1:L),"<"	!With annotation.
       END			!Simple enough with integers.

       PROGRAM MARCEL		!Stir the cup.
       CALL PROUST(7259)
       CALL PROUST(7260)
       CALL PROUST(86400)
       CALL PROUST(6000000)
       CALL PROUST(0)
       CALL PROUST(-666)
       END


'FreeBASIC version 1.05 32/64 bit

Sub Show(m As Long)
    Dim As Long c(1 To 5)={604800,86400,3600,60,1}
    Dim As String g(1 To 5)={" Wk"," d"," hr"," min"," sec"},comma
    Dim As Long b(1 To 5),flag,m2=m
    Redim As Long s(0)
    For n As Long=1 To 5
        If m>=c(n) Then
            Do
                Redim Preserve s(Ubound(s)+1)
                s(Ubound(s))=c(n)
                m=m-c(n)
            Loop Until m<c(n)
        End If
    Next n
    For n As Long=1 To Ubound(s)
        For m As Long=1 To 5
            If s(n)=c(m) Then b(m)+=1
        Next m
    Next n
    Print m2;" seconds = ";
    For n As Long=1 To 5
        If b(n) Then: comma=Iif(n<5 Andalso b(n+1),","," and"):flag+=1
        If flag=1 Then comma=""
        Print comma;b(n);g(n);
    End If
Next
Print
End Sub

#define seconds

Show 7259 seconds
Show 86400 seconds
Show 6000000 seconds
sleep


include c:\cxpl\codes;
real Val;

proc R2CF(N1, N2, Lev);         \Output continued fraction for N1/N2
int  N1, N2, Lev;
int  Quot, Rem;
[if Lev=0 then Val:= 0.0;
Quot:= N1/N2;
Rem:= rem(0);
IntOut(0, Quot);
if Rem then [ChOut(0, if Lev then ^, else ^;);  R2CF(N2, Rem, Lev+1)];
Val:= Val + float(Quot);        \generate value from continued fraction
if Lev then Val:= 1.0/Val;
];

int I, Data;
[Data:= [1,2, 3,1, 23,8, 13,11, 22,7, 0];
Format(0, 15);
I:= 0;
while Data(I) do
   [IntOut(0, Data(I));  ChOut(0, ^/);  IntOut(0, Data(I+1));  ChOut(0, 9\tab\);
   ChOut(0, ^[);  R2CF(Data(I), Data(I+1), 0);  ChOut(0, ^]);  ChOut(0, 9\tab\);
   RlOut(0, Val);  CrLf(0);
   I:= I+2];
]


fcn r2cf(nom,dnom){ // -->Walker (iterator)
   Walker.tweak(fcn(state){
      nom,dnom:=state;
      if(dnom==0) return(Void.Stop);
      n,d:=nom.divr(dnom);
      state.clear(dnom,d);
      n
   }.fp(List(nom,dnom)))  // partial application (light weight closure)
}


wk := week
n = eval[input["Enter duration in seconds: "]]
res = n s -> [0, "wk", "d", "hr", "min", "sec", 0]
res =~ %s/, 0[^,]+//g
println[res]


Public Sub Main()
Dim iInput As Integer[] = [7259, 86400, 6000000]                                  'Input details
Dim iChecks As Integer[] = [604800, 86400, 3600, 60]                              'Weeks, days, hours, mins in seconds
Dim iTime As New Integer[5]                                                       'To store wk, d, hr, min & sec
Dim iOriginal, iSec, iLoop As Integer                                             'Various integers
Dim sOrd As String[] = [" wk", " d", " hr", " min", " sec"]                       'To add to the output string
Dim sOutput As String                                                             'Output string

For Each iSec In iInput                                                           'For each iInput
  iOriginal = iSec                                                                'Store orginal value in seconds
  iTime[4] = iSec                                                                 'Store seconds in iTime[4]

  For iLoop = 0 To 3                                                              'Loop through wk, d, hr, min & sec
    If iTime[4] >= iChecks[iLoop] Then                                            'Check if value is = to wk, d, hr, min
      iTime[iLoop] = Int(iTime[4] / iChecks[iLoop])                               'Put the correct value for wk, d, hr, min in iTime
      iTime[4] = iTime[4] - (iTime[iLoop] * iChecks[iLoop])                       'Remove the amount of seconds for wk, d, hr, min from iTime[4]
    Endif
  Next

  For iLoop = 0 To 4                                                              'Loop through wk, d, hr, min & secs
    If iTime[iLoop] > 0 Then sOutput &= ", " & Str(iTime[iLoop]) & sOrd[iLoop]    'Add comma and ordinal as needed
  Next

  If Left(sOutput, 2) = ", " Then sOutput = Mid(sOutput, 3)                       'Remove unnecessary ", "
  sOutput = Format(Str(iOriginal), "#######") & " Seconds = " & sOutput           'Add original seconds to the output string
  Print sOutput                                                                   'Print sOutput string
  sOutput = ""                                                                    'Clear the sOutput string
  iTime = New Integer[5]                                                          'Reset iTime[]
Next

End


package main

import "fmt"

func main(){
	fmt.Println(TimeStr(7259))
	fmt.Println(TimeStr(86400))
	fmt.Println(TimeStr(6000000))
}

func TimeStr(sec int)(res string){
	wks, sec := sec / 604800,sec % 604800
	ds, sec := sec / 86400, sec % 86400
	hrs, sec := sec / 3600, sec % 3600
	mins, sec := sec / 60, sec % 60
	CommaRequired := false
	if wks != 0 {
		res += fmt.Sprintf("%d wk",wks)
		CommaRequired = true
	}
	if ds != 0 {
		if CommaRequired {
			res += ", "
		}
		res += fmt.Sprintf("%d d",ds)
		CommaRequired = true
	}
	if hrs != 0 {
		if CommaRequired {
			res += ", "
		}
		res += fmt.Sprintf("%d hr",hrs)
		CommaRequired = true
	}
	if mins != 0 {
		if CommaRequired {
			res += ", "
		}
		res += fmt.Sprintf("%d min",mins)
		CommaRequired = true
	}
	if sec != 0 {
		if CommaRequired {
			res += ", "
		}
		res += fmt.Sprintf("%d sec",sec)
	}
	return
}


import Control.Monad (forM_)
import Data.List (intercalate, mapAccumR)
import System.Environment (getArgs)
import Text.Printf (printf)
import Text.Read (readMaybe)

reduceBy :: Integral a => a -> [a] -> [a]
n `reduceBy` xs = n' : ys where (n', ys) = mapAccumR quotRem n xs

-- Duration/label pairs.
durLabs :: [(Integer, String)]
durLabs = [(undefined, "wk"), (7, "d"), (24, "hr"), (60, "min"), (60, "sec")]

-- Time broken down into non-zero durations and their labels.
compdurs :: Integer -> [(Integer, String)]
compdurs t = let ds = t `reduceBy` (map fst $ tail durLabs)
             in filter ((/=0) . fst) $ zip ds (map snd durLabs)

-- Compound duration of t seconds.  The argument is assumed to be positive.
compoundDuration :: Integer -> String
compoundDuration = intercalate ", " . map (uncurry $ printf "%d %s") . compdurs

main :: IO ()
main = do
  args <- getArgs
  forM_ args $ \arg -> case readMaybe arg of
    Just n  -> printf "%7d seconds = %s\n" n (compoundDuration n)
    Nothing -> putStrLn $ "Invalid number of seconds: " ++ arg


public class CompoundDuration {

    public static void main(String[] args) {
        compound(7259);
        compound(86400);
        compound(6000_000);
    }

    private static void compound(long seconds) {
        StringBuilder sb = new StringBuilder();

        seconds = addUnit(sb, seconds, 604800, " wk, ");
        seconds = addUnit(sb, seconds, 86400, " d, ");
        seconds = addUnit(sb, seconds, 3600, " hr, ");
        seconds = addUnit(sb, seconds, 60, " min, ");
        addUnit(sb, seconds, 1, " sec, ");

        sb.setLength(sb.length() > 2 ? sb.length() - 2 : 0);

        System.out.println(sb);
    }

    private static long addUnit(StringBuilder sb, long sec, long unit, String s) {
        long n;
        if ((n = sec / unit) > 0) {
            sb.append(n).append(s);
            sec %= (n * unit);
        }
        return sec;
    }
}


(function () {
    'use strict';

    // angloDuration :: Int -> String
    function angloDuration(intSeconds) {
        return zip(
                weekParts(intSeconds),
                ['wk', 'd', 'hr', 'min','sec']
            )
            .reduce(function (a, x) {
                return a.concat(x[0] ? (
                    [(x[0].toString() + ' ' + x[1])]
                ) : []);
            }, [])
            .join(', ');
    }

    // weekParts :: Int -> [Int]
    function weekParts(intSeconds) {

        return [undefined, 7, 24, 60, 60]
            .reduceRight(function (a, x) {
                var intRest = a.remaining,
                    intMod = isNaN(x) ? intRest : intRest % x;

                return {
                    remaining:(intRest - intMod) / (x || 1),
                    parts: [intMod].concat(a.parts)
                };
            }, {
                remaining: intSeconds,
                parts: []
            })
            .parts
    }

    // GENERIC ZIP

    // zip :: [a] -> [b] -> [(a,b)]
    function zip(xs, ys) {
        return xs.length === ys.length ? (
            xs.map(function (x, i) {
                return [x, ys[i]];
            })
        ) : undefined;
    }

    // TEST

    return [7259, 86400, 6000000]
        .map(function (intSeconds) {
            return intSeconds.toString() +
                '    ->    ' + angloDuration(intSeconds);
        })
        .join('\n');

})();


def seconds_to_time_string:
  def nonzero(text): floor | if . > 0 then "\(.) \(text)" else empty end;
  if . == 0 then "0 sec"
  else
  [(./60/60/24/7    | nonzero("wk")),
   (./60/60/24 % 7  | nonzero("d")),
   (./60/60    % 24 | nonzero("hr")),
   (./60       % 60 | nonzero("min")),
   (.          % 60 | nonzero("sec"))]
  | join(", ")
  end;


# 1.x
function duration(sec::Integer)::String
    t = Array{Int}([])
    for dm in (60, 60, 24, 7)
        sec, m = divrem(sec, dm)
        pushfirst!(t, m)
    end
    pushfirst!(t, sec)
    return join(["$num$unit" for (num, unit) in zip(t, ["w", "d", "h", "m", "s"]) if num > 0], ", ")
end

@show duration(7259)
@show duration(86400)
@show duration(6000000)


fun compoundDuration(n: Int): String {
    if (n < 0) return "" // task doesn't ask for negative integers to be converted
    if (n == 0) return "0 sec"
    val weeks  : Int
    val days   : Int
    val hours  : Int
    val minutes: Int
    val seconds: Int
    var divisor: Int = 7 * 24 * 60 * 60
    var rem    : Int
    var result = ""

    weeks = n / divisor
    rem   = n % divisor
    divisor /= 7
    days  = rem / divisor
    rem  %= divisor
    divisor /= 24
    hours = rem / divisor
    rem  %= divisor
    divisor /= 60
    minutes = rem / divisor
    seconds = rem % divisor

    if (weeks > 0)   result += "$weeks wk, "
    if (days > 0)    result += "$days d, "
    if (hours > 0)   result += "$hours hr, "
    if (minutes > 0) result += "$minutes min, "
    if (seconds > 0)
        result += "$seconds sec"
    else
        result = result.substring(0, result.length - 2)
    return result
}

fun main(args: Array<String>) {
    val durations = intArrayOf(0, 7, 84, 7259, 86400, 6000000)
    durations.forEach { println("$it\t-> ${compoundDuration(it)}") }
}


[start]
input "Enter SECONDS: "; seconds
seconds=int(abs(seconds))
if seconds=0 then print "Program complete.": end
UnitsFound=0: LastFound$=""
years=int(seconds/31449600): seconds=seconds mod 31449600
if years then LastFound$="years"
weeks=int(seconds/604800): seconds=seconds mod 604800
if weeks then LastFound$="weeks"
days=int(seconds/86400): seconds=seconds mod 86400
if days then LastFound$="days"
hours=int(seconds/3600): seconds=seconds mod 3600
if hours then LastFound$="hours"
minutes=int(seconds/60): seconds=seconds mod 60
if minutes then LastFound$="minutes"
if seconds then LastFound$="seconds"
select case years
    case 0
    case 1: print years; " year";
    case else: print years; " years";
end select
select case weeks
    case 0
    case 1
        if years then
            if LastFound$="weeks" then print " and "; else print ", ";
        end if
        print weeks; " week";
    case else
        if years then
            if LastFound$="weeks" then print " and "; else print ", ";
        end if
        print weeks; " weeks";
end select
select case days
    case 0
    case 1
        if years or weeks then
            if LastFound$="days" then print " and "; else print ", ";
        end if
        print days; " day";
    case else
        if years or weeks then
            if LastFound$="days" then print " and "; else print ", ";
        end if
        print days; " days";
end select
select case hours
    case 0
    case 1
        if years or weeks or days then
            if LastFound$="hours" then print " and "; else print ", ";
        end if
        print hours; " hour";
    case else
        if years or weeks or days then
            if LastFound$="hours" then print " and "; else print ", ";
        end if
        print hours; " hours";
end select
select case minutes
    case 0
    case 1
        if years or weeks or days or hours then
            if LastFound$="minutes" then print " and "; else print ", ";
        end if
        print minutes; " minute";
    case else
        if years or weeks or days or hours then
            if LastFound$="minutes" then print " and "; else print ", ";
        end if
        print minutes; " minutes";
end select
select case seconds
    case 0
    case 1
        if years or weeks or days or hours or minutes then
            if LastFound$="seconds" then print " and "; else print ", ";
        end if
        print seconds; " second";
    case else
        if years or weeks or days or hours or minutes then
            if LastFound$="seconds" then print " and "; else print ", ";
        end if
        print seconds; " seconds";
end select
print
goto [start]


function duration (secs)
    local units, dur = {"wk", "d", "hr", "min"}, ""
    for i, v in ipairs({604800, 86400, 3600, 60}) do
        if secs >= v then
            dur = dur .. math.floor(secs / v) .. " " .. units[i] .. ", "
            secs = secs % v
        end
    end
    if secs == 0 then
        return dur:sub(1, -3)
    else
        return dur .. secs .. " sec"
    end
end

print(duration(7259))
print(duration(86400))
print(duration(6000000))


tim := proc (s) local weeks, days, hours, minutes, seconds;
 weeks := trunc((1/604800)*s);
 days := trunc((1/86400)*s)-7*weeks;
 hours := trunc((1/3600)*s)-24*days-168*weeks;
 minutes := trunc((1/60)*s)-60*hours-1440*days-10080*weeks;
 seconds := s-60*minutes-3600*hours-86400*days-604800*weeks;
printf("%s", cat(`if`(0 < weeks, cat(weeks, "wk, "), NULL), `if`(0 < days, cat(days, "d, "), NULL), `if`(0 < hours, cat(hours, "hr, "), NULL), `if`(0 < minutes, cat(minutes, "min, "), NULL), `if`(0 < seconds, cat(seconds, "sec"), NULL)))
end proc;


from strutils import addSep

const
  Units = [" wk", " d", " hr", " min", " sec"]
  Quantities = [7 * 24 * 60 * 60, 24 * 60 * 60, 60 * 60, 60, 1]

#---------------------------------------------------------------------------------------------------

proc `$$`*(sec: int): string =
  ## Convert a duration in seconds to a friendly string.

  doAssert(sec > 0)

  var duration = sec
  var idx = 0
  while duration != 0:
    let q = duration div Quantities[idx]
    if q != 0:
      duration = duration mod Quantities[idx]
      result.addSep(", ", 0)
      result.add($q & Units[idx])
    inc idx

#———————————————————————————————————————————————————————————————————————————————————————————————————

when isMainModule:
  for sec in [7259, 86400, 6000000]:
    echo sec, "s = ", $$sec


let divisors = [
  (max_int, "wk");  (* many wk = many wk *)
  (7, "d");         (* 7 d = 1 wk *)
  (24, "hr");       (* 24 hr = 1 d *)
  (60, "min");      (* 60 min = 1 hr *)
  (60, "sec")       (* 60 sec = 1 min *)
]

(* Convert a number of seconds into a list of values for weeks, days, hours,
 * minutes and seconds, by dividing the number of seconds 'secs' successively by
 * the values contained in the list 'divisors' (taking them in reverse order).
 * Ex:
 *     compute_duration 7259
 * returns
 *     [(0, "wk"); (0, "d"); (2, "hr") (0, "min"); (59, "sec")]
 *)
let compute_duration secs =
  let rec doloop remain res = function
    | [] -> res
    | (n, s) :: ds -> doloop (remain / n) ((remain mod n, s) :: res) ds
  in
  doloop secs [] (List.rev divisors)

(* Format nicely the list of values.
 * Ex:
 *     pretty_print [(0, "wk"); (0, "d"); (2, "hr") (0, "min"); (59, "sec")]
 * returns
 *     "2 hr, 59 sec"
 *
 * Intermediate steps:
 * 1. Keep only the pairs where duration is not 0
 *     [(2, "hr"); (59, "sec")]
 * 2. Format each pair as a string
 *     ["2 hr"; "59 sec"]
 * 3. Concatenate the strings separating them by a comma+space
 *     "2 hr, 59 sec"
 *)
let pretty_print dur =
  List.filter (fun (d, _) -> d <> 0) dur
  |> List.map (fun (d, l) -> Printf.sprintf "%d %s" d l)
  |> String.concat ", "

(* Transform a number of seconds into the corresponding compound duration
 * string.
 * Not sure what to do with 0... *)
let compound = function
  | n when n > 0 -> compute_duration n |> pretty_print
  | n when n = 0 -> string_of_int 0 ^ "..."
  | _ -> invalid_arg "Number of seconds must be positive"

(* Some testing... *)
let () =
  let test_cases = [
    (7259, "2 hr, 59 sec");
    (86400, "1 d");
    (6000000, "9 wk, 6 d, 10 hr, 40 min");
    (0, "0...");
    (3599, "59 min, 59 sec");
    (3600, "1 hr");
    (3601, "1 hr, 1 sec")
  ] in
  let testit (n, s) =
    let calc = compound n in
    Printf.printf "[%s] %d seconds -> %s; expected: %s\n"
                  (if calc = s then "PASS" else "FAIL")
                  n calc s
  in
  List.iter testit test_cases


\\ Convert seconds to compound duration
\\ 4/11/16 aev
secs2compdur(secs)={
my(us=[604800,86400,3600,60,1],ut=[" wk, "," d, "," hr, "," min, "," sec"],
   cd=[0,0,0,0,0],u,cdt="");
for(i=1,5, u=secs\us[i]; if(u==0, next, cd[i]=u; secs-=us[i]*cd[i]));
for(i=1,5, if(cd[i]==0, next, cdt=Str(cdt,cd[i],ut[i])));
if(ssubstr(cdt,#cdt-1,1)==",", cdt=ssubstr(cdt,1,#cdt-2));
return(cdt);
}

{\\ Required tests:
print(secs2compdur(7259));
print(secs2compdur(86400));
print(secs2compdur(6000000));
}


program convertSecondsToCompoundDuration(output);

const
	suffixUnitWeek    = 'wk';
	suffixUnitDay     = 'd';
	suffixUnitHour    = 'hr';  { Use `'h'` to be SI-compatible. }
	suffixUnitMinute  = 'min';
	suffixUnitSecond  = 'sec'; { NB: Only `'s'` is SI-approved. }

	suffixSeparator   = ' ';  { A non-breaking space would be appropriate. }
	quantitySeparator = ', ';

	{ Maximum expected length of `string` “12345 wk, 6 d, 7 hr, 8 min, 9 sec” }
	timeSpanPrintedMaximumLength = 4 * length(quantitySeparator) +
		20 + length(suffixUnitWeek) + 1 + length(suffixUnitDay) +
		2 + length(suffixUnitHour) + 2 + length(suffixUnitMinute) +
		2 + length(suffixUnitSecond) + 5 * length(suffixSeparator);

	{ Units of time expressed in seconds. }
	minute = 60;
	hour   = 60 * minute;
	day    = 24 * hour;
	week   =  7 * day;

type
	wholeNumber   = 0..maxInt;
	naturalNumber = 1..maxInt;

	canonicalTimeSpan = record
			weeks:   wholeNumber;
			days:    0..6;
			hours:   0..23;
			minutes: 0..59;
			seconds: 0..59;
		end;

	stringFitForTimeSpan = string(timeSpanPrintedMaximumLength);

{
	\brief turns a time span expressed in seconds into a `canonicalTimeSpan`
	\param duration the non-negative duration expressed in seconds
	\return a `canonicalTimeSpan` representing \param duration seconds
}
function getCanonicalTimeSpan(duration: wholeNumber): canonicalTimeSpan;
	{ Perform `div`ision and update `duration`. }
	function split(protected unit: naturalNumber): wholeNumber;
	begin
		split := duration div unit;
		duration := duration mod unit
	end;
var
	result: canonicalTimeSpan;
begin
	with result do
	begin
		weeks   := split(week);
		days    := split(day);
		hours   := split(hour);
		minutes := split(minute);
		seconds := duration
	end;
	{ In Pascal there needs to be _exactly_ one assignment to the }
	{ result variable bearing the same name as of the `function`. }
	getCanonicalTimeSpan := result
end;

{
	\brief turns a non-trivial duration into a string
	\param n a positive duration expressed in seconds
	\return \param n expressed in some human-readable form
}
function timeSpanString(protected n: naturalNumber): stringFitForTimeSpan;
const
	qs = quantitySeparator;
var
	result: stringFitForTimeSpan;
begin
	with getCanonicalTimeSpan(n) do
	begin
		{ `:1` specifies the minimum-width. Omitting it would cause }
		{ the compiler to insert a vendor-defined default, e. g. 20. }
		writeStr(result, weeks:1, suffixSeparator, suffixUnitWeek);
		{ For strings, `:n` specifies the _exact_ width (padded with spaces). }
		writeStr(result, result:ord(weeks > 0) * length(result));

		if days > 0 then
		begin
			writeStr(result, result, qs:ord(length(result) > 0) * length(qs),
				days:1, suffixSeparator, suffixUnitDay);
		end;
		if hours > 0 then
		begin
			writeStr(result, result, qs:ord(length(result) > 0) * length(qs),
				hours:1, suffixSeparator, suffixUnitHour);
		end;
		if minutes > 0 then
		begin
			writeStr(result, result, qs:ord(length(result) > 0) * length(qs),
				minutes:1, suffixSeparator, suffixUnitMinute);
		end;
		if seconds > 0 then
		begin
			writeStr(result, result, qs:ord(length(result) > 0) * length(qs),
				seconds:1, suffixSeparator, suffixUnitSecond);
		end
	end;
	timeSpanString := result
end;

{ === MAIN ============================================================= }
begin
	writeLn(   7259, ' seconds are “', timeSpanString(7259), '”');
	writeLn(  86400, ' seconds are “', timeSpanString(86400), '”');
	writeLn(6000000, ' seconds are “', timeSpanString(6000000), '”')
end.