############################################################################
#
#	File:     weaving.icn
#
#	Subject:  Procedures to implement weaving expressions
#
#	Author:   Ralph E. Griswold
#
#	Date:     October 22, 1999
#
############################################################################
#
#  This file is in the public domain.
#
############################################################################
#
#  These procedures implement the weaving expressions supported by Painter
#  and described in the PDF document "Advanced Weaving" that accompanies
#  that application.
#
############################################################################
#
#  Links:  strings
#
############################################################################

$define Domain		"12345678"
$define DomainForward	"1234567812345678"
$define DomainBackward  "8765432187654321"

procedure Between(p1, p2)

   DomainForward ? {
      tab(upto(p1[-1]) + 1)
      return tab(upto(p2[1]))
      }

end

procedure Block(p1, p2)			#: weaving block
   local i, s, p3, counts

   if *p1 < *p2 then p1 := Extend(p1, *p2) | fail
   else if *p2 <  *p1 then p2 := Extend(p2, *p1) | fail

   counts := []

   p2 ? {
      while s := tab(upto('{')) do {
         every put(counts, !s)
         move(1)
         put(counts, tab(upto('}')))
         move(1)
         }
      every put(counts, !tab(0))
      }

   p3 := ""

   every i := 1 to *p1 do
      p3 ||:= repl(p1[i], counts[i]) 

   return p3

end

procedure DownRun(c1, c2)		#: weaving downrun

   DomainBackward ? {
       tab(upto(c1))
       return tab(upto(c2) + 1)
       }

end

#  CYCLES WRONG

procedure DownUp(p1, p2, cycles)	#: weaving downup
   local i, p3

   /cycles := 0

   if *p1 < *p2 then p1 := Extend(p1, *p2) | fail
   else if *p2 <  *p1 then p2 := Extend(p2, *p1) | fail

   p3 := p1[1]

   if cycles > 0 then {
      DomainForward ? {
         tab(upto(p1[-1]) + 1)
         p3 ||:= repl(move(8), cycles)
         }
      }

   every i := 1 to *p1 do {
      p3 ||:= DownRun(p1[i], p2[i])[2:0]
      p3 ||:= UpRun(p2[i], p1[i + 1])[2:0]		# might fail
      }

   return p3

end

procedure Downto(p1, p2, cycles)	#: weaving downto
   local p3

   p3 := p1

   /cycles := 0

   if cycles > 0 then {
      DomainBackward ? {
         tab(upto(p1[-1]) + 1)
         p3 ||:= repl(move(8), cycles)
         }
      }

   DomainBackward ? {
      tab(upto(p1[-1]) + 1)
      return p3 || tab(upto(p2[1])) || p2
      }

end

procedure Extend(p, i)			#: weaving extension

   if *p = 0 then fail

   i := integer(i)

   return case i of {
      *p > i   :  left(p, i)
      *p < i   :  left(repl(p, (i / *p) + 1), i)
      default  :  p
      }

end

procedure Interleave(p1, p2)		#: weaving interleave
   local i, p3

   if *p1 < *p2 then p1 := Extend(p1, *p2) | fail
   else if *p2 <  *p1 then p2 := Extend(p2, *p1) | fail

   p3 := ""

   every i := 1 to *p1 do
      p3 ||:= p1[i] || p2[i]

   return p3

end

procedure Palindrome(p)			#: weaving palindrome

   if *p = 1 then return p
   else return p || reverse(p[2:-1])

end

procedure Pbox(p1, p2)			#: weaving pbox
   local p3, i

   if *p2 ~= *p1 then p2 := Extend(p2, *p1) | fail

   p3 := ""
   
   every i := !p1 do
      p3 ||:= p1[p2[i]]

   return p3

end

procedure Permute(p1, p2)		#: weaving permutation
   local p3, chunk, i, j

   j := *p1 % *p2
   if j ~= 0 then p1 := Extend(p1, *p1 + *p2 - j) | fail

   p3 := ""

   p1 ? {
      while chunk := move(*p2) do
         every i := !p2 do
            p3 ||:= chunk[i]
      }

   return p3

end

procedure Run(p, count)

   DomainForward ? {
      tab(upto(p[-1]) + 1)
      return repl(move(*Domain), count)
      }

end

procedure Template(p1, p2)		#: weaving Template
   local p3, dlist, i, j, k

   dlist := []

   every i := 1 to *p1 do
      put(dlist, p1[i] - p1[1])

   p3 := ""

   every j := 1 to *dlist do
      every i := 1 to *p2 do {
         k := p2[i] + dlist[j]
         if k > 8 then k -:= 8
         p3 ||:= k
         }

   return p3

end

#  CYCLES WRONG

procedure UpDown(p1, p2, cycles)	#: weaving updown
   local p3, i

   /cycles := 0

   if *p1 < *p2 then p1 := Extend(p1, *p2) | fail
   else if *p2 <  *p1 then p2 := Extend(p2, *p1) | fail

   p3 := p1[1]

   if cycles > 0 then {
      DomainForward ? {
         tab(upto(p1[-1]) + 1)
         p3 ||:= repl(move(8), cycles)
         }
      }

   every i := 1 to *p1 do {
      p3 ||:= UpRun(p1[i], p2[i])[2:0]
      p3 ||:= DownRun(p2[i], p1[i + 1])[2:0]		# might fail
      }

   return p3

end

procedure UpRun(c1, c2)			#: weaving uprun

   DomainForward ? {
       tab(upto(c1))
       return tab(upto(c2) + 1)
       }

end

procedure Upto(p1, p2, cycles)		#: weaving upto
   local p3

   /cycles := 0

   p3 := p1

   return p1 || Run(p1, cycles) || Between(p1, p2) || p2

end
