############################################################################
#
#	File:     seqops.icn
#
#	Subject:  Procedures to manipulate T-sequences
#
#	Author:   Ralph E. Griswold
#
#	Date:     June 11, 2001
#
############################################################################
#
#  This file is in the public domain.
#
############################################################################
#
#  These procedures perform operations related to T-Sequences and to
#  analyze T-Sequences.
#
############################################################################
#
#  Requires:  Courage.
#
############################################################################
#
#   copyl(xargs[])	 copy list of lists
#   eval_tree(n)	 evaluate expression tree
#   expression_tree(n)	 create expression tree
#   fragment(s, i, p, arg)
#
#   get_analysis(s)	 analyze sequence
#   get_scollate(s)	 analyze for collation
#   get_splace(s)	 analyze for motif along a path
#   get_srepeat(s)	 analyze for repeat
#   get_srun(s)		 analyze for run
#   get_sruns(s)	 analyze for simple runs
#   is_scompact(x)	 test sequence for compactness
#   pimage(x)
#   remod(s, p)
#   sanalout()		 output analysis
#   sanalysis(x)	 over-all analysis
#   sbinop(op, xargs[])	 binary operation on terms
#   sbound(xargs[])	 compute sequence upper bound  FIX!
#   scollate(xargs[])	 sequence collation
#   scompress(xargs[])	 compact sequence
#   sconcat(xargs[])	 concatenate sequences
#   scpal(xargs[])	 closed sequence palindrome
#   sdecimate(xargs[])	 decimate sequence
#   sdecollate(order, x) decollate sequence
#   sdelta(x)		 get delta sequence
#   sdirection(x)	 "direction" of delta(x)
#   sequiv(x1, x2)	 test sequence equivalence
#   sextend(xargs[])	 extend sequence
#   sflatten(x)		 flatten nested sequence
#   sground(s, i)	 ground sequence to i
#   shaft_period(x1, x2) shaft period
#   simage(x, limit)	 string image of sequence
#   sinit()		 initialize sequence operations
#   slayer(xargs[])	 layer sequences
#   slength(x)		 compute sequence length
#   slocate(xargs[])	 sequences of first positions of terms
#   smap(xargs[])	 map terms in sequence
#   smin(xargs[])	 compute sequence lower bound	FIX
#   smissing(x)		 missing terms in sequence  BOGUS??
#   smod(xargs[])	 modular reduction
#   smutate(xargs[])	 mutation
#   snormal(x)		 normalize sequence
#   sopal(xargs[])	 create open sequence palindrome
#   sorder(x)		 positions of first occurrence
#   sparity(xargs[])	 adjust parity
#   speriod(s)		 sequence period
#   splace(xargs[])	 place motif along a path
#   splaceg(xargs[])	 generalized motifs along a path
#   spositions(x1, x2)	 shaft positions
#   spromote(x)		 promote term to sequence
#   srandom(x)		 random selection
#   sreflecth(xargs[])	 reflect sequence horizontally
#   sreflectr(xargs[])
#   sreflectv(xargs[])	 reflect sequence vertically
#   sremdupl(xargs[])	 remove duplicate adjacent terms
#   srepeat(xargs[])	 repeat sequence
#   srepl(xargs[])	 replicate sequence terms
#   srotatev(xargs[])	 rotate sequence vertically
#   srun(xargs[])	 create connected run
#   sruns(xargs[])	 create simple runs
#   sscale(xargs[])	 scale terms in sequence
#   sscollate(xargs[])	 collate entire sequences
#   sselect(xargs[])	 select terms from sequence
#   sshift(x, i)	 shift terms sequence
#   sunmod(x)		 modular expansion
#   sunop(op, xargs[])	 unary operation on terms
#   walk_tree(n, tree_list, tree_ptrs, depth)
#			 walk expression tree
#
############################################################################
#
#  Links:  factors, numbers
#
############################################################################

link factors
link numbers

global expressions
global node_gen
global saltparity
global scompact
global sfliph
global sflipv
global sflipr
global sflipl

record node(name, seqlist)

$define MaxTerms 300

procedure copyl(xargs[])		#: copy list of lists
   local new_xargs

   new_xargs := []

   every put(new_xargs, copy(spromote(!xargs)))

   return new_xargs

end

procedure eval_tree(n)
   local i

   n := integer(n)

   if type(n) ~== "node" then return n

   every i := 1 to *n.seqlist do
      n.seqlist[i] := eval_tree(n.seqlist[i])

   return n.name ! n.seqlist

end

procedure expression_tree(n)
   local result

   n := integer(n)

   case type(n) of {
      "list" | "integer"  :  return "[" || simage(n, MaxTerms) || "]"
      "string"            :  return n
      }

   result :=  n.name || "("

   every result ||:= expression_tree(!n.seqlist) || ","

   return result[1:-1] || ")"

end

procedure fragment(s, i, p, arg)
   local results, j, k

   if *s <= i then return s

   /p := 1

   results := list(i)

   every !results := []

   k := 0

   every j := 1 to i do
      every 1 to  *s / i do
         put(results[j], s[k +:= 1]) | break break

   every j := 1 to i do
      results[j] := p(results[j], arg)

   every j := 1 to i do
      results[j] := fragment(results[j], i, p, arg)

   return results

end

$define MinLength 5	# minimum length for attempting analysis

procedure get_analysis(seq)
   local expression

   if *seq < MinLength then return simageb(seq)

   expression := (
      get_scollate(seq) |
      get_srepeat(seq) |
      remod(seq, get_srun) |	# before sruns(), which would subsume it
      remod(seq, get_sruns) |
      get_splace(seq) |		# would subsume some runs
      simageb(seq)
      )

   return expression

end

procedure get_scollate(seq)		#: find collation in sequence
   local bound, deltas, i, j, poses, positions, oper, seqs
   local results, result, k, count, oseq, m, nonperiod, facts, period

   bound := (sbound ! seq)

   speriod(seq) | fail		# only handle periodic case

   deltas := table()
   positions := table()

   every  i := 1 to bound do {
      poses := spositions(seq, i)
      positions[i] := poses
      j := sconstant(sdelta(poses)) | fail	# CONTRADICTION
      /deltas[j] := []
      put(deltas[j], i) 
      }

   oseq := list(*seq, 1)		# decollation order sequence

   count := 0

   every k := key(deltas) do {
      count +:= 1
      every j := !deltas[k] do
         every m := !positions[j] do
            oseq[m] := count
      }

   if *set(oseq) < 2 then fail		# not enough sequences

#  oseq := srun([1, get(facts)]) | fail

   seqs := sdecollate(oseq, seq) | fail

   oper := "scollate(" || (simageb(oseq[1+:speriod(oseq)]) |
      get_analysis(oseq))

   every oper ||:= ", " || get_analysis(!seqs)

   return oper || ")"

end

procedure get_splace(seq)		#: find motif along a path in sequence
   local i, j, motif, seq2, path

   if i := sconstant(seq) then return "srepeat(" || i || "," || *seq || ")"

   every i := divisors(*seq) do {
      motif := seq[1+:i]
      every j := i + 1 to *seq by i do
         if not sequiv(motif, sground(seq[j+:i], seq[1])) then break next
      path := []
      every put(path, seq[1 to *seq by i])
      return "splace(" || get_analysis(motif) || ", " || get_analysis(path) || ")"
      }

   fail

end

procedure get_srepeat(seq)		#: find repeat in sequence
   local i

   i := speriod(seq) | fail
   return "srepeat(" || get_analysis(seq[1+:i]) || ", " || (*seq / i) || ")"

end

procedure get_srun(seq)
   local i, j, new_seq, dir

   seq := copy(seq)

   i := get(seq)
   j := get(seq)

   if j = i - 1 then dir := -1		# down going
   else if j = i + 1 then dir := 1			# upgoing
   else fail

   new_seq := [i]

   while i := get(seq) do {
      if i = j + 1 then {
         if dir = -1 then put(new_seq, j)
         dir := 1
         }
      else if i =  j - 1 then {
         if dir = 1 then put(new_seq, j)
         dir := -1
         }
      else {
         put(new_seq, j)
         push(seq, i)		# put back non-continuing value
         break
         }
      j := i
      }

   if *seq ~= 0 then fail

   put(new_seq, j)

   return "srun(" || get_analysis(new_seq) || ")"

end

procedure get_sruns(seq)
   local i, j, seq1, seq2, dir

   seq1 := []
   seq2 := []

   repeat {
      i := get(seq) | {
         put(seq2, j)
         break					# end of road
         }
      j := get(seq) | fail			# isolated end point
      if j = i - 1 then dir := -1		# down going
      else if j = i + 1 then dir := 1		# up going
      else fail
      put(seq1, i)				# beginning point
      while i := get(seq) do {
         if i = j + dir then {
            j := i
            next
            }
         else {
            push(seq, i)			# put back next value
            put(seq2, j)
            break
            }
         }
      }

   return "sruns(" || get_analysis(seq1) || ", " || get_analysis(seq2) || ")"

end

procedure is_scompact(x)		#: test sequence for compactness
   local bound

   x := spromote(x)

   bound := sbound ! x

   if bound = *set(x) then return bound
   else fail

end

procedure pimage(s)			# DOES THIS BELONG HERE?
   local result, x

   result := ""

   every x := !s do {
      if integer(x) then result ||:= x else
         result ||:= pimage(x)
      result ||:= ","
      }

   return "[" || result[1:-1] || "]"

end

procedure remod(seq, p)			#: handle modulus
   local nseq, bound

   nseq := sunmod(seq)

   if (sbound ! nseq) > (bound := sbound ! seq) then
      return "smod(" || p(nseq) || ", " || bound || ")"
   else return p(copy(seq))

end

procedure sanalout()
   local expression, var

   write("link seqops")
   write("procedure main()")

   expressions := sort(expressions, 4)

   while expression := get(expressions) do
      write(var := get(expressions), " := ", expression)

   write("every write(!", var, ")")

   write("end")

   expressions := table()

   return

end

procedure sanalysis(x)

#  sanalyze(x)

   sanalout()

   return

end

procedure sbinop(op, xargs[])		#: binary operation on terms
   local lseq, i, x1, x2

   x1 := spromote(xargs[1])
   x2 := spromote(xargs[2])

   op := proc(op, 2) | fail

   lseq := []

   every i := 1 to smin(*x1, *x2) do
      put(lseq, op(x1[i], x2[i]))

   return lseq

end

procedure sbound(xargs[])		#: compute sequence upper bound  FIX!

   return sort(xargs)[-1]

end

procedure scollate(xargs[])		#: sequence term collation
   local lseq, i, order

   if \node_gen then return node("scollate", xargs)

   order := get(xargs)

   /order := srun(1, *xargs)

   xargs := copyl ! xargs

   lseq := []

   while i := get(order) do {
      put(order, i)
      put(lseq, get(xargs[i])) | break
      }

   put(lseq, get(xargs[get(order)]))		# ?????

   return lseq

end

procedure scompress(xargs[])		#: compact sequence
   local unique, target, x

   if \node_gen then return node("compress", xargs)

   x := spromote(xargs[1])

   unique := set(x)

   target := []

   every put(target, 1 to *unique)

   return smap(x, sort(unique), target)

end

procedure sconcat(xargs[])		#: concatenate sequences
   local lseq

   if \node_gen then return node("sconcat", xargs)

   lseq := []

   every lseq |||:= spromote(!xargs)

   return lseq

end

procedure sconstant(seq)		#: test for constant sequence

   if *set(seq) = 1 then return !seq
   else fail

end

procedure scpal(xargs[])		#: closed sequence palindrome
   local lseq, x1, x2, i

   if \node_gen then return node("scpal", xargs)

   x1 := spromote(xargs[1])
   x2 := spromote(xargs[2]) | [1]

   i := 0

   every i +:= !x2

   lseq := srepeat(sopal(x1), i)

   put(lseq, lseq[1])

   return lseq

end

procedure sdecimate(xargs[])		#: decimate sequence
   local lseq, j, k, x1, x2

   x1 := spromote(xargs[1])
   x2 := sort(spromote(xargs[2]))

   lseq := []

   k := 1

   while j := get(x2) do {
      every put(lseq, x1[k to j - 1])
      k := j + 1
      }

   every put(lseq, x1[j + 1 to *x1])

   return lseq

end


procedure sdecollate(order, x)		#: sequence decollation
   local lseq, i, j

   x := spromote(x)

   if *x = 0 then fail

   order := copy(order)

   lseq := list(sbound ! order)		# list of lists to return

   every !lseq := []			# initially empty

   every j := !x do {
      i := get(order)	| fail
      put(order, i)
      put(lseq[i], j)
      }

   return lseq

end

procedure sdelta(seq)			#: sequence delta
   local i, lseq, j

   if *seq < 2 then fail

   seq := copy(seq)

   i := get(seq)

   lseq := []

   while j := get(seq) do {
      put(lseq, j - i)
      i := j
      }

   return lseq

end

procedure sdirection(x)			#: sequence delta "direction"
   local lseq, i

   x := sdelta(spromote(x)) | fail

   lseq := []

   while i := get(x) do
      put(lseq,
         if i > 0 then 3
         else if i = 0 then 2
         else 1
         )

   return lseq

end

procedure sdistrib(x)
   local lseq, i

   x := copy(spromote(x))

   lseq := list(sbound ! x, 0)

   while i := get(x) do
      lseq[i] +:= 1

   return lseq

end

procedure sequiv(x1, x2)		# test for sequence equivalence
   local i

   x1 := spromote(x1)
   x2 := spromote(x2)

   if *x1 ~= *x2 then fail

   every i := 1 to *x1 do
      if x1[i] ~= x2[i] then fail

   return x2

end

procedure sextend(xargs[])		#: extend sequence
   local lseq, part, i, x1, x2

   if \node_gen then return node("sextend", xargs)

   x1 := spromote(xargs[1])

   lseq := []

   every i := !spromote(xargs[2]) do {
      part := []
      until *part >= i do
         part |||:= x1
      lseq |||:= part[1+:i]
      }

   return lseq

end

procedure sflatten(s)		# flatten packet sequence  BELONGS HERE?
   local lseq, x

   lseq := []

   every x := !s do
      if type(x) == "list" then lseq |||:= sflatten(x)
      else put(lseq, x)

   return lseq

end

procedure sground(seq, i)		#: ground sequence to i
   local j

   j := smin ! seq

   every !seq -:= (j - i)

   return seq

end

procedure shaft_period(x1, x2)		#: shaft period
   local results

   x1 := spromote(x1)
   x2 := spromote(x2)

   return sconstant(sdelta(spositions(x1, x2)))

end

procedure simage(x, limit)		#: string image of sequence
   local str

   x := spromote(x)

   if *x = 0 then return "[]"

   /limit := 2 ^ 16			# good enough

   str:= ""

   every str ||:= (!x \ limit) || ", "

   if *x > limit then str ||:= "...  "

   return str[1:-2]

end

procedure simageb(seq)			#: bracketed sequence image

   if *seq = 1 then return seq[1]

   return "sconcat(" || simage(seq) || ")"

end

procedure sinit()			#: initialize sequence operations

   saltparity := sparity
   scompact := scompress
   sfliph := sreflecth
   sflipv := sreflectv
   sflipr := sreflectr
#  sflipl := sreflectl

   return

end

procedure slayer(xargs[])		#: layer sequences
   local new_xargs, i, shift

   if \node_gen then return node("slayer", xargs)

   new_xargs := [xargs[1], xargs[2]] | fail

   if not integer(xargs[2][1]) then return scollate ! xargs

   shift := sbound ! xargs[2]

   every i := 3 to *xargs do {
      put(new_xargs, sshift(xargs[i], shift))
      shift +:= sbound ! xargs[i]
      }

   return scollate ! new_xargs

end

procedure slength(x)			#: compute sequence length

   return *spromote(x)

end

procedure slocate(xargs[])		#: sequences of first positions of terms
   local count, i, lseq, x1, x2

   if \node_gen then return node("slocate", xargs)

   x1 := copy(spromote(xargs[1]))
   x2 := set(spromote(xargs[2]))

   lseq := []

   count := 0

   while i := get(x1) do {
      count +:= 1
      if member(x2, integer(i)) then
         return count
      }

   fail

end

procedure smap(xargs[])			#: map terms in sequence
   local i, smaptbl, x1, x2, x3
   static tdefault

   initial tdefault := []

   x1 := copy(spromote(xargs[1]))
   x2 := spromote(xargs[2])
   x3 := spromote(xargs[3])

   if *x2 ~= *x3 then fail

   smaptbl := table(tdefault)		# mapping table

   every i := 1 to *x2 do		# build the map
      smaptbl[x2[i]] := x3[i]

   every i := 1 to *x1 do		# map the values
      x1[i] := (tdefault ~=== smaptbl[x1[i]])

   return x1

end

procedure smin(xargs[])			#: compute sequence lower bound	FIX

   return sort(xargs)[1]

end

procedure smissing(x)			#: missing terms in sequence  BOGUS??
   local lseq, i, result

   x := spromote(x)

   lseq := sorder(x)

   result := []

   every i := 1 to *lseq do
      if lseq[i] = 0 then put(result, i)

   return result

end

procedure smod(xargs[])			#: modular reduction
   local lseq, i, x1, x2

   if \node_gen then return node("smod", xargs)

   x1 := spromote(xargs[1])
   x2 := spromote(xargs[2])

   lseq := []

   every i := !x2 do
      every put(lseq, residue(!x1, i, 1))

   return lseq

end

procedure smutate(xargs[])		#: mutation
   local lseq, x1, x2

   if \node_gen then return node("smutate", xargs)

   x1 := spromote(xargs[1])
   x2 := spromote(xargs[2])

   lseq := []

   every put(lseq, x1[!x2])

   return lseq

end

procedure snormal(x)			#: normalize sequence
   local lseq, i, target, count	# maps shafts so they are numbered in order
				# first appearance
   x := spromote(x)

   lseq := []

   count := 0

   target := table()

   every i := !x do {
      /target[i] := (count +:= 1)
      put(lseq, target[i])
      }

   return lseq

end

procedure sopal(xargs[])		#: create open sequence palindrome
   local x

   if \node_gen then return node("sopal", xargs)

   x := spromote(xargs[1])

   return x ||| sreflecth(x)[2:-1]

end

procedure sorder(x)			#: positions of first occurrence
   local lseq, i, done		#  of terms in *compact* sequence

   x := copy(spromote(x))

   lseq := []

   done := set()

   while i := integer(get(x)) do {
      if member(done, i) then next
      else {
         put(lseq, i)
         insert(done, i)
         }
      }

   return lseq

end

procedure sparity(xargs[])		#: adjust parity
   local lseq, i, j, k, x1, x2

   if \node_gen then return node("sparity", xargs)

   x1 := spromote(xargs[1])
   x2 := spromote(xargs[2])

   lseq := []

   every i := 1 to *x1 do {
      j := x1[i]
      k := x2[i]
      if (j % 2) = (k % 2) then put(lseq, j)
      else put(lseq, j + 1, j)
      }

   return lseq

end

procedure speriod(seq)			#: period of sequence
   local i, segment

   every i := divisors(*seq) do {
      segment := seq[1+:i]
      if sequiv(sextend(segment, *seq), seq) then return i
      }

   fail

end

procedure splace(xargs[])		#: place motif along a path
   local lseq, i, x1, x2

   if \node_gen then return node("splace", xargs)

   x1 := copy(spromote(xargs[1]))
   x2:= spromote(xargs[2])

   lseq := []

   every i := !x2 do
      every put(lseq, !x1 + i - 1)

   return lseq

end

procedure splaceg(xargs[])		#: generalized motifs along a path
   local lseq, i, path, motif

   if \node_gen then return node("splaceg", xargs)

   path := copy(get(xargs))

   xargs := copyl ! xargs

   lseq := []

   while i := get(path) do {
      motif := get(xargs)
      put(xargs, motif)
      every put(lseq, !motif + i - 1)
      }

   return lseq

end

procedure spositions(x1, x2)		#: positions of values in sequence
   local lseq, count, i

   x1 := copy(spromote(x1))
   x2 := set(spromote(x2))

   lseq := []

   count := 0

   while i := get(x1) do {
      count +:= 1
      if member(x2, integer(i)) then
         put(lseq, count)
      }

   return lseq

end

procedure spromote(x)			#: promote term to sequence

   if type(x) ~== "list" then x := [x]

   return x

end

procedure srandom(x)			#: random selection

   return ?spromote(x)

end

procedure sreflecth(xargs[])		#: reflect sequence horizontally
   local lseq, x

   if \node_gen then return node("sreflecth", xargs)

   lseq := []

   every push(lseq, !spromote(xargs[1]))

   return lseq

end


procedure sreflectr(xargs[])
   local lseq, i, bound, x

   if \node_gen then return node("sreflectr", xargs)

   x := spromote(xargs[1])

   bound := sbound ! x

   lseq := []

   every i := !x do
      push(lseq, bound - i + 1)

   return lseq

end

procedure sreflectv(xargs[])		#: reflect sequence vertically
   local lseq, m, x

   if \node_gen then return node("sreflectv", xargs)

   x := spromote(xargs[1])

   if not integer(x[1]) then return x

   m := sbound ! x

   lseq := []

   every put(lseq, m - !x + 1)

   return lseq

end

procedure sremdupl(xargs[])		#: remove duplicate adjacent terms
   local lseq, i, x

   if \node_gen then return node("sremdupl", xargs)

   x := copy(spromote(xargs[1]))

   lseq := [get(x)] | return []

   while i := get(x) do
      if lseq[-1] ~= i then
         put(lseq, i)

   return lseq

end

procedure srepeat(xargs[])		#: repeat sequence
   local lseq, count, x1, x2

   if \node_gen then return node("srepeat", xargs)

   x1 := spromote(xargs[1])

   count := 0

   every count +:= !spromote(xargs[2])

   lseq := copy(x1)

   every 2 to count do
         lseq |||:= x1

   return lseq

end

procedure srepl(xargs[])		# replicate sequence terms
   local lseq, i, j, x1, x2

   if \node_gen then return node("srepl", xargs)

   x1 := spromote(xargs[1])
   x2 := spromote(xargs[2])

   lseq := []

   every i := !x2 do
      every j := !x1 do
         every 1 to i do
            put(lseq, j)

   return lseq

end

procedure srotatev(xargs[])		#: rotate sequence vertically
   local lseq, m, x

   if \node_gen then return node("srotatev", xargs)

   x := spromote(xargs[1])

   if not integer(x[1]) then return x

   m := sbound ! x

   lseq := []

   every put(lseq, residue(!x + 1, m, 1))

   return lseq

end

procedure srun(xargs[])			#: create connected runs
   local lseq, i, j, x

   if \node_gen then return node("srun", xargs)

   x := copy(spromote(xargs[1]))

   lseq := []

   i := get(x) | return lseq

   while j := get(x) do {
      lseq |||:= sruns(i, j, 1)
      pull(lseq)
      i := j
      }

   put(lseq, i)

   return lseq

end     

procedure sruns(xargs[])		# disconnected runs
   local lseq, i, j, k, limit, x1, x2, x3

   if \node_gen then return node("sruns", xargs)

   x1 := copy(spromote(xargs[1]))
   x2 := copy(spromote(xargs[2]))
   x3 := copy(spromote(xargs[3]) | [1])

   lseq := []

   repeat {
      i := get(x1) | break
      j := get(x2) | break
      k := get(x3) | break
      put(x3, k)			# cycle
      if integer(j) < integer(i) then k := -k
      every put(lseq, i to j by k)
      }

   return lseq

end
 
procedure sscale(xargs[])		#: scale terms in sequence
   local lseq, j, i, x1, x2

   if \node_gen then return node("sscale", xargs)

   x1 := spromote(xargs[1])

   lseq := []

   every i := !spromote(xargs[2]) do
      every j := 1 to *x1 do
          put(lseq, (x1[j] - 1) * i + 1)

   return lseq	

end

procedure sscollate(xargs[])		#: entire sequence collation
   local lseq, i, order

   if \node_gen then return node("sscollate", xargs)

   order := get(xargs)

   /order := srun(1, *xargs)

   xargs := copyl ! xargs

   lseq := []

   while i := get(order) do
      lseq |||:= xargs[i]

   return lseq

end

procedure sselect(xargs[])		#: select terms from sequence
   local lseq, i, x1, x2

   if \node_gen then return node("sselect", xargs)

   x1 := spromote(xargs[1])
   x2 := copy(spromote(xargs[2]))

   lseq := []

   while i := get(x2) do
      put(lseq, x1[i])		# may fail

   return lseq

end

procedure sshift(x, i)			#: shift terms sequence
   local lseq

   lseq := []

   every put(lseq, !spromote(x) + i)

   return lseq

end

procedure sunmod(x)			#: modular expansion
   local base, bound, i, lseq, k

   x := copy(spromote(x))

   if not integer(x[1]) then return x

   base := 0

   bound := sbound ! x

   lseq := [get(x)] | fail

   while i := get(x) do {
      if (i = 1) & (lseq[-1] = base + bound) then
         base +:= bound
      else if (i = bound) & (lseq[-1] = base + 1) then
         base -:= bound
      put(lseq, base + i)
      }

   k := (smin ! lseq)

   if k > 0 then return lseq

   k := bound * (-k / bound + 1)

   every !lseq +:= k

   return lseq

end
procedure sunop(op, xargs[])		#: unary operation on terms
   local lseq, i, x

   if \node_gen then return node("sunop", xargs)

   x := spromote(xargs[1])

   op := proc(op, 1) | fail

   lseq := []

   every i := 1 to *x do
      put(lseq, op(x[i]))

   return lseq

end

procedure walk_tree(n, tree_list, tree_ptrs, depth)
   local indent

   /tree_list := []
   /tree_ptrs := []
   /depth := 0

   indent := repl(" ", 3 * depth)

   n := integer(n)

   case type(n) of {
      "integer" | "list"  :  {
         put(tree_list, indent || "[" || simage(n, MaxTerms) || "]")
         put(tree_ptrs, n)
         return [tree_list, tree_ptrs]
         }
      "string"            :  {
         put(tree_list, indent || n)
         put(tree_ptrs, n)
         return [tree_list, tree_ptrs]
         }
      }

   put(tree_list, indent || n.name)
   put(tree_ptrs, n)

   every walk_tree(!n.seqlist, tree_list, tree_ptrs, depth + 1)

   return [tree_list, tree_ptrs]

end
