\ align columns

: split ( addr len char -- addr len1 addr len-len1 )
  >r 2dup r> scan 2swap 2 pick - ;

variable column

: for-each-line ( file len xt -- )
  >r begin #lf split [email protected] execute 1 /string dup 0<= until 2drop rdrop ;

: for-each-field ( line len xt -- )
  0 column !
  >r begin '$ split [email protected] execute 1 column +! 1 /string dup 0<= until 2drop rdrop ;

0 value num-columns

: count-columns ( line len -- )
  ['] 2drop for-each-field
  num-columns column @ max to num-columns ;
: find-num-columns ( file len -- )
  0 to num-columns
  ['] count-columns for-each-line ;

0 value column-widths

: column-width ( field len -- )
  column-widths column @ + [email protected]
  max
  column-widths column @ + c!
  drop ;
: measure-widths ( line len -- )
  ['] column-width for-each-field ;
: find-column-widths ( file len -- )
  num-columns allocate throw to column-widths
  column-widths num-columns erase
  ['] measure-widths for-each-line ;

\ type aligned, same naming convention as standard numeric U.R, .R
: type.l ( addr len width -- )
  over -               >r type r>       spaces ;
: type.c ( addr len width -- )
  over - dup 2/ spaces >r type r> 1+ 2/ spaces ;
: type.r ( addr len width -- )
  over -        spaces    type ;

defer type.aligned

: print-field ( field len -- )
  column-widths column @ + [email protected] type.aligned space ;
: print-line   ( line len -- ) cr ['] print-field for-each-field ;
: print-fields ( file len -- )    ['] print-line  for-each-line ;

\ read file
s" columns.txt" slurp-file  ( file len )

\  scan once to determine num-columns
2dup find-num-columns

\  scan again to determine column-widths
2dup find-column-widths

\  print columns, once for each alignment type
' type.l is type.aligned  2dup print-fields cr
' type.c is type.aligned  2dup print-fields cr
' type.r is type.aligned  2dup print-fields cr

\ cleanup
nip free throw
column-widths free throw


      SUBROUTINE RAKE(IN,M,X,WAY)	!Casts forth text in fixed-width columns.
Collates column widths so that each column is wide enough for its widest member.
       INTEGER IN		!Fingers the input file.
       INTEGER M		!Maximum record length thereof.
       CHARACTER*1 X		!The delimiter, possibly a comma.
       INTEGER WAY		!Alignment style.
       INTEGER W(M + 1)		!If every character were X in the maximum-length record,
       INTEGER C(0:M + 1)	!Then M + 1 would be the maximum number of fields possible.
       CHARACTER*(M) ACARD	!A scratchpad big enough for the biggest.
       CHARACTER*(28 + 4*M) FORMAT	!Guess. Allow for "Ann," per field.
       INTEGER I		!A stepper.
       INTEGER L,LF		!Text fingers.
       INTEGER NF,MF		!Field counts.
       CHARACTER*6 WAYNESS(-1:+1)	!Some annotation may be helpful.
       PARAMETER (WAYNESS = (/"Left","Centre","Right"/))	!Using normal language.
       INTEGER LINPR	!The mouthpiece.
       COMMON LINPR	!Used all over.
        W = 0		!Maximum field widths so far seen.
        MF = 0		!Maximum number of fields to a record.
        C(0) = 0	!Syncopation for the first field's predecessor.
        WRITE (LINPR,*)	!Some separation.
        WRITE (LINPR,*) "Align ",WAYNESS(MIN(MAX(WAY,-1),+1))	!Explain, cautiously.

Chase through the file assessing the lengths of each field.
   10   READ (IN,11,END = 20) L,ACARD(1:L)	!Grab a record.
   11   FORMAT (Q,A)				!Working only up to its end.
        CALL LIZZIEBORDEN	!Find the chop points.
        W(1:NF) = MAX(W(1:NF),C(1:NF) - C(0:NF - 1) - 1)	!Thereby the lengths between.
        MF = MAX(MF,NF)		!Also want to know the most number of chops.
        GO TO 10		!Get the next record.

Concoct a FORMAT based on the maximum size of each field. Plus one.
   20   REWIND(IN)		!Back to the beginning.
        WRITE (FORMAT,21) W(1:MF) + 1	!Add one to meet the specified at least one space between columns.
   21   FORMAT ("(",<MF>("A",I0,","))	!Generates a sequence of An, items.
        LF = INDEX(FORMAT,", ")		!The last one has a trailing comma.
        IF (LF.LE.0) STOP "Format trouble!"	!Or, maybe not!
        FORMAT(LF:LF) = ")"			!Convert it to the closing bracket.
        WRITE (LINPR,*) "Format",FORMAT(1:LF)	!Present it.

Chug afresh, this time knowing the maximum length of each field.
   30   READ (IN,11,END = 40) L,ACARD(1:L)	!Place just the record's content.
        CALL LIZZIEBORDEN		!Find the chop points.
        SELECT CASE(WAY)	!What is to be done?
         CASE(-1)		!Shove leftwards by appending spaces.
          WRITE (LINPR,FORMAT) (ACARD(C(I - 1) + 1:C(I) - 1)//	!The chopped text.
     1     REPEAT(" ",W(I) - C(I) + C(I - 1) + 1),I = 1,NF)	!Some spaces.
         CASE( 0)		!Centre by appending half as many spaces.
          WRITE (LINPR,FORMAT) (ACARD(C(I - 1) + 1:C(I) - 1)//	!The chopped text.
     1     REPEAT(" ",(W(I) - C(I) + C(I - 1) + 1)/2),I = 1,NF)	!Some spaces.
         CASE(+1)		!Align rightwards is the default style.
          WRITE (LINPR,FORMAT) (ACARD(C(I - 1) + 1:C(I) - 1),I = 1,NF)	!So, just the texts.
         CASE DEFAULT		!This shouldn't happen.
         WRITE (LINPR,*) "Huh? WAY=",WAY	!But if it does,
         STOP "Unanticipated value for WAY!"	!Explain.
        END SELECT		!So much for that record.
        GO TO 30		!Go for another.
Closedown
   40   REWIND(IN)		!Be polite.
       CONTAINS	!This also marks the end of source for RAKE...
        SUBROUTINE LIZZIEBORDEN	!Take an axe to ACARD, chopping at X.
          NF = 0		!No strokes so far.
          DO I = 1,L		!So, step away.
            IF (ICHAR(ACARD(I:I)).EQ.ICHAR(X)) THEN	!Here?
              NF = NF + 1		!Yes!
              C(NF) = I		!The place!
            END IF		!So much for that.
          END DO		!On to the next.
          NF = NF + 1		!And the end of ACARD is also a chop point.
          C(NF) = L + 1		!As if here.
        END SUBROUTINE LIZZIEBORDEN	!She was aquitted.
      END SUBROUTINE RAKE	!So much raking over.

      INTEGER L,M,N	!To be determined the hard way.
      INTEGER LINPR,IN	!I/O unit numbers.
      COMMON LINPR	!Some of general note.
      LINPR = 6		!Standard output via this unit number.
      IN = 10		!Some unit number for the input file.
      OPEN (IN,FILE="Rake.txt",STATUS="OLD",ACTION="READ")	!For formatted input.
      N = 0		!No records read.
      M = 0		!Longest record so far.

    1 READ (IN,2,END = 10) L	!How long is this record?
    2 FORMAT (Q)	!Obviously, Q specifies the length, not a content field.
      N = N + 1		!Anyway, another record has been read.
      M = MAX(M,L)	!And this is the longest so far.
      GO TO 1		!Go back for more.

   10 REWIND (IN)	!We're ready now.
      WRITE (LINPR,*) N,"Recs, longest rec. length is ",M
      CALL RAKE(IN,M,"$",-1)	!Align left.
      CALL RAKE(IN,M,"$", 0)	!Centre.
      CALL RAKE(IN,M,"$",+1)	!Align right.
      END	!That's all.


' FB 1.05.0 Win64

Sub Split(s As String, sep As String, result() As String)
  Dim As Integer i, j, count = 0
  Dim temp As String
  Dim As Integer position(Len(s) + 1)
  position(0) = 0
  For i = 0 To Len(s) - 1
    For j = 0 To Len(sep) - 1
      If s[i] = sep[j] Then
        count += 1
        position(count) = i + 1
      End If
    Next j
  Next i
  position(count + 1) = Len(s) + 1
  Redim result(count)
  For i = 1 To count + 1
    result(i - 1) = Mid(s, position(i - 1) + 1, position(i) - position(i - 1) - 1)
  Next
End Sub

Sub CSet(buffer As String, s As Const String)
  Dim As Integer bLength = Len(buffer)
  Dim As Integer sLength = Len(s)
  Dim As Integer diff, lSpaces
  If sLength >= bLength Then
    LSet buffer, s
  Else
    diff = bLength - sLength
    lSpaces = diff \ 2
    LSet buffer, Space(lSpaces) + s
  End If
End Sub

Dim lines() As String
Dim count As Integer = 0

Open "align_columns.txt" For Input As #1

While Not Eof(1)
  Redim Preserve lines(count)
  Line Input #1, lines(count)
  count +=1
Wend

Close #1

Dim As Integer i,j, length, numColumns = 0
Dim As Integer numLines = UBound(lines) + 1
Dim fields() As String

' Work out the maximum number of columns
For i = 0 To numLines - 1
  Erase fields
  Split RTrim(lines(i), "$"), "$", fields()
  length = UBound(fields) + 1
  If length > numColumns Then numColumns = length
Next

' Split lines into fields and work out maximum size of each column
Dim matrix(numLines - 1, numColumns - 1) As String
Dim columnSizes(numColumns - 1) As Integer

For i = 0 To numLines - 1
  Erase fields
  Split RTrim(lines(i), "$"), "$", fields()
  For j = 0 To UBound(fields)
    matrix(i, j) = fields(j)
    length = Len(fields(j))
    If  length > columnSizes(j) Then columnSizes(j) = length
  Next j
Next i

Dim buffer As String

'Separate each column by 2 spaces
Open "align_left_columns.txt" For Output As #1
Open "align_right_columns.txt" For Output As #2
Open "align_center_columns.txt" For Output As #3

For i = 0 To UBound(matrix, 1)
  For j = 0 To UBound(matrix, 2)
    buffer = Space(columnSizes(j))
    LSet buffer, matrix(i, j)
    Print #1, buffer;
    RSet buffer, matrix(i, j)
    Print #2, buffer;
    CSet buffer, matrix(i, j)
    Print #3, buffer;
    If j < UBound(matrix, 2) Then
      Print #1, "  "; : Print #2, "  "; : Print #3, "  ";
    End If
  Next j
  Print #1, : Print #2, : Print #3,
Next i

Close #1 : Close #2 : Close #3


include "NSLog.incl"

local fn AlignColumn
'~'1
NSUInteger i

CFStringRef testStr = @"Given$a$text$file$of$many$lines,$where$fields$within$a$line$are$delineated$by¬
$a$single$'dollar'$character,$write$a$program$that$aligns$each$column$of$fields$by$ensuring$that$words¬
$in$each$column$are$separated$by$at$least$one$space.$Further,$allow$for$each$word$in$a$column$to$be¬
$either$left$justified$right$justified,$or$center$justified$within$its$column."

CFArrayRef       temp = fn StringComponentsSeparatedByString( testStr, @"$" )
CFMutableArrayRef arr = fn MutableArrayWithArray( temp )
NSUInteger      count = fn ArrayCount( arr )
ptr a(50)

NSLog( @"\nLeft aligned:\n" )
NSUInteger lineCheck = 1
for i = 0 to count -1
a( lineCheck ) = (ptr)fn StringUTF8String( arr[i] )
if ( lineCheck == 9 )
NSLog( @"%-12s %-11s %-12s %-11s %-12s %-12s %-12s %-12s %-12s", a(1),a(2),a(3),a(4),a(5),a(6),a(7),a(8),a(9) )
lineCheck = 1
else
lineCheck++
end if
next

NSLog( @"\n\nRight aligned:\n" )

lineCheck = 1
for i = 0 to count -1
a( lineCheck ) = (ptr)fn StringUTF8String( arr[i] )
if ( lineCheck == 9 )
NSLog( @"%12s %11s %12s %11s %12s %12s %12s %12s %12s", a(1),a(2),a(3),a(4),a(5),a(6),a(7),a(8),a(9) )
lineCheck = 1
else
lineCheck++
end if
next

end fn

fn AlignColumn

HandleEvents


Public Sub Main()                                                             'Written in Gambas 3.9.2 as a Command line Application - 15/03/2017
Dim siCount, siCounter, siLength As Short                                     'Counters
Dim siLongest As Short = -1                                                   'To store the longest 'Word'
Dim sLine, sRows As New String[]                                              'Arrays
Dim sTemp, sAlign As String                                                   'Temp strings
Dim sInput As String = "Given$a$text$file$of$many$lines, $where$fields$within$a$line$" & "\n"
"are$delineated$by$a$single$ 'dollar'$character,$write$a$program" & "\n"
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$" & "\n"
"column$are$separated$by$at$least$one$space." & "\n"
"Further, $allow$for$each$word$in$a$column$to$be$either$left$" & "\n"
"justified, $right$justified, $or$center$justified$within$its$column."        'Main string (with End Of Line characters added)

For Each sTemp In Split(sInput, "\n")                                         'For each Line (split by End of Line character)..
  sLine.add(sTemp)                                                            'Add the Line to sLine array
Next

For siCount = 0 To sLine.Max                                                  'For each of Lines in the array..
  For Each sTemp In Split(sLine[siCount], "$")                                'For each 'Word' in the Line (Split by the '$')
    siLength = Len(sTemp)                                                     'Store the length of the current 'Word'
    If siLength > siLongest Then siLongest = siLength                         'Make sure siLength has the length of the longest 'Word'
    sRows.add(Trim(sTemp))                                                    'Create an array of the 'Words'
  Next
  sRows.add("\n")                                                             'Add a End Of Line character to the sRows array
Next

For siCounter = 0 To 2                                                        'For each alignment (Left, Right and Centre)
  For Each sTemp In sRows                                                     'For each 'Word' in the sRows array..
    If sTemp = "\n" Then                                                      'If it's a End Of Line character then..
      Print                                                                   'Print
      Continue                                                                'Jump to the next iteration of the For Next Loop
    Endif
    If siCounter = 0 Then Print sTemp & Space(siLongest - Len(sTemp));        'Print control for Left align
    If siCounter = 1 Then Print Space(siLongest - Len(sTemp)) & sTemp;        'Print control for Right align
    If siCounter = 2 Then                                                     'Print control for Centre align
      siCount = (siLongest - Len(sTemp)) / 2                                  'Difference between the length of the longest 'Word' and the current 'Word' / 2
      sAlign = Space(siCount) & sTemp & Space(siCount)                        'Put the string together for printing
      If Len(sAlign) < siLongest Then sAlign &= " "                           'Check it's the correct length if not add a space on the end
      Print sAlign;                                                           'Print the 'Word'
    Endif
  Next
  Print                                                                       'Print an empty line between each alignment list
Next

End


package main

import (
    "fmt"
    "strings"
)

const text = `Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.`

type formatter struct {
    text  [][]string
    width []int
}

func newFormatter(text string) *formatter {
    var f formatter
    for _, line := range strings.Split(text, "\n") {
        words := strings.Split(line, "$")
        for words[len(words)-1] == "" {
            words = words[:len(words)-1]
        }
        f.text = append(f.text, words)
        for i, word := range words {
            if i == len(f.width) {
                f.width = append(f.width, len(word))
            } else if len(word) > f.width[i] {
                f.width[i] = len(word)
            }
        }
    }
    return &f
}

const (
    left = iota
    middle
    right
)

func (f formatter) print(j int) {
    for _, line := range f.text {
        for i, word := range line {
            fmt.Printf("%-*s ", f.width[i], fmt.Sprintf("%*s",
                len(word)+(f.width[i]-len(word))*j/2, word))
        }
        fmt.Println("")
    }
    fmt.Println("")
}

func main() {
    f := newFormatter(text)
    f.print(left)
    f.print(middle)
    f.print(right)
}


def alignColumns = { align, rawText ->
    def lines = rawText.tokenize('\n')
    def words = lines.collect { it.tokenize(/\$/) }
    def maxLineWords = words.collect {it.size()}.max()
    words = words.collect { line -> line + [''] * (maxLineWords - line.size()) }
    def columnWidths = words.transpose().collect{ column -> column.collect { it.size() }.max() }

    def justify = [   Right  : { width, string -> string.padLeft(width) },
                            Left   : { width, string -> string.padRight(width) },
                            Center : { width, string -> string.center(width) }      ]
    def padAll = { pad, colWidths, lineWords -> [colWidths, lineWords].transpose().collect { pad(it) + ' ' } }

    words.each { padAll(justify[align], columnWidths, it).each { print it }; println() }
}


PROCEDURE Main()
   LOCAL a := { "Given$a$text$file$of$many$lines,$where$fields$within$a$line$",;
                "are$delineated$by$a$single$'dollar'$character,$write$a$program",;
                "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$",;
                "column$are$separated$by$at$least$one$space.",;
                "Further,$allow$for$each$word$in$a$column$to$be$either$left$",;
                "justified,$right$justified,$or$center$justified$within$its$column." }
   LOCAL e, nMax

   // remove trailing dollars
   AEval( a, {|e,n| Iif( Right(e,1)=="$", a[n] := hb_StrShrink( e, 1 ), NIL ) } )

   // find max word length
   nMax := 0
   AEval( a, {|e| AEval( hb_Atokens( e, "$"), {|i| nMax := Max( nMax, Len(i) )} ) } )
   nMax++

   // start printing, padding words as needed
   ?
   ? "----Left aligned columns----"
   FOR EACH e IN a
      ?
      AEval( hb_Atokens( e, "$"), {|i| QQout( PadR(i, nMax) )} )
   NEXT

   ?
   ? "----Center aligned columns----"
   FOR EACH e IN a
      ?
      AEval( hb_Atokens( e, "$"), {|i| QQout( PadC(i, nMax) )} )
   NEXT

   ?
   ? "----Right aligned columns----"
   FOR EACH e IN a
      ?
      AEval( hb_Atokens( e, "$"), {|i| QQout( PadL(i, nMax) )} )
   NEXT

   RETURN


import Data.List (unfoldr, transpose)
import Control.Arrow (second)

dat =
  "Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n" ++
  "are$delineated$by$a$single$'dollar'$character,$write$a$program\n" ++
  "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n" ++
  "column$are$separated$by$at$least$one$space.\n" ++
  "Further,$allow$for$each$word$in$a$column$to$be$either$left$\n" ++
  "justified,$right$justified,$or$center$justified$within$its$column.\n"

brkdwn =
  takeWhile (not . null) . unfoldr (Just . second (drop 1) . span ('$' /=))

format j ls = map (unwords . zipWith align colw) rows
  where
    rows = map brkdwn $ lines ls
    colw = map (maximum . map length) . transpose $ rows
    align cw w =
      case j of
        'c' -> replicate l ' ' ++ w ++ replicate r ' '
        'r' -> replicate dl ' ' ++ w
        'l' -> w ++ replicate dl ' '
      where
        dl = cw - length w
        (l, r) = (dl `div` 2, dl - l)


CHARACTER Fnam = "\HicEst\Rosetta\Align columns.txt"

   OPEN(FIle=Fnam, Format="12$", LENgth=rows)
! call the DLG function in MatrixExplorer mode:
   DLG(Edit=Fnam, Format='12A10') ! left adjusted, 12 columns, 10 spaces each

! or the standard way:
   CALL Align( "LLLLLLLLLLL ", Fnam, rows)   ! left   align
   CALL Align( "CCCCCCCCCCC ", Fnam, rows)   ! center align
   CALL Align( "RRRRRRRRRRR ", Fnam, rows)   ! right  align
END

SUBROUTINE Align(picture, filename, rows)
   CHARACTER picture, filename
   CHARACTER out*400, txt*20

   W = LEN(picture)
   DO i = 1, rows
     out = " "
     DO j = 0, 100
       txt = filename(i, j+1, *9) ! on error branch to label 9
       WRITE(Text=out(j*W+1 : ), Format=picture) txt
     ENDDO
 9 CONTINUE
   WRITE() out
   ENDDO
END


global width

procedure main(args)
    lines := []
    width := 0
    format := left
    match("left"|"right"|"center", format <- !args)
    every put(lines,prepare(!&input))
    display(lines, proc(format,3))
end

procedure prepare(lines)
    line := []
    lines ? {
        while (not pos(0)) & (field := tab(upto('$')|0)) do {
            put(line, field)
            width <:= *field
            move(1)
            }
        }
    return line
end

procedure display(lines, format)
    width +:= 1
    every line := !lines do {
        every writes(format(!line, width))
        write()
        }
end


'LEFT CENTER RIGHT'=: i.3                NB. justification constants

NB.* alignCols v Format delimited text in justified columns
NB. y:          text to format
NB.                 rows marked by last character in text
NB.                 columns marked by $
NB. optional x: justification. Default is LEFT
NB. result:     text table
alignCols=: verb define
  LEFT alignCols y                       NB. default
:
  global=. dyad def'9!:x y'each
  oldbox=. 6 16 global '';''             NB. save settings
  7 17 global (11#' ');,~x               NB. apply new settings
  result=. _2{:\ ": <;._2 @:,&'$';._2 y  NB. parse & format text
  7 17 global oldbox                     NB. restore settings
  result
)


import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

/**
 * Aligns fields into columns, separated by "|"
 */
public class ColumnAligner {
    private List<String[]> words = new ArrayList<>();
    private int columns = 0;
    private List<Integer> columnWidths = new ArrayList<>();

    /**
     * Initialize columns aligner from lines in a single string
     *
     * @param s
     *            lines in a single string. Empty string does form a column.
     */
    public ColumnAligner(String s) {
        String[] lines = s.split("\\n");
        for (String line : lines) {
            processInputLine(line);
        }
    }

    /**
     * Initialize columns aligner from lines in a list of strings
     *
     * @param lines
     *            lines in a single string. Empty string does form a column.
     */
    public ColumnAligner(List<String> lines) {
        for (String line : lines) {
            processInputLine(line);
        }
    }

    private void processInputLine(String line) {
        String[] lineWords = line.split("\\$");
        words.add(lineWords);
        columns = Math.max(columns, lineWords.length);
        for (int i = 0; i < lineWords.length; i++) {
            String word = lineWords[i];
            if (i >= columnWidths.size()) {
                columnWidths.add(word.length());
            } else {
                columnWidths.set(i, Math.max(columnWidths.get(i), word.length()));
            }
        }
    }

    interface AlignFunction {
        String align(String s, int length);
    }

    /**
     * Left-align all columns
     *
     * @return Lines, terminated by "\n" of columns, separated by "|"
     */
    public String alignLeft() {
        return align(new AlignFunction() {
            @Override
            public String align(String s, int length) {
                return StringUtils.rightPad(s, length);
            }
        });
    }

    /**
     * Right-align all columns
     *
     * @return Lines, terminated by "\n" of columns, separated by "|"
     */
    public String alignRight() {
        return align(new AlignFunction() {
            @Override
            public String align(String s, int length) {
                return StringUtils.leftPad(s, length);
            }
        });
    }

    /**
     * Center-align all columns
     *
     * @return Lines, terminated by "\n" of columns, separated by "|"
     */
    public String alignCenter() {
        return align(new AlignFunction() {
            @Override
            public String align(String s, int length) {
                return StringUtils.center(s, length);
            }
        });
    }

    private String align(AlignFunction a) {
        StringBuilder result = new StringBuilder();
        for (String[] lineWords : words) {
            for (int i = 0; i < lineWords.length; i++) {
                String word = lineWords[i];
                if (i == 0) {
                    result.append("|");
                }
                result.append(a.align(word, columnWidths.get(i)) + "|");
            }
            result.append("\n");
        }
        return result.toString();
    }

    public static void main(String args[]) throws IOException {
        if (args.length < 1) {
            System.out.println("Usage: ColumnAligner file [left|right|center]");
            return;
        }
        String filePath = args[0];
        String alignment = "left";
        if (args.length >= 2) {
            alignment = args[1];
        }
        ColumnAligner ca = new ColumnAligner(Files.readAllLines(Paths.get(filePath), StandardCharsets.UTF_8));
        switch (alignment) {
        case "left":
            System.out.print(ca.alignLeft());
            break;
        case "right":
            System.out.print(ca.alignRight());
            break;
        case "center":
            System.out.print(ca.alignCenter());
            break;
        default:
            System.err.println(String.format("Error! Unknown alignment: '%s'", alignment));
            break;
        }
    }
}


var justification="center",
input=["Given$a$text$file$of$many$lines,$where$fields$within$a$line$",
"are$delineated$by$a$single$'dollar'$character,$write$a$program",
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$",
"column$are$separated$by$at$least$one$space.",
"Further,$allow$for$each$word$in$a$column$to$be$either$left$",
"justified,$right$justified,$or$center$justified$within$its$column."],
x,y,cols,max,cols=0,diff,left,right

String.prototype.repeat=function(n){return new Array(1 + parseInt(n)).join(this);}

for(x=0;x<input.length;x++) {
 input[x]=input[x].split("$");
 if(input[x].length>cols) cols=input[x].length;
}
for(x=0;x<cols;x++) {
 max=0;
 for(y=0;y<input.length;y++) if(input[y][x]&&max<input[y][x].length) max=input[y][x].length;
 for(y=0;y<input.length;y++)
  if(input[y][x]) {
   diff=(max-input[y][x].length)/2;
   left=" ".repeat(Math.floor(diff));
   right=" ".repeat(Math.ceil(diff));
   if(justification=="left") {right+=left;left=""}
   if(justification=="right") {left+=right;right=""}
   input[y][x]=left+input[y][x]+right;
  }
}
for(x=0;x<input.length;x++) input[x]=input[x].join(" ");
input=input.join("\n");
document.write(input);


# transpose a possibly jagged matrix
def transpose:
  if . == [] then []
  else (.[1:] | transpose) as $t
  | .[0] as $row
  | reduce range(0; [($t|length), (.[0]|length)] | max) as $i
      ([]; . + [ [ $row[$i] ] + $t[$i] ])
  end;

# left/right/center justification of strings:
def ljust(width): . + " " * (width - length);

def rjust(width): " " * (width - length) + .;

def center(width):
  (width - length) as $pad
  | if $pad <= 0 then .
    else ($pad / 2 | floor) as $half
    | $half * " " + . + ($pad-$half) * " "
    end ;

# input: a single string, which includes newlines to separate lines, and $ to separate phrases;
# method must be "left" "right" or anything else for central justification.
def format(method):
  def justify(width):
    if   method == "left"  then ljust(width)
    elif method == "right" then rjust(width)
    else center(width)
    end;

  # max_widths: input: an array of strings, each with "$" as phrase-separator;
  # return the appropriate column-wise maximum lengths
  def max_widths:
    map(split("$") | map(length))
    | transpose | map(max) ;

  split("\n") as $input
  | $input
  | (max_widths | map(.+1)) as $widths
  | map( split("$") | . as $line | reduce range(0; length) as $i
      (""; . + ($line[$i]|justify($widths[$i])) ))
  | join("\n")
;


/* Align columns, in Jsish */
function alignColumns(phrases:array, just:string) {
    var x, y, max, diff, left, right, cols=0;

    for(x=0; x<phrases.length; x++) {
        phrases[x] = phrases[x].split("$");
        if (phrases[x].length>cols) cols=phrases[x].length;
    }

    for (x=0; x<cols; x++) {
        max = 0;
        for (y=0; y<phrases.length; y++) if (phrases[y][x] && max<phrases[y][x].length) max = phrases[y][x].length;
        for (y=0; y<phrases.length; y++) {
            if (phrases[y][x]) {
                diff = (max-phrases[y][x].length)/2;
                left = " ".repeat(Math.floor(diff));
                right = " ".repeat(Math.ceil(diff));
                if (just == "left") { right += left; left=""; }
                if (just == "right") { left += right; right=""; }
                phrases[y][x] = left + phrases[y][x] + right;
            }
        }
    }
    for (x=0; x<phrases.length; x++) phrases[x] = phrases[x].join(" ");
    phrases = phrases.join("\n");
    return phrases;
}

if (Interp.conf('unitTest')) {
    var phrases = ["Given$a$text$file$of$many$lines,$where$fields$within$a$line$",
        "are$delineated$by$a$single$'dollar'$character,$write$a$program",
        "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$",
        "column$are$separated$by$at$least$one$space.",
        "Further,$allow$for$each$word$in$a$column$to$be$either$left$",
        "justified,$right$justified,$or$center$justified$within$its$column."];

    for (var just of ['left', 'center', 'right']) {
        var trial = phrases.slice(0);
        puts(just);
        puts(alignColumns(trial, just), '\n');
    }
}


txt = """Given\$a\$txt\$file\$of\$many\$lines,\$where\$fields\$within\$a\$line\$
are\$delineated\$by\$a\$single\$'dollar'\$character,\$write\$a\$program
that\$aligns\$each\$column\$of\$fields\$by\$ensuring\$that\$words\$in\$each\$
column\$are\$separated\$by\$at\$least\$one\$space.
Further,\$allow\$for\$each\$word\$in\$a\$column\$to\$be\$either\$left\$
justified,\$right\$justified,\$or\$center\$justified\$within\$its\$column."""

# left/right/center justification of strings:
ljust(s, width) = s * " "^max(0, width - length(s))
rjust(s, width) = " "^max(0, width - length(s)) * s
function center(s, width)
  pad = width - length(s)
  if pad <= 0
    return s
  else
    pad2 = div(pad, 2)
    return " "^pad2 * s * " "^(pad - pad2)
  end
end

parts = [split(rstrip(line, '$'), '$') for line in split(txt, '\n')]

max_widths = zeros(Int, maximum(length, parts))
for line in parts
  for (i, word) in enumerate(line)
    max_widths[i] = max(max_widths[i], length(word))
  end
end
max_widths += 1 # separate cols by at least one space

for (label, justify) in (("Left", ljust), ("Right",rjust), ("Center",center))
  println(label, " column-aligned output:")
  for line in parts
    for (j, word) in enumerate(line)
      print(justify(word, max_widths[j]))
    end
    println()
  end
  println("-"^sum(max_widths))
end


import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths

enum class AlignFunction {
    LEFT { override fun invoke(s: String, l: Int) = ("%-" + l + 's').format(("%" + s.length + 's').format(s)) },
    RIGHT { override fun invoke(s: String, l: Int) = ("%-" + l + 's').format(("%" + l + 's').format(s)) },
    CENTER { override fun invoke(s: String, l: Int) = ("%-" + l + 's').format(("%" + ((l + s.length) / 2) + 's').format(s)) };

    abstract operator fun invoke(s: String, l: Int): String
}

/** Aligns fields into columns, separated by "|".
 * @constructor Initializes columns aligner from lines in a list of strings.
 * @property lines Lines in a single string. Empty string does form a column.
 */
class ColumnAligner(val lines: List<String>) {
     operator fun invoke(a: AlignFunction) : String {
        var result = ""
        for (lineWords in words) {
            for (i in lineWords.indices) {
                if (i == 0)
                    result += '|'
                result += a(lineWords[i], column_widths[i])
                result += '|'
            }
            result += '\n'
        }
        return result
    }

    private val words = arrayListOf<Array<String>>()
    private val column_widths = arrayListOf<Int>()

    init {
        lines.forEach  {
            val lineWords = java.lang.String(it).split("\\$")
            words += lineWords
            for (i in lineWords.indices) {
                if (i >= column_widths.size) {
                    column_widths += lineWords[i].length
                } else {
                    column_widths[i] = Math.max(column_widths[i], lineWords[i].length)
                }
            }
        }
    }
}

fun main(args: Array<String>) {
    if (args.isEmpty()) {
        println("Usage: ColumnAligner file [L|R|C]")
        return
    }
    val ca = ColumnAligner(Files.readAllLines(Paths.get(args[0]), StandardCharsets.UTF_8))
    val alignment = if (args.size >= 2) args[1] else "L"
    when (alignment) {
        "L" -> print(ca(AlignFunction.LEFT))
        "R" -> print(ca(AlignFunction.RIGHT))
        "C" -> print(ca(AlignFunction.CENTER))
        else -> System.err.println("Error! Unknown alignment: " + alignment)
    }
}


{def txt
Given$a$text$file$of$many$lines,$where$fields$within$a$line\$are$delineated$by$a$single$'dollar'$character,$write$a$program\$that$aligns$each$column$of$fields$by$ensuring$that$words$in$each\$column$are$separated$by$at$least$one$space.\$Further,$allow$for$each$word$in$a$column$to$be$either$left\$justified,$right$justified,$or$center$justified$within$its$column.}
-> txt
{def columns                                                 // function's name

 {def columns.r                                               // loop function
  {lambda {:just :a :b}
   {if {A.empty? :a}                                          // end of loop
    then :b                                                   // return the string
    else {columns.r :just                                     // justification
                    {A.rest :a}.                              // loop on next char
                    {if {W.equal? {A.first :a} \}             // if end of line
                     then < tr> :b                            // open a table row
                     else {if {W.equal? {A.first :a} $}       // if space between words
                     then < td style="text-align::just;">:b   // open a table data with justif
                     else {A.first :a}:b }} } }}}             // else add character

 {lambda {:just :txt}                                         // main function
  {table                                                      // open an HTML table
   {columns.r                                                 // call the loop function
              :just                                           // justification
              {A.reverse {A.split ${:txt}}}                   // split and reverse
              .                                               // end point
}}}}
-> columns

{columns left txt}
->  Given	a	text	file	of	many	lines,	where	fields	within	a	line
are	delineated	by	a	single	'dollar'	character,	write	a	program
that	aligns	each	column	of	fields	by	ensuring	that	words	in	each
column	are	separated	by	at	least	one	space.
Further,	allow	for	each	word	in	a	column	to	be	either	left
justified,	right	justified,	or	center	justified	within	its	column..

{columns center txt} and {columns right txt} outputs can be seen in this website: http://lambdaway.free.fr/lambdawalks/?view=align_columns


#!/usr/bin/lasso9

local(text = "Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.
")

define go_left(text::array, width::integer) => {
	local(output = string)
	with row in #text do {
		with word in #row do {
			#output -> append(string(#word) -> padtrailing(#width + 1)&)
		}
		#output -> append('\n')
	}
	return #output
}

define go_right(text::array, width::integer) => {
	local(output = string)
	with row in #text do {
		with word in #row do {
			#output -> append(string(#word) -> padleading(#width + 1)&)
		}
		#output -> append('\n')
	}
	return #output
}

define go_center(text::array, width::integer) => {
	local(output = string)
	with row in #text do {
		with word in #row do {
			local(
				padlength	= (#width + 1 - #word -> size),
				padleft		= (' ' * (#padlength / 2)),
				padright	= (' ' * (#padlength - #padleft -> size))
			)
			#output -> append(#padleft + string(#word) + #padright)
		}
		#output -> append('\n')
	}
	return #output
}

define prepcols(text::string) => {
	local(
		result		= array,
		maxwidth	= 0
	)
	with row in #text -> split('\n') do {
		#row -> removetrailing('$')
		#result -> insert(#row -> split('$'))
	}
	with word in delve(#result) do {
		#word -> size > #maxwidth ? #maxwidth = #word -> size
	}
	stdoutnl('Left aligned result: \n' + go_left(#result, #maxwidth))
	stdoutnl('Right aligned result: \n' + go_right(#result, #maxwidth))
	stdoutnl('Centered result: \n' + go_center(#result, #maxwidth))
}

prepcols(#text)


mainwin 140 32

    CRLF$  =chr$( 13)
    maxlen =0

    read y

    Dim txt$( y)

    For i =1 To y
        Read i$
        print i$
        if right$( i$, 1) <>"$" then i$ =i$ +"$"
        txt$( i) =i$
        x  =max( CountDollars( txt$( i)), x)
    Next i

    print x

    Dim matrix$( x, y)

    Print CRLF$; "  ---- Left ----"
    For yy =1 To y
        For xx =1 To x
            matrix$( xx, yy) =word$( txt$( yy), xx, "$")
            print matrix$( xx, yy), "|";
            maxlen           =max( maxlen, Len( matrix$( xx, yy)))
        Next xx
        print ""
    Next yy

    Print CRLF$; "  ---- Right ----"
    For yy =1 To y
        For xx =1 To x
            Print right$( "                    " +matrix$( xx, yy), maxlen +1); "|";
            '   will truncate column words longer than 20. Change to use maxlen....
        Next xx
        Print ""
    Next yy

    Print CRLF$ +"  ---- Center ----"
    For yy =1 to y
        For xx =1 to x
            wordLen     =Len( matrix$( xx, yy))
            padNeeded   =maxlen -wordLen +4
            LeftSpaces  =padNeeded /2

            if LeftSpaces =int( LeftSpaces) then
                RightSpaces =LeftSpaces
            else
                RightSpaces =LeftSpaces -1
            end if

            Print space$( LeftSpaces); matrix$( xx, yy); space$( RightSpaces); "|";
        Next xx
        Print ""
    Next yy

    wait

    Data  6

    Data "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
    Data "are$delineated$by$a$single$'dollar'$character,$write$a$program"
    Data "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
    Data "column$are$separated$by$at$least$one$space."
    Data "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
    Data "justified,$right$justified,$or$center$justified$within$its$column."

    function CountDollars( src$)
        c =0
        for j =1 to len( src$)
            if mid$( src$, j, 1) ="$" then c =c +1
        next j
        CountDollars =c
    end function

    end


local tWord = {}        -- word table
local tColLen = {}      -- maximum word length in a column
local rowCount = 0      -- row counter
--store maximum column lengths at 'tColLen'; save words into 'tWord' table
local function readInput(pStr)
    for line in pStr:gmatch("([^\n]+)[\n]-") do  -- read until '\n' character
        rowCount = rowCount + 1
        tWord[rowCount] = {}                     -- create new row
        local colCount = 0
        for word in line:gmatch("[^$]+") do      -- read non '$' character
            colCount = colCount + 1
            tColLen[colCount] = math.max((tColLen[colCount] or 0), #word)   -- store column length
            tWord[rowCount][colCount] = word                                -- store words
        end--for word
    end--for line
end--readInput
--repeat space to align the words in the same column
local align = {
    ["left"] = function (pWord, pColLen)
        local n = (pColLen or 0) - #pWord + 1
        return pWord .. (" "):rep(n)
    end;--["left"]
    ["right"] = function (pWord, pColLen)
        local n = (pColLen or 0) - #pWord + 1
        return (" "):rep(n) .. pWord
    end;--["right"]
    ["center"] = function (pWord, pColLen)
        local n = (pColLen or 0) - #pWord + 1
        local n1 = math.floor(n/2)
        return (" "):rep(n1) .. pWord .. (" "):rep(n-n1)
    end;--["center"]
}
--word table padder
local function padWordTable(pAlignment)
    local alignFunc = align[pAlignment]                         -- selecting the spacer function
    for rowCount, tRow in ipairs(tWord) do
        for colCount, word in ipairs(tRow) do
            tRow[colCount] = alignFunc(word, tColLen[colCount]) -- save the padded words into the word table
        end--for colCount, word
    end--for rowCount, tRow
end--padWordTable
--main interface
--------------------------------------------------[]
function alignColumn(pStr, pAlignment, pFileName)
--------------------------------------------------[]
    readInput(pStr)                           -- store column lengths and words
    padWordTable(pAlignment or "left")        -- pad the stored words
    local output = ""
    for rowCount, tRow in ipairs(tWord) do
        local line = table.concat(tRow)       -- concatenate words in one row
        print(line)                           -- print the line
        output = output .. line .. "\n"       -- concatenate the line for output, add line break
    end--for rowCount, tRow
    if (type(pFileName) == "string") then
        local file = io.open(pFileName, "w+")
        file:write(output)                    -- write output to file
        file:close()
    end--if type(pFileName)
    return output
end--alignColumn


Module Align_Columns {
	a$={Given$a$text$file$of$many$lines,$where$fields$within$a$line$
		are$delineated$by$a$single$'dollar'$character,$write$a$program
		that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
		column$are$separated$by$at$least$one$space.
		Further,$allow$for$each$word$in$a$column$to$be$either$left$
		justified,$right$justified,$or$center$justified$within$its$column.
		}
	const cr$=chr$(13), lf$=chr$(10)
	def c1=0, cmax=0, p1=-1, i
	flush  ' empty stack
	for i=1 to len(a$)
		select case mid$(a$,i,1)
		case "$", cr$
			if p1<>-1 then data (p1, c1):  p1=-1: cmax=max(c1,cmax):c1=0
		case lf$
			data (-1,0)     ' push to end of  stack an array of two items (a tuple in m2000)
		else case
			if p1=-1 then p1=i :c1=1 else c1++
		end select
	next
	if p1<>-1 then push (p1, c1): cmax=max(c1,cmax):c1=0
	\\ so now stack of values hold all tuples.
	Dim Words(), AlignType$(1 to 3)
	AlignType$(1)=lambda$ (a$,wd)->field$(a$, wd)
	AlignType$(2)=lambda$ (a$,wd)->{
		a$=left$(a$, wd)
		=left$(string$(" ", (len(a$)-wd) div 2)+a$+string$(" ",wd),wd)
	}
	AlignType$(3)= lambda$ (a$,wd)->format$("{0:"+str$(-wd)+"}", a$)
	\\ [] return a stack object, reference and leave current stack of values a new stack
	\\ Array( stack_object) empty the stack object moving items to an array
	Words()=Array([])
	document export$
	def aline$
	cmax++  ' add one space
	For al=1 to 3
		For i=0 to len(Words())-1
			if Words(i)(0)=-1 then
				' we use rtrim$() to cut trailing spaces
				export$=rtrim$(aline$)+cr$+lf$ : aline$=""
			else
				aline$+=AlignType$(al)(mid$(a$,Words(i)(0), Words(i)(1)),cmax)
			end if
		next i
	next
	\\ export to clipboard
	Clipboard  export$
	Rem	Form 140, 60
	Rem	Print #-2, export$ ' render text to console without using console's columns
}
Align_Columns


T AbstractQueue
   F.virtual.abstract enqueue(Int item) -> N

T PrintQueue(AbstractQueue)
   F.virtual.assign enqueue(Int item) -> N
      print(item)


function r = align_columns(f)
    fid = fopen('align_column_data.txt', 'r');
    D   = {};
    M   = 0;
    while ~feof(fid)
        s = fgetl(fid);
        strsplit(s,'$');
	m = diff([0,find(s=='$')])-1;
	M = max([M,zeros(1,length(m)-length(M))], [m,zeros(1,length(M)-length(m))]);
        D{end+1}=s;
    end
    fclose(fid);

    fprintf(1,'%%-- right-justified --%%\n')
    FMT = sprintf('%%%ds ',M);
    for k=1:length(D)
	d = strsplit(D{k},'$');
        fprintf(1,FMT,d{:});
        fprintf(1,'\n');
    end

    fprintf(1,'%%-- left-justified --%%\n')
    FMT = sprintf('%%-%ds ',M);
    for k=1:length(D)
	d = strsplit(D{k},'$');
        fprintf(1,FMT,d{:});
        fprintf(1,'\n');
    end
end;


class abs definition abstract.
  public section.
    methods method1 abstract importing iv_value type f exporting ev_ret type i.
  protected section.
    methods method2 abstract importing iv_name type string exporting ev_ret type i.
    methods add importing iv_a type i iv_b type i exporting ev_ret type i.
endclass.

class abs implementation.
  method add.
    ev_ret = iv_a + iv_b.
  endmethod.
endclass.


MCSKIP "WITH" NL
"" Align columns - assumes macros on input stream 1, data on stream 2
MCPVAR 102
"" Set P102 to alignment required:
""   1 = centre
""   2 = left
""   3 = right
MCSET P102 = 1
MCSKIP MT,<>
MCINS %.
MCSKIP SL WITH *
"" Assume no more than 100 columns - P101 used for max number of fields
"" Set P variables 1-101 to 0
MCDEF ZEROPS WITHS NL AS <MCSET T1=1
%L1.MCSET PT1=0
MCSET T1=T1+1
MCGO L1 UNLESS T1 EN 102
>
ZEROPS
"" First pass - macro to accumulate max columns, and max widths
MCDEF SL N1 OPT $ N1 OR $ WITHS NL OR SPACE WITHS NL OR NL ALL
AS <MCGO L3 UNLESS T1 GR P101
MCSET P101=T1
%L3.MCSET T2=1
%L1.MCGO L0 IF T2 GR T1
MCSET T3=MCLENG(%WBT2.)
MCGO L2 UNLESS T3 GR PT2
MCSET PT2=T3
%L2.MCSET T2=T2+1
MCGO L1
>
MCSET S1=1
*MCSET S10=2
*MCSET S1=0
MCSET S4=1
""MCNOTE Max field is %P101.
""MCDEF REP NL AS <MCSET T1=1
""%L1.%PT1. MCSET T1=T1+1
""MCGO L1 UNLESS T1 GR P101
"">
""REP
MCDEF SL N1 OPT $ N1 OR $ WITHS NL OR SPACE WITHS NL OR NL ALL
AS <MCSET T2=1
%L5.MCGO L6 IF T2 GR T1
MCGO LP102
%L1.MCSET T3=%%%PT2.-MCLENG(%WBT2.)./2.
MCGO L7 IF T3 EN 0
MCSUB(<                            >,1,T3)%L7.%WBT2.""
MCSUB(<                            >,1,PT2-T3-MCLENG(%WBT2.)+1)MCGO L4
%L2.MCSUB(%WBT2.<                              >,1,PT2)MCGO L4
%L3.MCSUB(<                              >%WBT2.,1-PT2,0)""
%L4. MCSET T2=T2+1
MCGO L5
%L6.
>
MCSET S1=1
*MCSET S10=102


package
{
    public interface IInterface
    {
        function method1():void;
        function method2(arg1:Array, arg2:Boolean):uint;
    }
}


type Queue is limited interface;
procedure Enqueue (Lounge : in out Queue; Item : in out Element) is abstract;
procedure Dequeue (Lounge : in out Queue; Item : in out Element) is abstract;


columns(how)	; how = "Left", "Center" or "Right"
	New col,half,ii,max,spaces,word
	Set ii=0
	Set ii=ii+1,line(ii)="Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
	Set ii=ii+1,line(ii)="are$delineated$by$a$single$'dollar'$character,$write$a$program"
	Set ii=ii+1,line(ii)="that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
	Set ii=ii+1,line(ii)="column$are$separated$by$at$least$one$space."
	Set ii=ii+1,line(ii)="Further,$allow$for$each$word$in$a$column$to$be$either$left$"
	Set ii=ii+1,line(ii)="justified,$right$justified,$or$center$justified$within$its$column."
	Set ii="" For  Set ii=$Order(line(ii)) Quit:ii=""  Do
	. For col=1:1:$Length(line(ii),"$") Do
	. . Set max=$Length($Piece(line(ii),"$",col))
	. . Set:max>$Get(max(col)) max(col)=max
	. . Quit
	. Quit
	Set ii="" For  Set ii=$Order(line(ii)) Quit:ii=""  Do
	. Write ! For col=1:1:$Length(line(ii),"$") Do:$Get(max(col))
	. . Set word=$Piece(line(ii),"$",col)
	. . Set spaces=$Justify("",max(col)-$Length(word))
	. . If how="Left" Write word,spaces," " Quit
	. . If how="Right" Write spaces,word," " Quit
	. . Set half=$Length(spaces)\2
	. . Write $Extract(spaces,1,half),word,$Extract(spaces,half+1,$Length(spaces))," "
	. . Quit
	. Quit
	Write !
	Quit
Do columns("Left")

Given      a          text       file   of     many      lines,     where    fields  within  a      line
are        delineated by         a      single 'dollar'  character, write    a       program
that       aligns     each       column of     fields    by         ensuring that    words   in     each
column     are        separated  by     at     least     one        space.
Further,   allow      for        each   word   in        a          column   to      be      either left
justified, right      justified, or     center justified within     its      column.

Do columns("Center")

  Given        a         text     file    of     many      lines,    where   fields  within    a    line
   are     delineated     by       a    single 'dollar'  character,  write      a    program
   that      aligns      each    column   of    fields       by     ensuring  that    words    in   each
  column      are     separated    by     at     least      one      space.
 Further,    allow       for      each   word     in         a       column    to      be    either left
justified,   right    justified,   or   center justified   within     its    column.

Do columns("Right")

     Given          a       text   file     of      many     lines,    where  fields  within      a line
       are delineated         by      a single  'dollar' character,    write       a program
      that     aligns       each column     of    fields         by ensuring    that   words     in each
    column        are  separated     by     at     least        one   space.
  Further,      allow        for   each   word        in          a   column      to      be either left
justified,      right justified,     or center justified     within      its column.


from strutils import splitLines, split
from sequtils import mapIt
from strfmt import format, write

let textinfile = """Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column."""

var words = textinfile.splitLines.mapIt(it.split '$')
var maxs = newSeq[int](max words.mapIt(it.len))

for line in words:
  for j,w in line:
    maxs[j] = max(maxs[j], w.len+1)

for i, align in ["<",">","^"]:
  echo(["Left", "Right", "Center"][i], " column-aligned output:")
  for line in words:
    for j,w in line:
      stdout.write(w.format align & $maxs[j])
    stdout.write "\n"
  stdout.write "\n"


module AbstractInterfaceExample where

open import Function
open import Data.Bool
open import Data.String

-- * One-parameter interface for the type `a' with only one method.

record VoiceInterface (a : Set) : Set where
  constructor voice-interface
  field say-method-of : a → String

open VoiceInterface

-- * An overloaded method.

say : {a : Set} → ⦃ _ : VoiceInterface a ⦄ → a → String
say ⦃ instance ⦄ = say-method-of instance

-- * Some data types.

data Cat : Set where
  cat : Bool → Cat

crazy! = true
plain-cat = false

-- | This cat is crazy?
crazy? : Cat → Bool
crazy? (cat x) = x

-- | A 'plain' dog.
data Dog : Set where
  dog : Dog

-- * Implementation of the interface (and method).

instance-for-cat : VoiceInterface Cat
instance-for-cat = voice-interface case where
  case : Cat → String
  case x with crazy? x
  ... | true = "meeeoooowwwww!!!"
  ... | false = "meow!"

instance-for-dog : VoiceInterface Dog
instance-for-dog = voice-interface $ const "woof!"

-- * and then:
--
-- say dog => "woof!"
-- say (cat crazy!) => "meeeoooowwwww!!!"
-- say (cat plain-cat) => "meow!"
--


class Abs {
        public function method1...
        public function method2...

}


OBJECT fruit
ENDOBJECT

PROC color OF fruit IS EMPTY

OBJECT apple OF fruit
ENDOBJECT

PROC color OF apple IS WriteF('red ')

OBJECT orange OF fruit
ENDOBJECT

PROC color OF orange IS WriteF('orange ')

PROC main()
  DEF a:PTR TO apple,o:PTR TO orange,x:PTR TO fruit
  FORALL({x},[NEW a, NEW o],`x.color())
ENDPROC


// Interface
public interface PurchaseOrder {
    // All other functionality excluded
    Double discount();
}

// One implementation of the interface for customers
public class CustomerPurchaseOrder implements PurchaseOrder {
    public Double discount() {
        return .05;  // Flat 5% discount
    }
}

// Abstract Class
public abstract class AbstractExampleClass {
	protected abstract Integer abstractMethod();
}

// Complete the abstract class by implementing its abstract method
public class Class1 extends AbstractExampleClass {
	public override Integer abstractMethod() { return 5; }
}


# Task: Align columns
#
# Uses `Text::justify` from the standard library.
module align_columns

fun aligner(text: String, left: Float)
do
	# Each row is a sequence of fields
	var rows = new Array[Array[String]]
	for line in text.split('\n') do
		rows.add line.split("$")
	end

	# Compute the final length of each column
	var lengths = new Array[Int]
	for fields in rows do
		var i = 0
		for field in fields do
			var fl = field.length
			if lengths.length <= i or fl > lengths[i] then
				lengths[i] = fl
			end
			i += 1
		end
	end

	# Process each line and align each field
	for fields in rows do
		var line = new Array[String]
		var i = 0
		for field in fields do
			line.add field.justify(lengths[i], left)
			i += 1
		end
		print line.join(" ")
	end
end

var text = """
Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column."""

aligner(text, 0.0)
aligner(text, 1.0)
aligner(text, 0.5)


MODULE Columns;
IMPORT
  NPCT:Tools,
  Object,
  Out;

TYPE
  Parts = ARRAY 32 OF STRING;
  Formatter = PROCEDURE (s: STRING; len: LONGINT): STRING;
VAR
  lines: ARRAY 6 OF STRING;
  words: ARRAY 6 OF Parts;
  columnWidth: ARRAY 128 OF INTEGER;
  lineIdx: INTEGER;

  (*
   * Size: returns de number of words in a line
   *)
  PROCEDURE Size(p: Parts): INTEGER;
  VAR
    i: INTEGER;
  BEGIN
    i := 0;
    WHILE (i < LEN(p)) & (p[i] # NIL) DO
      INC(i);
    END;
    RETURN i
  END Size;
  (*
   * Max: returns maximum number of words in the lines
   *)
  PROCEDURE Max(w: ARRAY OF Parts): INTEGER;
  VAR
    i, max, resp: INTEGER;
  BEGIN
    i := 0;resp := 0;
    WHILE (i < LEN(w)) DO
      max := Size(w[i]);
      IF (max > resp) THEN resp := max END;
      INC(i)
    END;
    RETURN resp;
  END Max;

  (*
   * MaxColumnWidth: returns the maximum width of a column
   *)
  PROCEDURE MaxColumnWidth(w: ARRAY OF Parts;column: INTEGER): INTEGER;
  VAR
    line,max: LONGINT;
  BEGIN
    line := 0;
    max := MIN(INTEGER);
    WHILE (line < LEN(w)) DO;
      IF (w[line,column]  # NIL) & (w[line,column](Object.String8).length > max) THEN max := w[line,column](Object.String8).length END;
      INC(line)
    END;
    RETURN SHORT(max)
  END MaxColumnWidth;

  (*
   * PrintWords: prints the words in 'w' using the formatter passed in 'format'
   *)
  PROCEDURE PrintWords(w: ARRAY OF Parts; format: Formatter);
  VAR
    i,j: INTEGER;
  BEGIN
    i := 0;
    WHILE (i < LEN(words)) DO
      j := 0;
      WHILE (j < Max(words)) & (words[i,j] # NIL) DO
        Out.Object(format(words[i,j],columnWidth[j] + 1));
        INC(j)
      END;
      Out.Ln;
      INC(i)
    END;
    Out.Ln
  END PrintWords;

BEGIN
lines[0] := "Given$a$text$file$of$many$lines,$where$fields$within$a$line$";
lines[1] := "are$delineated$by$a$single$'dollar'$character,$write$a$program";
lines[2] := "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$";
lines[3] := "column$are$separated$by$at$least$one$space.";
lines[4] := "Further,$allow$for$each$word$in$a$column$to$be$either$left$";
lines[5] := "justified,$right$justified,$or$center$justified$within$its$column.";

(* Split line in words *)
lineIdx := 0;
WHILE lineIdx < LEN(lines) DO
  Tools.Split(lines[lineIdx],"$",words[lineIdx]);
  INC(lineIdx)
END;

(* Calculate width of the column *)
lineIdx := 0;
WHILE (lineIdx < Max(words)) DO
  columnWidth[lineIdx] := MaxColumnWidth(words,lineIdx);
  INC(lineIdx)
END;

(* Print Results *)
PrintWords(words,Tools.AdjustLeft);
PrintWords(words,Tools.AdjustCenter);
PrintWords(words,Tools.AdjustRight);
END Columns.


use std

(: abstract class :)

class Abs
   text		name
   AbsIface	iface

class AbsIface
   function(Abs)(int)->int	method

let Abs_Iface = Cdata [email protected] {.method = nil}

.: new Abs :. -> Abs {let a = new(Abs); a.iface = Abs_Iface; a}

=: <Abs self>.method <int i> := -> int
   (self.iface.method is nil) ? 0 , (call self.iface.method with self i)

(: implementation :)

class Sub <- Abs { int value }

let Sub_Iface = Cdata [email protected] {.method = (code of (nil the Sub).method 0)}

.: new Sub (<int value = -1>) :. -> Sub
   let s = new (Sub)
   s.iface = Sub_Iface
   s.value = value
   s

.: <Sub this>.method <int i> :. -> int {this.value + i}

(: example use :)

.:foobar<Abs a>:. {print a.method 12 ; del a}
foobar (new Sub 34)	(: prints 46 :)
foobar (new Sub)	(: prints 11 :)
foobar (new Abs)	(: prints  0 :)


color(r, g, b){
   static color
   If !color
      color := Object("base", Object("R", r, "G", g, "B", b
                                    ,"GetRGB", "Color_GetRGB"))
   return  Object("base", Color)
}
Color_GetRGB(clr) {
    return "not implemented"
}

waterColor(r, g, b){
   static waterColor
   If !waterColor
      waterColor := Object("base", color(r, g, b),"GetRGB", "WaterColor_GetRGB")
   return  Object("base", WaterColor)
}

WaterColor_GetRGB(clr){
return clr.R << 16 | clr.G << 8 | clr.B
}

test:
blue := color(0, 0, 255)
msgbox % blue.GetRGB() ; displays "not implemented"
blue := waterColor(0, 0, 255)
msgbox % blue.GetRGB() ; displays 255
return


#load "str.cma"
open Str

let input = "\
Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column."

let () =
  let lines = split (regexp_string "\n") input in
  let fields_l = List.map (split (regexp_string "$")) lines in
  let fields_l = List.map Array.of_list fields_l in
  let n = (* number of columns *)
    List.fold_left
      (fun n fields -> max n (Array.length fields))
      0 fields_l
  in
  let pads = Array.make n 0 in
  List.iter (
    (* calculate the max padding for each column *)
    Array.iteri
      (fun i word -> pads.(i) <- max pads.(i) (String.length word))
  ) fields_l;

  let print f =
    List.iter (fun fields ->
      Array.iteri (fun i word ->
        f word (pads.(i) - (String.length word))
      ) fields;
      print_newline()
    ) fields_l;
  in

  (* left column-aligned output *)
  print (fun word pad ->
    let spaces = String.make pad ' ' in
    Printf.printf "%s%s " word spaces);

  (* right column-aligned output *)
  print (fun word pad ->
    let spaces = String.make pad ' ' in
    Printf.printf "%s%s " spaces word);

  (* center column-aligned output *)
  print (fun word pad ->
    let pad1 = pad / 2 in
    let pad2 = pad - pad1 in
    let sp1 = String.make pad1 ' ' in
    let sp2 = String.make pad2 ' ' in
    Printf.printf "%s%s%s " sp1 word sp2);
;;


      INSTALL @lib$+"CLASSLIB"

      REM Declare a class with no implementation:
      DIM abstract{method}
      PROC_class(abstract{})

      REM Inherit from the abstract class:
      DIM derived{member%}
      PROC_inherit(derived{}, abstract{})
      PROC_class(derived{})

      REM Provide an implementation for the derived class:
      DEF derived.method : PRINT "Hello world!" : ENDPROC

      REM Instantiate the derived class:
      PROC_new(instance{}, derived{})

      REM Test by calling the method:
      PROC(instance.method)


#ifndef INTERFACE_ABS
#define INTERFACE_ABS

typedef struct sAbstractCls *AbsCls;

typedef struct sAbstractMethods {
    int         (*method1)(AbsCls c, int a);
    const char *(*method2)(AbsCls c, int b);
    void        (*method3)(AbsCls c, double d);
} *AbstractMethods, sAbsMethods;

struct sAbstractCls {
    AbstractMethods  klass;
    void     *instData;
};

#define ABSTRACT_METHODS( cName, m1, m2, m3 ) \
    static sAbsMethods cName ## _Iface = { &m1, &m2, &m3 }; \
    AbsCls cName ## _Instance( void *clInst) { \
        AbsCls ac = malloc(sizeof(struct sAbstractCls)); \
        if (ac) { \
            ac->klass = &cName ## _Iface; \
            ac->instData = clInst; \
        }\
        return ac; }

#define Abs_Method1( c, a) (c)->klass->method1(c, a)
#define Abs_Method2( c, b) (c)->klass->method2(c, b)
#define Abs_Method3( c, d) (c)->klass->method3(c, d)
#define Abs_Free(c) \
  do { if (c) { free((c)->instData); free(c); } } while(0);

#endif


abstract class Class1
{
   public abstract void method1();

   public int method2()
   {
      return 0;
   }
}


import: mapping
import: file

: <<nbl     \ stream n -- stream
   #[ ' ' <<c ] times ;

String method: justify( n just -- s )
| l m |
   n self size - dup ->l 2 / ->m
   String new
   just $RIGHT  if=: [ l <<nbl  self <<  return ]
   just $LEFT   if=: [ self <<  l <<nbl  return ]
   m <<nbl  self <<  l m - <<nbl
;

: align( file just -- )
| lines maxsize |
    #[ wordsWith( '$' ) ] file File new map   ->lines
    0 #[ apply( #[ size max ] ) ] lines apply ->maxsize
    #[ apply( #[ justify( maxsize , just) . ] ) printcr ] lines apply
;


text = .array~of("Given$a$text$file$of$many$lines,$where$fields$within$a$line$", -
                 "are$delineated$by$a$single$'dollar'$character,$write$a$program", -
                 "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$", -
                 "column$are$separated$by$at$least$one$space.", -
                 "Further,$allow$for$each$word$in$a$column$to$be$either$left$", -
                 "justified,$right$justified,$or$center$justified$within$its$column.")

columns = 0
parsedText = .array~new
-- split each line of text into words and figure out how many columns we need
loop line over text
    parsedLine = line~makearray("$")
    parsedText~append(parsedLine)
    columns = max(columns, parsedLine~items)
end

-- now figure out how wide we need to make each column
columnWidths = .array~new(columns)
linelength = 0
loop i = 1 to columns
    width = 0
    loop line over parsedText
        word = line[i]
        if word \= .nil then width = max(width, word~length)
    end
    columnWidths[i] = width
    -- keep track of the total width, including space for a separator
    linelength += width + 1
end

say "align left:"
say
out = .mutableBuffer~new(linelength)
loop line over parsedText
  -- mutable buffers are more efficient than repeated string concats
  -- reset the working buffer to zero
  out~setbuffersize(0)
  loop col = 1 to line~items
      word = line[col]
      if word == .nil then word = ''
      out~append(word~left(columnwidths[col] + 1))
  end
  say out~string
end
say
say "align right:"
say

loop line over parsedText
  -- mutable buffers are more efficient than repeated string concats
  -- reset the working buffer to zero
  out~setbuffersize(0)
  loop col = 1 to line~items
      word = line[col]
      if word == .nil then word = ''
      out~append(word~right(columnwidths[col] + 1))
  end
  say out~string
end
say
say "align center:"
say

loop line over parsedText
  -- mutable buffers are more efficient than repeated string concats
  -- reset the working buffer to zero
  out~setbuffersize(0)
  loop col = 1 to line~items
      word = line[col]
      if word == .nil then word = ''
      out~append(word~center(columnwidths[col] + 1))
  end
  say out~string
end


class Abs {
public:
	virtual int method1(double value) = 0;
	virtual int add(int a, int b){
		return a+b;
	}
};


Class Abstract.Class.Shape [ Abstract ]
{
Parameter SHAPE = 1;
Property Name As %String;
Method Description() {}
}

Class Abstract.Class.Square Extends (%RegisteredObject, Shape)
{
Method Description()
{
	Write "SHAPE=", ..#SHAPE, !
	Write ..%ClassName()_$Case(..%Extends(..%PackageName()_".Shape"), 1: " is a ", : " is not a ")_"shape"
}
}


       INTERFACE-ID. Shape.

       PROCEDURE DIVISION.

       METHOD-ID. perimeter.
       DATA DIVISION.
       LINKAGE SECTION.
       01  ret USAGE FLOAT-LONG.
       PROCEDURE DIVISION RETURNING ret.
       END METHOD perimeter.

       METHOD-ID. shape-area.
       DATA DIVISION.
       LINKAGE SECTION.
       01  ret USAGE FLOAT-LONG.
       PROCEDURE DIVISION RETURNING ret.
       END METHOD shape-area.

       END INTERFACE Shape.

       CLASS-ID. Rectangle.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       REPOSITORY.
           INTERFACE Shape.

       OBJECT IMPLEMENTS Shape.
           DATA DIVISION.
           WORKING-STORAGE SECTION.
           01  width  USAGE FLOAT-LONG PROPERTY.
           01  height USAGE FLOAT-LONG PROPERTY.

           PROCEDURE DIVISION.

           METHOD-ID. perimeter.
           DATA DIVISION.
           LINKAGE SECTION.
           01  ret USAGE FLOAT-LONG.
           PROCEDURE DIVISION RETURNING ret.
               COMPUTE ret = width * 2.0 + height * 2.0
               GOBACK
               .
           END METHOD perimeter.

           METHOD-ID. shape-area.
           DATA DIVISION.
           LINKAGE SECTION.
           01  ret USAGE FLOAT-LONG.
           PROCEDURE DIVISION RETURNING ret.
               COMPUTE ret = width * height
               GOBACK
               .
           END METHOD shape-area.
       END OBJECT.

       END CLASS Rectangle.


FUNCTION alignColumns RETURNS CHAR (
   i_c      AS CHAR,
   i_calign AS CHAR
):

   DEF VAR ipass     AS INT.
   DEF VAR iline     AS INT.
   DEF VAR icol      AS INT.
   DEF VAR iwidth    AS INT EXTENT.
   DEF VAR cword     AS CHAR.
   DEF VAR cspace    AS CHAR.
   DEF VAR cresult   AS CHAR.

   EXTENT( iwidth ) = NUM-ENTRIES( ENTRY( 1, i_c, "~n" ), "$" ).

   DO ipass = 0 TO 1:
      DO iline = 1 TO NUM-ENTRIES( i_c, "~n" ):
         DO icol = 1 TO NUM-ENTRIES( ENTRY( iline, i_c, "~n" ), "$" ):
            cword = ENTRY( icol, ENTRY( iline, i_c, "~n" ), "$" ).
            IF ipass = 0 THEN
               iwidth = MAXIMUM( LENGTH( cword ), iwidth[ icol ] ).
            ELSE DO:
               cspace = FILL( " ", iwidth[ icol ] - LENGTH( cword ) ).
               CASE i_calign:
                  WHEN "left"    THEN cresult = cresult + cword + cspace.
                  WHEN "right"   THEN cresult = cresult + cspace + cword.
                  WHEN "center"  THEN DO:
                     cword = FILL( " ", INTEGER( LENGTH( cspace ) / 2 ) ) + cword.
                     cresult = cresult + cword + FILL( " ", iwidth[icol] - LENGTH( cword ) ).
                  END.
               END CASE. /* i_calign */
               cresult = cresult + " ".
            END.
         END. /* DO icol = 1 TO ... */
         IF ipass = 1 THEN
            cresult = cresult + "~n".
      END. /* DO iline = 1 TO ... */
   END. /* DO ipass = 0 TO 1 */

   RETURN cresult.

END FUNCTION.

DEF VAR cc AS CHAR.

cc =  SUBSTITUTE(
         "&1~n&2~n&3~n&4~n&5~n&6",
         "Given$a$text$file$of$many$lines,$where$fields$within$a$line$",
         "are$delineated$by$a$single$'dollar'$character,$write$a$program",
         "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$",
         "column$are$separated$by$at$least$one$space.",
         "Further,$allow$for$each$word$in$a$column$to$be$either$left$",
         "justified,$right$justified,$or$center$justified$within$its$column."
      ).

MESSAGE
   alignColumns( cc, "left" )    SKIP
   alignColumns( cc, "right" )   SKIP
   alignColumns( cc, "center" )
VIEW-AS ALERT-BOX.


(defgeneric kar (kons)
  (:documentation "Return the kar of a kons."))

(defgeneric kdr (kons)
  (:documentation "Return the kdr of a kons."))

(defun konsp (object &aux (args (list object)))
  "True if there are applicable methods for kar and kdr on object."
  (not (or (endp (compute-applicable-methods #'kar args))
           (endp (compute-applicable-methods #'kdr args)))))

(deftype kons ()
  '(satisfies konsp))


(* Abstract type *)
Object = POINTER TO ABSTRACT RECORD END;

(* Integer inherits Object *)
Integer = POINTER TO RECORD (Object)
	i: INTEGER
END;
(* Point inherits Object *)
Point = POINTER TO RECORD (Object)
	x,y: REAL
END;


abstract class Animal # only abstract class can have abstract methods
  abstract def move
  abstract def think

  # abstract class can have normal fields and methods
  def initialize(@name : String)
  end

  def process
    think
    move
  end
end

# WalkingAnimal still have to be declared abstract because `think` was not implemented
abstract class WalkingAnimal < Animal
  def move
    puts "#{@name} walks"
  end
end

class Human < WalkingAnimal
  property in_car = false

  def move
    if in_car
      puts "#{@name} drives a car"
    else
      super
    end
  end

  def think
    puts "#{@name} thinks"
  end
end

# Animal.new # => can't instantiate abstract class
he = Human.new("Andrew") # ok
he.process


'================
Class AlignedText
'================

indexbase 1

string  buf, bufo, cr, tab, jus
sys     Cols, Rows, ColWidth[200], TotWidth, ColPad

method SetText(string s)
cr=chr(13)+chr(10)
tab=chr(9)
jus=string 200,"L"
buf=s
measure
end method

method measure()
sys a, b, wa, wb, cm, c, cw
a=1 : b=1
Cols=0 : Rows=0 : ColPad=3
do
  wb=b
  a=instr b,buf,cr
  if a=0 then exit do
  cm=0
  c++
  do
    wa=instr wb,buf,"$"
    if wa=0 or wa>a then exit do
    cm++
    if cm>cols then cols=cm
    cw=wa-wb
    if cw > ColWidth[cm] then ColWidth[cm]=cw
    wb=wa+1
  end do
  b=a+len cr
end do
rows=c
'
c=0
for i=1 to cols
  ColWidth[ i ]+=ColPad
  c+=ColWidth[ i ]
next
TotWidth=c+len cr
'print ShowMetrics
end method

method ShowMetrics() as string
pr="METRICS:" cr cr
pr+=rows tab cols tab totwidth cr cr
pr+="column" tab "spacing" cr
for i=1 to cols
  pr+=i tab ColWidth[ i ] cr
next
return pr
end method

method justify(string j)
mid jus,1,j
end method

method layout() as string
sys a, b, wa, wb, wl, cm, lpos, cpos
bufo=space Rows*TotWidth
a=1 : b=1
do
  wb=b
  a=instr(b,buf,cr)
  if a=0 then exit do
  cm=0
  cpos=1
  do
    wa=instr(wb,buf,"$")
    if wa=0 or wa>a then exit do
    '
    cm++
    '
    'JUSTIFICATION
    '
    wl=wa-wb
    p=lpos+cpos 'default "L" LEFT ALIGN
    '
    select case asc(jus,cm)
      case "R" : p=lpos+cpos+ColWidth[cm]-wl-Colpad
      case "C" : p=lpos+cpos+( ColWidth[cm]-wl-Colpad )*.5
    end select
    '
    mid bufo,p, mid buf,wb,wl
    cpos+=colwidth[cm]
    wb=wa+1
  end do
  b=a+len cr
  lpos+=TotWidth
  if lpos<len(bufo) then mid bufo,lpos-1,cr
end do
return bufo
end method

end class

'#recordof AlignedText

'====
'TEST
'====

AlignedText tt
tt.SetText quote
"""
Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.
"""
'print tt.ShowMetrics
tt.justify "LLLLCCCRRRRR"
putfile "t.txt", tt.layout


declare
  %% Lines: list of strings
  %% Alignment: function like fun {Left Txt ExtraSpace} ... end
  %% Returns: list of aligned (virtual) strings
  fun {Align Lines Alignment}
     ParsedLines = {Map Lines ParseLine}
     NumColumns = {Maximum {Map ParsedLines Record.width}}
     %% maps column index to column width:
     WidthOfColumn = {Record.map {TupleRange NumColumns}
                      fun {$ ColumnIndex}
                         fun {LengthOfThisColumn ParsedLine}
                            {Length {CondSelect ParsedLine ColumnIndex nil}}
                         end
                      in
                         {Maximum {Map ParsedLines LengthOfThisColumn}}
                      end}
  in
     {Map ParsedLines
      fun {$ Columns}
         {Record.mapInd Columns
          fun {$ ColumnIndex ColumnText}
             Extra = WidthOfColumn.ColumnIndex - {Length ColumnText}
          in
             {Alignment ColumnText Extra}#" "
          end}
      end}
  end

  %% A parsed line is a tuple of columns.
  %% "a$b$c" -> '#'(1:"a" 2:"b" 3:"c")
  fun {ParseLine Line}
     {List.toTuple '#' {String.tokens Line &$}}
  end

  %% possible alignments:

  fun {Left Txt Extra}
     Txt#{Spaces Extra}
  end

  fun {Right Txt Extra}
     {Spaces Extra}#Txt
  end

  fun {Center Txt Extra}
     Half = Extra div 2
  in
     {Spaces Half}#Txt#{Spaces Half + Extra mod 2}
  end

  %% helpers:

  %% 3 -> unit(1 2 3)
  fun {TupleRange Max}
     {List.toTuple unit {List.number 1 Max 1}}
  end

  fun {Maximum X|Xr}
     {FoldL Xr Value.max X}
  end

  fun {Spaces N}
     case N of 0 then nil
     else & |{Spaces N-1}
     end
  end

  Lines = ["Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
           "are$delineated$by$a$single$'dollar'$character,$write$a$program"
           "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
           "column$are$separated$by$at$least$one$space."
           "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
           "justified,$right$justified,$or$center$justified$within$its$column."]
in
  {ForAll {Align Lines Left} System.showInfo}


import std.stdio;

class Foo {
    // abstract methods can have an implementation for
    // use in super calls.
    abstract void foo() {
        writeln("Test");
    }
}

interface Bar {
    void bar();

    // Final interface methods are allowed.
    final int spam() { return 1; }
}

class Baz : Foo, Bar { // Super class must come first.
    override void foo() {
        writefln("Meep");
        super.foo();
    }

    void bar() {}
}

void main() {}


TSomeClass = class abstract (TObject)
  ...
end;


interface Foo {
    to bar(a :int, b :int)
}


program Project1;

{$H+}//Use ansistrings
uses
  Classes,
  SysUtils,
  StrUtils;

  procedure AlignByColumn(Align: TAlignment);
  const
    TextToAlign =
      'Given$a$text$file$of$many$lines,$where$fields$within$a$line$'#$D#$A +
      'are$delineated$by$a$single$''dollar''$character,$write$a$program'#$D#$A +
      'that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$'#$D#$A +
      'column$are$separated$by$at$least$one$space.'#$D#$A +
      'Further,$allow$for$each$word$in$a$column$to$be$either$left$'#$D#$A +
      'justified,$right$justified,$or$center$justified$within$its$column.';
  var
    TextLine: TStringList;
    TextLines: array of TStringList;
    OutPutString, EmptyString, Item: string;
    MaxLength, i, j: Int32;
  begin
    try
      MaxLength := 0;
      TextLine := TStringList.Create;
      TextLine.Text := TextToAlign;
      setlength(Textlines, TextLine.Count);
      for i := 0 to TextLine.Count - 1 do
      begin
        Textlines[i] := TStringList.Create;
        Textlines[i].Text := AnsiReplaceStr(TextLine[i], '$', #$D#$A);
      end;

      for i := 0 to High(TextLines) do
        for j := 0 to Textlines[i].Count - 1 do
          if MaxLength < Length(TextLines[i][j]) then
            MaxLength := Length(TextLines[i][j]);
      if MaxLength > 0 then
        MaxLength := MaxLength + 2; // Add two empty spaces to it

      for i := 0 to High(TextLines) do
      begin
        OutPutString := '';
        for j := 0 to Textlines[i].Count - 1 do
        begin
          EmptyString := StringOfChar(' ', MaxLength);
          if j <> 0 then
            EmptyString[1] := '|';
          Item := TextLines[i][j];
          case Align of
            taLeftJustify: Move(Item[1], EmptyString[2], Length(Item));
            taRightJustify: Move(Item[1], EmptyString[MaxLength - Length(Item) + 1],
                Length(Item));
            taCenter: Move(Item[1], EmptyString[(MaxLength - Length(Item) + 1) div
                2 + 1], Length(Item));
          end;
          OutPutString := OutPutString + EmptyString;
        end;
        writeln(OutPutString);
      end;
    finally
      writeln;
      FreeAndNil(TextLine);
      for i := High(TextLines) downto 0 do
        FreeAndNil(TextLines[i]);
    end;
  end;

begin
  AlignByColumn(taLeftJustify);
  AlignByColumn(taCenter);
  AlignByColumn(taRightJustify);
end.


deferred class
    AN_ABSTRACT_CLASS

feature

    a_deferred_feature
        -- a feature whose implementation is left to a descendent
        deferred
        end

    an_effective_feature: STRING
        -- deferred (abstract) classes may still include effective features
        do
            Result := "I am implemented!"
        end

end


abstract class Bike
{
    abstract run();
}


type Shape =
  abstract Perimeter: unit -> float
  abstract Area: unit -> float

type Rectangle(width, height) =
  interface Shape with
    member x.Perimeter() = 2.0 * width + 2.0 * height
    member x.Area() = width * height


#/usr/bin/perl -w
use strict ;

die "Call : perl columnaligner.pl <inputfile> <printorientation>!\n" unless
   @ARGV == 2 ; #$ARGV[ 0 ] contains example file , $ARGV[1] any of 'left' , 'right' or 'center'
die "last argument must be one of center, left or right!\n" unless
   $ARGV[ 1 ] =~ /center|left|right/ ;
sub printLines( $$$ ) ;
open INFILE , "<" , "$ARGV[ 0 ]" or die "Can't open $ARGV[ 0 ]!\n" ;
my @lines = <INFILE> ;
close INFILE ;
chomp @lines ;
my @fieldwidths = map length, split /\$/ , $lines[ 0 ] ;
foreach my $i ( 1..$#lines ) {
   my @words = split /\$/ , $lines[ $i ] ;
   foreach my $j ( 0..$#words ) {
      if ( $j <= $#fieldwidths ) {
         if ( length $words[ $j ] > $fieldwidths[ $j ] ) {
               $fieldwidths[ $j ] = length $words[ $j ] ;
         }
      }
      else {
         push @fieldwidths, length $words[ $j ] ;
      }
   }
}
printLine( $_ , $ARGV[ 1 ] , \@fieldwidths ) foreach @lines ;
##################################################################    ####
sub printLine {
   my $line = shift ;
   my $orientation = shift ;
   my $widthref = shift ;
   my @words = split /\$/, $line ;
   foreach my $k ( 0..$#words ) {
      my $printwidth = $widthref->[ $k ] + 1 ;
      if ( $orientation eq 'center' ) {
         $printwidth++ ;
      }
      if ( $orientation eq 'left' ) {
         print $words[ $k ] ;
         print " " x ( $printwidth - length $words[ $k ] ) ;
      }
      elsif ( $orientation eq 'right' ) {
         print " " x ( $printwidth - length $words[ $k ] ) ;
         print $words[ $k ] ;
      }
      elsif ( $orientation eq 'center' ) {
         my $left = int( ( $printwidth - length $words[ $k ] )     / 2 ) ;
         my $right = $printwidth - length( $words[ $k ] ) - $left      ;
         print " " x $left ;
         print $words[ $k ] ;
         print " " x $right ;
      }
   }
   print "\n" ;
}


abstract class X
{
  Void method1 ()
  {
    echo ("Method 1 in X")
  }

  abstract Void method2 ()
}

class Y : X
{
  // Y must override the abstract method in X
  override Void method2 ()
  {
    echo ("Method 2 in Y")
  }
}

class Main
{
  public static Void main ()
  {
    y := Y()
    y.method1
    y.method2
  }
}


include 4pp/lib/foos.4pp

:: X()
   class
     method: method1
     method: method2
   end-class {
     :method { ." Method 1 in X" cr } ; defines method1
   }
;

:: Y()
   extends X()
   end-extends {
     :method { ." Method 2 in Y" cr } ; defines method2
   }
;

: Main
  static Y() y
  y => method1
  y => method2
;

Main


   ! abstract derived type
   type, abstract :: TFigure
      real(rdp) :: area
   contains
      ! deferred method i.e. abstract method =  must be overridden in extended type
      procedure(calculate_area), deferred, pass :: calculate_area
   end type TFigure
   ! only declaration of the abstract method/procedure for TFigure type
   abstract interface
      function  calculate_area(this)
         import TFigure !imports TFigure type from host scoping unit and makes it accessible here
         implicit none
         class(TFigure) :: this
         real(rdp) :: calculate_area
      end function calculate_area
   end interface


' FB 1.05.0 Win64

Type Animal Extends Object
  Declare Abstract Sub MakeNoise()
End Type

Type Bear Extends Animal
  name As String
  Declare Constructor(name As String)
  Declare Sub MakeNoise()
End Type

Constructor Bear(name As String)
  This.name = name
End Constructor

Sub Bear.MakeNoise()
  Print name; " is growling"
End Sub

Type Dog Extends Animal
  name As String
  Declare Constructor(name As String)
  Declare Sub MakeNoise()
End Type

Constructor Dog(name As String)
  This.name = name
End Constructor

Sub Dog.MakeNoise()
  Print name; " is barking"
End Sub

Dim b As Animal Ptr = New Bear("Bruno")
b -> MakeNoise()
Dim d As Animal Ptr = New Dog("Rover")
d -> MakeNoise()
Delete b
Delete d
Print
Print "Press any key to quit program"
Sleep


constant data = {
    "Given$a$text$file$of$many$lines,$where$fields$within$a$line$",
    "are$delineated$by$a$single$'dollar'$character,$write$a$program",
    "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$",
    "column$are$separated$by$at$least$one$space.",
    "Further,$allow$for$each$word$in$a$column$to$be$either$left$",
    "justified,$right$justified,$or$center$justified$within$its$column."
}

function split(sequence s, integer c)
sequence out = {}
integer first = 1, delim
    while first<=length(s) do
        delim = find_from(c,s,first)
        if delim = 0 then
            delim = length(s)+1
        end if
        out = append(out,s[first..delim-1])
        first = delim + 1
    end while
    return out
end function

function align(sequence s, integer width, integer alignment)
integer n = width-length(s)
    if n<=0 then
        return s
    elsif alignment<0 then
        return s & repeat(' ', n)
    elsif alignment>0 then
        return repeat(' ', n) & s
    else
        -- (PL if I'd written this, I'd have n-floor(n/2) on the rhs)
        return repeat(' ', floor(n/2)) & s & repeat(' ', floor(n/2+0.5))
    end if
end function

procedure AlignColumns()
integer llij
sequence lines, li
sequence maxlens = {}
    lines = repeat(0,length(data))
    for i=1 to length(data) do
        li = split(data[i],'$')
        lines[i] = li
        if length(li)>length(maxlens) then
            maxlens &= repeat(0,length(li)-length(maxlens))
        end if
        for j=1 to length(li) do
            llij = length(li[j])
            if llij>maxlens[j] then maxlens[j] = llij end if
        end for
    end for

    for a=-1 to 1 do    -- (alignment = left/centre/right)
        for i=1 to length(lines) do
            for j=1 to length(lines[i]) do
                puts(1, align(lines[i][j],maxlens[j],a) & ' ')
            end for
            puts(1,'\n')
        end for
        puts(1,'\n')
    end for
    if getc(0) then end if
end procedure

AlignColumns()


class AbstractStack()
   def .valid?(object) nil

tag AbstractStack some-object # always fails


package main

import "fmt"

type Beast interface {
    Kind() string
    Name() string
    Cry() string
}

type Dog struct {
    kind string
    name string
}

func (d Dog) Kind() string { return d.kind }

func (d Dog) Name() string { return d.name }

func (d Dog) Cry() string { return "Woof" }

type Cat struct {
    kind string
    name string
}

func (c Cat) Kind() string { return c.kind }

func (c Cat) Name() string { return c.name }

func (c Cat) Cry() string { return "Meow" }

func bprint(b Beast) {
    fmt.Printf("%s, who's a %s, cries: %q.\n", b.Name(), b.Kind(), b.Cry())
}

func main() {
    d := Dog{"labrador", "Max"}
    c := Cat{"siamese", "Sammy"}
    bprint(d)
    bprint(c)
}


public interface Interface {
    int method1(double value)
    int method2(String name)
    int add(int a, int b)
}


include ..\Utilitys.pmt

0 40 repeat var gap

( "Given$a$text$file$of$many$lines,$where$fields$within$a$line$"
  "are$delineated$by$a$single$'dollar'$character,$write$a$program"
  "that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$"
  "column$are$separated$by$at$least$one$space."
  "Further,$allow$for$each$word$in$a$column$to$be$either$left$"
  "justified,$right$justified,$or$center$justified$within$its$column." )

len var lines

def alignWords  /# -1/0/1 for left/center/right #/
    >ps

    lines for
        get len for >ps
            tps get gap ps> get nip 1 +
            tps -1 == if -1 * align else
            tps 0 == if tostr align else
            tps 1 == if align else
            drop drop "Wrong!" exitfor
            endif endif endif
            print
        endfor
        drop
        nl
    endfor
    ps> drop
enddef

lines for var i
    i get "$" " " subst split
    len for var j
        j get len dup
        gap j get rot
        < if swap j set else swap drop endif var gap drop
    endfor
    i set
endfor

-1 alignWords nl
0 alignWords nl
1 alignWords nl
drop


<?php
$j2justtype = array('L' => STR_PAD_RIGHT,
                    'R' => STR_PAD_LEFT,
                    'C' => STR_PAD_BOTH);

/**
 Justify columns of textual tabular input where the record separator is the newline
 and the field separator is a 'dollar' character.
 justification can be L, R, or C; (Left, Right, or Centered).

 Return the justified output as a string
*/
function aligner($str, $justification = 'L') {
  global $j2justtype;
  assert(array_key_exists($justification, $j2justtype));
  $justtype = $j2justtype[$justification];

  $fieldsbyrow = array();
  foreach (explode("\n", $str) as $line)
    $fieldsbyrow[] = explode('$', $line);
  $maxfields = max(array_map('count', $fieldsbyrow));

  foreach (range(0, $maxfields - 1) as $col) {
    $maxwidth = 0;
    foreach ($fieldsbyrow as $fields)
      $maxwidth = max($maxwidth, strlen(array_key_exists($col, $fields) ? $fields[$col] : 0));
    foreach ($fieldsbyrow as &$fields)
      $fields[$col] = str_pad(array_key_exists($col, $fields) ? $fields[$col] : "", $maxwidth, ' ', $justtype);
    unset($fields); // see http://bugs.php.net/29992
  }
  $result = '';
  foreach ($fieldsbyrow as $fields)
    $result .= implode(' ', $fields) . "\n";
  return $result;
}

$textinfile = 'Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$\'dollar\'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.';

foreach (array('L', 'R', 'C') as $j)
  echo aligner($textinfile, $j);

?>


class  Eq a  where
   (==) :: a -> a -> Bool
   (/=) :: a -> a -> Bool


class abstraction()
    abstract method compare(l,r) # generates runerr(700, "method compare()")
end


public abstract class Abs {
    public abstract int method1(double value);
    protected abstract int method2(String name);
    int add(int a, int b) {
        return a + b;
    }
}


import util.

main =>
    Text =
"Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.",
    Lines = split(Text,"\n"),
    Lines = [Line1|_],
    N = len(split(strip(Line1,"$ "), "$")),   % number of columns
    WidthArr = {0 : _ in 1..N},
    foreach (Line in Lines)
        Words = split(strip(Line,"$ "), "$"),
        foreach ({I,Word} in zip(1..N, Words))
            WidthArr[I] := max(WidthArr[I], len(Word))
        end
    end,
    foreach (I in 1..N)
        WidthArr[I] := WidthArr[I]+1          % separate cols by at least one space
    end,
    foreach (Align in [left, right, center])
        output_lines(Lines,N,WidthArr,Align),
        nl,nl
    end.

output_lines(Lines,N,WidthArr,Align) =>
    foreach (Line in Lines)
        Words = split(strip(Line,"$ "), "$"),
        foreach ({I,Word} in zip(1..N, Words))
            output_word(Word,WidthArr[I],Align)
        end,
        nl
    end.

output_word(Word,Width,left) =>
    printf("%-*s",Width,Word).
output_word(Word,Width,right) =>
    printf("%*s",Width,Word).
output_word(Word,Width,_) =>
    Pad = len(Word)-Width,
    Pad1 is Pad div 2,
    Pad2 is Pad-Pad1,
    printf("%*s%s%*s",Pad1,"",Word,Pad2,"").


(let Sizes NIL                         # Build a list of sizes
   (let Lines                          # and of lines
      (make
         (in "input.txt"                     # Reading input file
            (while (split (line) "$")        # delimited by '$'
               (let (L (link (mapcar pack @))  S Sizes)
                  (setq Sizes                   # Maintain sizes
                     (make
                        (while (or L S)
                           (link
                              (max
                                 (inc (length (pop 'L)))
                                 (pop 'S) ) ) ) ) ) ) ) ) )
      (for L Lines                                 # Print lines
         (prinl (apply align L (mapcar - Sizes))) )   # left aligned
      (prinl)
      (for L Lines
         (prinl (apply align L Sizes)) )              # right aligned
      (prinl)
      (for L Lines
         (prinl (apply center L Sizes)) ) ) )         # and centered


abstract type «name» end
abstract type «name» <: «supertype» end


// version 1.1

interface Announcer {
    fun announceType()

    // interface can contain non-abstract members but cannot store state
    fun announceName() {
        println("I don't have a name")
    }
}

abstract class Animal: Announcer {
    abstract fun makeNoise()

    // abstract class can contain non-abstract members
    override fun announceType() {
        println("I am an Animal")
    }
}

class Dog(private val name: String) : Animal() {
    override fun makeNoise() {
       println("Woof!")
    }

    override fun announceName() {
       println("I'm called $name")
    }
}

class Cat: Animal() {
    override fun makeNoise() {
       println("Meow!")
    }

    override fun announceType() {
       println("I am a Cat")
    }
}

fun main(args: Array<String>) {
    val d = Dog("Fido")
    with(d) {
        makeNoise()
        announceType()  // inherits Animal's implementation
        announceName()
    }
    println()
    val c = Cat()
    with(c) {
        makeNoise()
        announceType()
        announceName()  // inherits Announcer's implementation
   }
}


define abstract_trait => trait {
    require get(index::integer)

    provide first()  => .get(1)
    provide second() => .get(2)
    provide third()  => .get(3)
    provide fourth() => .get(4)
}

define my_type => type {
    parent array
    trait { import abstract_trait }

    public onCreate(...) => ..onCreate(:#rest)
}

local(test) = my_type('a','b','c','d','e')
#test->first  + "\n"
#test->second + "\n"
#test->third  + "\n"
#test->fourth + "\n"


declare text character (300) varying;
declare word character (20) varying;
declare justification character (1);
declare k fixed binary;
declare input file, output file output;

open file (input)  title ( '/CENTER.DAT,type(text),recsize(1000)' );
open file (output) title ( '/OUT.TXT,type(text),recsize(1000)' );
on endfile (input) stop;

display ('Specify whether justification is left, centered, or right');
display ('Reply with a single letter: L, C, or R');
get edit (justification) (A(1));

do forever;
   get file (input) edit (text) (L);
   put skip list (text);
   text = trim(text, '$', '$');
   do until (k = 0);
      k = index(text, '$');
      if k = 0 then /* last word in line */
         word = text;
      else
         do;
            word = substr(text, 1, k-1);
            text = substr(text, k);
            text = trim(text, '$');
         end;
      select (justification);
         when ('C', 'c') word = center(word, maxlength(word));
         when ('R', 'r') word = right (word, maxlength(word));
         otherwise ; /* The default is left adjusted. */
      end;
      put file (output) edit (word) (a(maxlength(word)));
   end;
   put file (output) skip;
end;


$file =
@'
Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.
'@.Split("`n")

$arr = @()
$file | foreach {
    $line = $_
    $i = 0
    $hash = [ordered]@{}
    $line.split('$') | foreach{
        $hash["$i"] = "$_"
        $i++
     }
    $arr += @([pscustomobject]$hash)
}
$arr | Format-Table -HideTableHeaders -Wrap *


T Sandpile
   DefaultDict[(Int, Int), Int] grid

   F (gridtext)
      V array = gridtext.split_py().map(x -> Int(x))
      L(x) array
         .grid[(L.index I/ 3, L.index % 3)] = x

   Set[(Int, Int)] _border = Set(cart_product(-1 .< 4, -1 .< 4).filter((r, c) -> !(r C 0..2) | !(c C 0..2)))
   _cell_coords = cart_product(0.<3, 0.<3)

   F topple()
      V& g = .grid
      L(r, c) ._cell_coords
         I g[(r, c)] >= 4
            g[(r - 1, c)]++
            g[(r + 1, c)]++
            g[(r, c - 1)]++
            g[(r, c + 1)]++
            g[(r, c)] -= 4
            R 1B
      R 0B

   F stabilise()
      L .topple() {}

      L(row_col) ._border.intersection(Set(.grid.keys()))
         .grid.pop(row_col)

   F ==(other)
      R all(._cell_coords.map(row_col -> @.grid[row_col] == @other.grid[row_col]))

   F +(other)
      V ans = Sandpile(‘’)
      L(row_col) ._cell_coords
         ans.grid[row_col] = .grid[row_col] + other.grid[row_col]
      ans.stabilise()
      R ans

   F String()
      [String] txt
      L(row) 3
         txt.append((0.<3).map(col -> String(@.grid[(@row, col)])).join(‘ ’))
      R txt.join("\n")

V unstable = Sandpile(‘4 3 3
                       3 1 2
                       0 2 3’)
V s1 = Sandpile(‘1 2 0
                 2 1 1
                 0 1 3’)
V s2 = Sandpile(‘2 1 3
                 1 0 1
                 0 1 0’)
V s3 = Sandpile(‘3 3 3  3 3 3  3 3 3’)
V s3_id = Sandpile(‘2 1 2  1 0 1  2 1 2’)

print(unstable)
print()
unstable.stabilise()
print(unstable)
print()
print(s1 + s2)
print()
print(s2 + s1)
print()
print(s1 + s2 == s2 + s1)
print()
print(s3 + s3_id)
print()
print(s3 + s3_id == s3)
print()
print(s3_id + s3_id)
print()
print(s3_id + s3_id == s3_id)


on extendAbstractClass (instance, abstractClass)
  -- 'raw' instance of abstract class is made parent ("ancestor") of the
  -- passed instance, i.e. the passed instance extends the abstract class
  instance.setProp(#ancestor, abstractClass.rawNew())
end


:- protocol(datep).

    :- public(today/3).
    :- public(leap_year/1).
    :- public(name_of_day/3).
    :- public(name_of_month/3).
    :- public(days_in_month/3).

:- end_protocol.


BaseClass = {}

function class ( baseClass )
    local new_class = {}
    local class_mt = { __index = new_class }

    function new_class:new()
        local newinst = {}
        setmetatable( newinst, class_mt )
        return newinst
    end

    if not baseClass then baseClass = BaseClass end
        setmetatable( new_class, { __index = baseClass } )

    return new_class
end

function abstractClass ( self )
    local new_class = {}
    local class_mt = { __index = new_class }

    function new_class:new()
        error("Abstract classes cannot be instantiated")
    end

    if not baseClass then baseClass = BaseClass end
        setmetatable( new_class, { __index = baseClass } )

    return new_class
end

BaseClass.class = class
BaseClass.abstractClass = abstractClass


aligner :-
	L ="Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.",

	% read the lines and the words
	% compute the length of the longuest word.
	% LP is the list of lines,
	% each line is a list of words
	parse(L, 0, N, LP, []),

	% we need to add 1 to aligned
	N1 is N+1,
	% words will be left aligned
	sformat(AL, '~~w~~t~~~w|', [N1]),
	% words will be centered
	sformat(AC, '~~t~~w~~t~~~w|', [N1]),
	% words will be right aligned
	sformat(AR, '~~t~~w~~~w|', [N1]),

	write('Left justified :'), nl,
	maplist(affiche(AL), LP), nl,
	write('Centered justified :'), nl,
	maplist(affiche(AC), LP), nl,
	write('Right justified :'), nl,
	maplist(affiche(AR), LP), nl.

affiche(F, L) :-
	maplist(my_format(F), L),
	nl.

my_format(_F, [13]) :-
	nl.

my_format(F, W) :-
	string_to_atom(W,AW),
	sformat(AF, F, [AW]),
	write(AF).

parse([], Max, Max) --> [].

parse(T, N, Max) -->
	{ parse_line(T, 0, N1, T1, L, []),
	  (   N1 > N -> N2 = N1; N2 = N)},
	[L],
	parse(T1, N2, Max).

parse_line([], NF, NF, []) --> [].

parse_line([H|TF], NF, NF, TF) -->
	{code_type(H, end_of_line), !},
	[].

parse_line(T, N, NF, TF) -->
	{ parse_word(T, 0, N1, T1, W, []),
	  (   N1 > N -> N2 = N1; N2 = N)},
	[W],
	parse_line(T1, N2, NF, TF).

% 36 is the code of '$'
parse_word([36|T], N, N, T) -->
	{!},
	[].

parse_word([H|T], N, N, [H|T]) -->
	{code_type(H, end_of_line), !},
	[].

parse_word([], N, N, []) --> [].

parse_word([H|T], N1, NF, TF) -->
	[H],
	{N2 is  N1 + 1},
	parse_word(T, N2, NF, TF).