############################################################################
#
#	File:     periodic.icn
#
#	Subject:  Procedures related to periodic sequences
#
#	Author:   Ralph E. Griswold
#
#	Date:     June 10, 2001
#
############################################################################
#
#  This file is in the public domain.
#
############################################################################
#
#  Sqrt(i, j) produces a rational approximation to the square root of i
#  with j iterations of the half-way method.  j defaults to 5.
#
############################################################################
#
#  Requires:  Large-integer arithmetic  
#
############################################################################
#
#  Links:  lists, numbers, rational, strings
#
############################################################################

link lists
link numbers
link rational
link strings

record perseq(pre, rep)

procedure Sqrt(i, j)		#: rational approximate to square root
   local rat, half

   /j := 5

   half := rational(1, 2, 1)

   rat := rational(integer(sqrt(i)), 1, 1)	# initial approximation

   i := rational(i, 1, 1)

   every 1 to j do
      rat := mpyrat(half, addrat(rat, divrat(i, rat, 1), 1))

   return rat

end

procedure rat2cf(rat)		#: continued fraction sequence for rational
   local r, result, i, j

   i := rat.numer
   j := rat.denom

   result := []

   repeat {
     put(result, rational(integer(i / j), 1, 1).numer)
     r := i % j
     i := j
     j := r
     if j = 0 then break
     }

   return perseq(result, [])

end

procedure cfapprox(lst)		#: continued-fraction approximation
   local prev_n, prev_m, n, m, t

   lst := copy(lst)

   prev_n := [1]
   prev_m := [0, 1]

   put(prev_n, get(lst).denom) | fail

   while t := get(lst) do {
      n := t.denom * get(prev_n) + t.numer * prev_n[1]
      m := t.denom * get(prev_m) + t.numer * prev_m[1]
      suspend rational(n, m, 1)
      put(prev_n, n)
      put(prev_m, m)
      if t.denom ~= 0 then {		# renormalize
         every !prev_n /:= t.denom
         every !prev_m /:= t.denom
         }
      }

end

procedure dec2rat(pre, rep)	#: convert repeating decimal to rational
   local s

   s := ""

   every s ||:= (!pre | |!rep) \ (*pre + *rep)

   return ratred(rational(s - left(s, *pre),
      10 ^ (*pre + *rep) - 10 ^ *pre, 1))

end

procedure rat2dec(rat)		#: decimal expansion of rational
   local  result, remainders, count, seq

   rat := copy(rat)

   result := ""

   remainders := table()

   rat.numer %:= rat.denom
   rat.numer *:= 10

   count := 0

   while rat.numer > 0 do {
      count +:= 1
      if member(remainders, rat.numer) then {	# been here; done that
         seq := perseq()
         result ? {
            seq.pre := move(remainders[rat.numer] - 1)
            seq.rep := tab(0)
            }
         return seq
         }
      else insert(remainders, rat.numer, count)
      result ||:= rat.numer / rat.denom
      rat.numer %:= rat.denom
      rat.numer *:= 10
      }

   return perseq([rat.denom], [])		# WRONG!!!

end

procedure repeater(seq, ratio, limit)		#: find repeat in sequence
   local init, i, prefix, results, segment, span

   /ratio := 2
   /limit := 0.75

   results := copy(seq)

   prefix := []

   repeat {
      span := *results / ratio
      every i := 1 to span do {
         segment := results[1+:i] | next
         if lequiv(lextend(segment, *results), results) then
            return perseq(prefix, segment)
         }
      put(prefix, get(results)) |		# first term to prefix
         return perseq(prefix, results)
      if *prefix > limit * *seq then return perseq(seq, [])
      }

end

procedure seqimage(seq)		#: sequence image
   local result

   result := ""

   every result ||:= !seq.pre || ","

   result ||:= "["

   if *seq.rep > 0 then {
      every result ||:= !seq.rep || ","
      result[-1] := "]"
      }
   else result ||:= "]"

   return result

end
