// Version 1.2.31

import java.io.File

typealias Maze = List<CharArray>

/**
    * Makes the maze half as wide (i. e. "+---+" becomes "+-+"), so that
    * each cell in the maze is the same size horizontally as vertically.
    * (Versus the expanded version, which looks better visually.)
    * Also, converts each line of the maze from a String to a
    * char[], because we'll want mutability when drawing the solution later.
    */
fun decimateHorizontally(lines: List<String>): Maze {
    val width = (lines[0].length + 1) / 2
    val c = List(lines.size) { CharArray(width) }
    for (i in 0 until lines.size) {
        for (j in 0 until width) c[i][j] = lines[i][j * 2]
    }
    return c
}

/**
    * Given the maze, the x and y coordinates (which must be odd),
    * and the direction we came from, return true if the maze is
    * solvable, and draw the solution if so.
    */
fun solveMazeRecursively(maze: Maze, x: Int, y: Int, d: Int): Boolean {
    var ok = false
    var i = 0
    while (i < 4 && !ok) {
        if (i != d) {
            // 0 = up, 1 = right, 2 = down, 3 = left
            when(i) {
                0 -> if (maze[y - 1][x] == ' ') ok = solveMazeRecursively (maze, x, y - 2, 2)
                1 -> if (maze[y][x + 1] == ' ') ok = solveMazeRecursively (maze, x + 2, y, 3)
                2 -> if (maze[y + 1][x] == ' ') ok = solveMazeRecursively (maze, x, y + 2, 0)
                3 -> if (maze[y][x - 1] == ' ') ok = solveMazeRecursively (maze, x - 2, y, 1)
             else -> {}
            }
        }
        i++
    }

    // check for end condition
    if (x == 1 && y == 1) ok = true

    // once we have found a solution, draw it as we unwind the recursion
    if (ok) {
        maze[y][x] = '*'
        when (d) {
            0 -> maze[y - 1][x] = '*'
            1 -> maze[y][x + 1] = '*'
            2 -> maze[y + 1][x] = '*'
            3 -> maze[y][x - 1] = '*'
         else -> {}
        }
    }
    return ok
}

/**
    * Solve the maze and draw the solution. For simplicity,
    * assumes the starting point is the lower right, and the
    * ending point is the upper left.
    */
fun solveMaze(maze: Maze) =
    solveMazeRecursively(maze, maze[0].size - 2, maze.size - 2, -1)

/**
    * Opposite of decimateHorizontally(). Adds extra characters to make
    * the maze "look right", and converts each line from char[] to
    * String at the same time.
    */
fun expandHorizontally(maze: Maze): Array<String> {
    val tmp = CharArray(3)
    val lines = Array<String>(maze.size) { "" }
    for (i in 0 until maze.size) {
        val sb = StringBuilder(maze[i].size * 2)
        for (j in 0 until maze[i].size) {
            if (j % 2 == 0)
                sb.append(maze[i][j])
            else {
                for (k in 0..2) tmp[k] = maze[i][j]
                if (tmp[1] == '*') {
                    tmp[0] = ' '
                    tmp[2] = ' '
                }
                sb.append(tmp)
            }
        }
        lines[i] = sb.toString()
    }
    return lines
}

/**
    * Accepts a maze as generated by:
    * http://rosettacode.org/wiki/Maze_generation#Kotlin
    * in a file whose name is specified as a command-line argument.
    */
fun main(args: Array<String>) {
    if (args.size != 1) {
        println("The maze file to be read should be passed as a single command line argument.")
        return
    }
    val f = File(args[0])
    if (!f.exists()) {
        println("Sorry ${args[0]} does not exist.")
        return
    }
    val lines = f.readLines(Charsets.US_ASCII)
    val maze = decimateHorizontally(lines)
    solveMaze(maze)
    val solvedLines = expandHorizontally(maze)
    println(solvedLines.joinToString("\n"))
}


HighlightGraph[maze, [email protected][maze, 1, 273]]


import random, strutils

type

  Direction {.pure.} = enum None, Up, Left, Down, Right

  Maze = object
    cells: seq[string]
    hwalls: seq[string]
    vwalls: seq[string]

####################################################################################################
# Maze creation.

func initMaze(rows, cols: Positive): Maze =
  ## Initialize a maze description.
  var h = repeat('-', cols)
  var v = repeat("|", cols)
  for i in 0..<rows:
    result.cells.add newString(cols)
    result.hwalls.add h
    result.vwalls.add v

proc gen(maze: var Maze; r, c: Natural) =
  ## Generate a maze starting from point (r, c).

  maze.cells[r][c] = ' '
  var dirs = [Up, Left, Down, Right]
  dirs.shuffle()
  for dir in dirs:
    case dir
    of None:
      discard
    of Up:
      if r > 0 and maze.cells[r-1][c] == '\0':
        maze.hwalls[r][c] = chr(0)
        maze.gen(r-1, c)
    of Left:
      if c > 0 and maze.cells[r][c-1] == '\0':
        maze.vwalls[r][c] = chr(0)
        maze.gen(r, c-1)
    of Down:
      if r < maze.cells.high and maze.cells[r+1][c] == '\0':
        maze.hwalls[r+1][c] = chr(0)
        maze.gen(r+1, c)
    of Right:
      if c < maze.cells[0].high and maze.cells[r][c+1] == '\0':
        maze.vwalls[r][c+1] = chr(0)
        maze.gen(r, c+1)

proc gen(maze: var Maze) =
  ## Generate a maze, choosing a random starting point.
  maze.gen(rand(maze.cells.high), rand(maze.cells[0].high))

####################################################################################################
# Maze solving.

proc solve(maze: var Maze; ra, ca, rz, cz: Natural) =
  ## Solve a maze by finding the path from point (ra, ca) to point (rz, cz).

  proc rsolve(maze: var Maze; r, c: Natural; dir: Direction): bool {.discardable.} =
    ## Recursive solver.

    if r == rz and c == cz:
      maze.cells[r][c] = 'F'
      return true

    if dir != Down and maze.hwalls[r][c] == '\0':
      if maze.rSolve(r-1, c, Up):
        maze.cells[r][c] = '^'
        maze.hwalls[r][c] = '^'
        return true

    if dir != Up and r < maze.hwalls.high and maze.hwalls[r+1][c] == '\0':
      if maze.rSolve(r+1, c, Down):
        maze.cells[r][c] = 'v'
        maze.hwalls[r+1][c] = 'v'
        return true

    if dir != Left and c < maze.vwalls[0].high and maze.vwalls[r][c+1] == '\0':
      if maze.rSolve(r, c+1, Right):
        maze.cells[r][c] = '>'
        maze.vwalls[r][c+1] = '>'
        return true

    if dir != Right and maze.vwalls[r][c] == '\0':
      if maze.rSolve(r, c-1, Left):
        maze.cells[r][c] = '<'
        maze.vwalls[r][c] = '<'
        return true

  maze.rsolve(ra, ca, None)
  maze.cells[ra][ca] = 'S'

####################################################################################################
# Maze display.

func `$`(maze: Maze): string =
  ## Return the string representation fo a maze.

  const
    HWall = "+---"
    HOpen = "+   "
    VWall = "|   "
    VOpen = "    "
    RightCorner = "+\n"
    RightWall = "|\n"

  for r, hw in maze.hwalls:

    for h in hw:
      if h == '-' or r == 0:
        result.add HWall
      else:
        result.add HOpen
        if h notin {'-', '\0'}: result[^2] = h
    result.add RightCorner

    for c, vw in maze.vwalls[r]:
      if vw == '|' or c == 0:
        result.add VWall
      else:
        result.add VOpen
        if vw notin {'|', '\0'}: result[^4] = vw
      if maze.cells[r][c] != '\0': result[^2] = maze.cells[r][c]
    result.add RightWall

  for _ in 1..maze.hwalls[0].len:
    result.add HWall
  result.add RightCorner

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

when isMainModule:

  const
    Width = 8
    Height = 8

  randomize()
  var maze = initMaze(Width, Height)
  maze.gen()
  var ra, rz = rand(Width - 1)
  var ca, cz = rand(Height - 1)
  while rz == ra and cz == ca:
    # Make sur starting and ending points are different.
    rz = rand(Width - 1)
    cz = rand(Height - 1)
  maze.solve(ra, ca , rz, cz)
  echo maze


#!perl
use strict;
use warnings;

my ($width, $height) = @ARGV;
$_ ||= 10 for $width, $height;

my %visited;

my $h_barrier = "+" . ("--+" x $width) . "\n";
my $v_barrier = "|" . ("  |" x $width) . "\n";
my @output = ($h_barrier, $v_barrier) x $height;
push @output, $h_barrier;
my @dx = qw(-1 1 0 0);
my @dy = qw(0 0 -1 1);

sub visit {
   my ($x, $y) = @_;
   $visited{$x, $y} = 1;
   my $rand = int rand 4;
   for my $n ( $rand .. 3, 0 .. $rand-1 ) {
      my ($xx, $yy) = ($x + $dx[$n], $y + $dy[$n]);
      next if $visited{ $xx, $yy };
      next if $xx < 0 or $xx >= $width;
      next if $yy < 0 or $yy >= $height;

      my $row = $y * 2 + 1 + $dy[$n];
      my $col = $x * 3 + 1 + $dx[$n];
      substr( $output[$row], $col, 2, '  ' );

      no warnings 'recursion';
      visit( $xx, $yy );
   }
}

visit( int rand $width, int rand $height );

print "Here is the maze:\n";
print @output;

%visited = ();

my @d = ('>>', '<<', 'vv', '^^');
sub solve {
   my ($x, $y) = @_;
   return 1 if $x == 0 and $y == 0;
   $visited{ $x, $y } = 1;
   my $rand = int rand 4;
   for my $n ( $rand .. 3, 0 .. $rand-1 ) {
      my ($xx, $yy) = ($x + $dx[$n], $y + $dy[$n]);
      next if $visited{ $xx, $yy };
      next if $xx < 0 or $xx >= $width;
      next if $yy < 0 or $yy >= $height;

      my $row = $y * 2 + 1 + $dy[$n];
      my $col = $x * 3 + 1 + $dx[$n];

      my $b = substr( $output[$row], $col, 2 );
      next if "  " ne $b;

      no warnings 'recursion';
      next if not solve( $xx, $yy );

      substr( $output[$row], $col, 2, $d[$n] );
      substr( $output[$row-$dy[$n]], $col-$dx[$n], 2, $d[$n] );
      return 1;
   }
   0;
}

if( solve( $width-1, $height-1 ) ) {
   print "Here is the solution:\n";
   substr( $output[1], 1, 2, '**' );
   print @output;
} else {
   print "Could not solve!\n";
}


--
-- demo\rosetta\Maze_solving.exw
-- =============================
--
with javascript_semantics
constant w = 11, h = 8

sequence wall = join(repeat("+",w+1),"---")&"\n",
         cell = join(repeat("|",w+1)," ? ")&"\n",
         grid = split(join(repeat(wall,h+1),cell),'\n')

procedure amaze(integer x, y)
    grid[y][x] = ' '                        -- mark cell visited
    sequence p = shuffle({{x-4,y},{x,y+2},{x+4,y},{x,y-2}})
    for i=1 to length(p) do
        integer {nx,ny} = p[i]
        if nx>1 and nx<w*4 and ny>1 and ny<=2*h and grid[ny][nx]='?' then
            integer mx = (x+nx)/2
            grid[(y+ny)/2][mx-1..mx+1] = ' ' -- knock down wall
            amaze(nx,ny)
        end if
    end for
end procedure

integer dx, dy -- door location (in a wall!)

function solve_maze(integer x, y)
    sequence p = {{x-4,y},{x,y+2},{x+4,y},{x,y-2}}
    for d=1 to length(p) do
        integer {nx,ny} = p[d]
        integer {wx,wy} = {(x+nx)/2,(y+ny)/2}
        if grid[wy][wx]=' ' then
            grid[wy][wx] = "-:-:"[d]        -- mark path
            if {wx,wy}={dx,dy} then return true end if
            if grid[ny][nx]=' ' then
                grid[ny][nx] = 'o'          -- mark cell
                if solve_maze(nx,ny) then return true end if
                grid[ny][nx] = ' '          -- unmark cell
            end if
            grid[wy][wx] = ' '              -- unmark path
        end if
    end for
    return false
end function

function heads()
    return rand(2)=1 -- toin coss 50:50 true(1)/false(0)
end function

integer {x,y} = {(rand(w)*4)-1,rand(h)*2}
amaze(x,y)
-- mark start pos
grid[y][x] = '*'
-- add a random door (heads=rhs/lhs, tails=top/btm)
if heads() then
    {dy,dx} = {rand(h)*2,heads()*w*4+1}
    grid[dy][dx] = ' '
else
    {dy,dx} = {heads()*h*2+1,rand(w)*4-1}
    grid[dy][dx-1..dx+1] = ' '
end if
{} = solve_maze(x,y)
puts(1,join(grid,'\n'))


(de shortestPath (Goal This Maze)
   (let (Path NIL  Best NIL  Dir " > ")
      (recur (This Path Dir)
         (when (and This (not (: mark)))
            (push 'Path (cons This Dir))
            (if (== Goal This)
               (unless (and Best (>= (length Path) (length Best)))
                  (setq Best Path) )
               (=: mark T)
               (recurse (: west) Path " > ")
               (recurse (: east) Path " < ")
               (recurse (: south) Path " \^ ")
               (recurse (: north) Path " v ")
               (=: mark NIL) ) ) )
      (disp Maze 0
         '((Fld) (if (asoq Fld Best) (cdr @) "   ")) ) ) )


:- dynamic cell/2.
:- dynamic maze/3.
:- dynamic path/1.

maze_solve(Lig,Col) :-
	retractall(cell(_,_)),
	retractall(maze(_,_,_)),
	retractall(path(_)),

	% initialisation of the neighbours of the cells
	forall(between(0, Lig, I),
	       (   forall(between(0, Col, J), assert(maze(I, J, []))))),

	% creation of the window of the maze
	new(D, window('Maze')),
	forall(between(0,Lig, I),
	       (XL is  50, YL is I * 30 + 50,
		XR is Col * 30 + 50,
		new(L, line(XL, YL, XR, YL)),
		send(D, display, L))),

	forall(between(0,Col, I),
	       (XT is  50 + I * 30, YT is 50,
		YB is Lig * 30 + 50,
		new(L, line(XT, YT, XT, YB)),
		send(D, display, L))),

	SX is Col * 30 + 100,
	SY is Lig * 30 + 100,
	send(D, size, new(_, size(SX, SY))),
	L0 is random(Lig),
	C0 is random(Col),
	assert(cell(L0, C0)),
	\+search(D, Lig, Col, L0, C0),
	send(D, open),

	% we look for a path from cell(0, 0) to cell(Lig-1, Col-1)
	% creation of the entrance
	erase_line(D, -1, 0, 0, 0),

	% creation of the exit
	Lig1 is Lig-1,
	Col1 is Col-1,
	erase_line(D, Lig1, Col1, Lig, Col1),

	% seraching the path
	assert(path([[0, 0], [-1, 0]])),
	walk(Lig, Col),
	path(P),
	display_path(D, P).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
walk(Lig, Col) :-
	path([[L, C] | _R]),
	L is Lig - 1,
	C is Col - 1,
	retract(path(P)),
	assert(path([[Lig, C]|P])).

walk(Lig, Col) :-
	retract(path([[L, C] | R])),
	maze(L, C, Edge),
	member([L1, C1], Edge),
	\+member([L1, C1], R),
	assert(path([[L1,C1], [L, C] | R])),
	walk(Lig, Col).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
display_path(_, []).

display_path(D, [[L, C] | R]):-
	new(B, box(10,10)),
	send(B, fill_pattern, new(_, colour(@default, 0,0,0))),
	X is C * 30 + 60,
	Y is L * 30 + 60,
	send(D, display, B, point(X,Y)),
	display_path(D, R).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
search(D, Lig, Col, L, C) :-
	Dir is random(4),
	nextcell(Dir, Lig, Col, L, C, L1, C1),
	assert(cell(L1,C1)),
	assert(cur(L1,C1)),

	retract(maze(L, C, Edge)),
	assert(maze(L, C, [[L1, C1] | Edge])),
	retract(maze(L1, C1, Edge1)),
	assert(maze(L1, C1, [[L, C] | Edge1])),

	erase_line(D, L, C, L1, C1),
	search(D, Lig, Col, L1, C1).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
erase_line(D, L, C, L, C1) :-
	(   C < C1 -> C2 = C1; C2 = C),
	XT is C2  * 30 + 50,
	YT is L * 30 + 51, YR is (L+1) * 30 + 50,
	new(Line, line(XT, YT, XT, YR)),
	send(Line, colour, white),
	send(D, display, Line).

erase_line(D, L, C, L1, C) :-
	XT is  51 + C * 30, XR is 50 + (C + 1) * 30,
	(   L < L1 -> L2 is L1; L2 is L),
	YT is L2 * 30 + 50,
	new(Line, line(XT, YT, XR, YT)),
	send(Line, colour, white),
	send(D, display, Line).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
nextcell(Dir, Lig, Col, L, C, L1, C1) :-
	next(Dir, Lig, Col, L, C, L1, C1);
	(   Dir1 is (Dir+3) mod 4,
	    next(Dir1, Lig, Col, L, C, L1, C1));
	(   Dir2 is (Dir+1) mod 4,
	    next(Dir2, Lig, Col, L, C, L1, C1));
	(   Dir3 is (Dir+2) mod 4,
	    next(Dir3, Lig, Col, L, C, L1, C1)).

% 0 => northward
next(0, _Lig, _Col, L, C, L1, C) :-
	L > 0,
	L1 is L - 1,
	\+cell(L1, C).

% 1 => rightward
next(1, _Lig, Col, L, C, L, C1) :-
	C < Col - 1,
	C1 is C + 1,
	\+cell(L, C1).

% 2 => southward
next(2, Lig, _Col, L, C, L1, C) :-
	L < Lig - 1,
	L1 is L + 1,
	\+cell(L1, C).

% 3 => leftward
next(2, _Lig, _Col, L, C, L, C1) :-
	C > 0,
	C1 is C - 1,
	\+cell(L, C1).


;code from the maze generation task is place here in its entirety before the rest of the code

Procedure displayMazePath(Array maze(2), List Path.POINT())
  Protected x, y, vWall.s, hWall.s
  Protected mazeWidth = ArraySize(maze(), 1), mazeHeight = ArraySize(maze(), 2)
  Protected Dim mazeOutput.mazeOutput(mazeHeight)
  Protected Dim mazeRow.mazeOutput(0)
  Static pathChars.s = "@^>v<"

  For y = 0 To mazeHeight
    makeDisplayMazeRow(mazeRow(), maze(), y): mazeOutput(y) = mazeRow(0)
  Next

  If ListSize(path())
    FirstElement(path())
    Protected prevPath.POINT = path()

    While NextElement(path())
      x = path()\x - prevPath\x
      y = path()\y - prevPath\y
      Select x
        Case -1: dirTaken = #dir_W
        Case 1: dirTaken = #dir_E
        Default
          If y < 0
            dirTaken = #dir_N
          Else
            dirTaken = #dir_S
          EndIf
      EndSelect
      hWall = mazeOutput(prevPath\y)\hWall
      mazeOutput(prevPath\y)\hWall = Left(hWall, prevPath\x * #cellDWidth + 2) + Mid(pathChars, dirTaken + 1, 1) + Right(hWall, Len(hWall) - (prevPath\x * #cellDWidth + 3))
      prevPath = path()
    Wend
    hWall = mazeOutput(prevPath\y)\hWall
    mazeOutput(prevPath\y)\hWall = Left(hWall, prevPath\x * #cellDWidth + 2) + Mid(pathChars, #dir_ID + 1, 1) + Right(hWall, Len(hWall) - (prevPath\x * #cellDWidth + 3))

    For y = 0 To mazeHeight
      PrintN(mazeOutput(y)\vWall): PrintN(mazeOutput(y)\hWall)
    Next
  EndIf
EndProcedure

Procedure solveMaze(Array maze(2), *start.POINT, *finish.POINT, List Path.POINT())
  Protected mazeWidth = ArraySize(maze(), 1), mazeHeight = ArraySize(maze(), 2)
  Dim visited(mazeWidth + 1, mazeHeight + 1) ;includes padding for easy border detection

  Protected i
  ;mark outside border as already visited (off limits)
  For i = 1 To mazeWidth
    visited(i, 0) = #True: visited(i, mazeHeight + 1) = #True
  Next
  For i = 1 To mazeHeight
    visited(0, i) = #True: visited(mazeWidth + 1, i) = #True
  Next

  Protected x = *start\x, y = *start\y, nextCellDir
  visited(x + offset(#visited, #dir_ID)\x, y + offset(#visited, #dir_ID)\y) = #True

  ClearList(path())
  Repeat
    If x = *finish\x And y = *finish\y
      AddElement(path())
      path()\x = x: path()\y = y
      Break ;success
    EndIf

    nextCellDir = #firstDir - 1
    For i = #firstDir To #numDirs
      If Not visited(x + offset(#visited, i)\x, y + offset(#visited, i)\y)
        If maze(x + offset(#wall, i)\x, y + offset(#wall, i)\y) & wallvalue(i) <> #Null
          nextCellDir = i: Break ;exit for/next search
        EndIf
      EndIf
    Next

    If nextCellDir >= #firstDir
      visited(x + offset(#visited, nextCellDir)\x, y + offset(#visited, nextCellDir)\y) = #True

      AddElement(path())
      path()\x = x: path()\y = y

      x + offset(#maze, nextCellDir)\x: y + offset(#maze, nextCellDir)\y
    ElseIf ListSize(path()) > 0
      x = path()\x: y = path()\y
      DeleteElement(path())
    Else
      Break
    EndIf
  ForEver

EndProcedure

;demonstration
If OpenConsole()
  Define.POINT start, finish
  start\x = Random(mazeWidth - 1): start\y = Random(mazeHeight - 1)
  finish\x = Random(mazeWidth - 1): finish\y = Random(mazeHeight - 1)
  NewList Path.POINT()
  solveMaze(maze(), start, finish, path())
  If ListSize(path()) > 0
    PrintN("Solution found for path between (" + Str(start\x) + ", " + Str(start\y) + ") and (" + Str(finish\x) + ", " + Str(finish\y) + ")")
    displayMazePath(maze(), path())
  Else
    PrintN("No solution found for path between (" + Str(start\x) + ", " + Str(start\y) + ") and (" + Str(finish\x) + ", " + Str(finish\y) + ")")
  EndIf

  Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
  CloseConsole()
EndIf


# python 3

def Dijkstra(Graph, source):
    '''
        +   +---+---+
        | 0   1   2 |
        +---+   +   +
        | 3   4 | 5
        +---+---+---+

        >>> graph = (        # or ones on the diagonal
        ...     (0,1,0,0,0,0,),
        ...     (1,0,1,0,1,0,),
        ...     (0,1,0,0,0,1,),
        ...     (0,0,0,0,1,0,),
        ...     (0,1,0,1,0,0,),
        ...     (0,0,1,0,0,0,),
        ... )
        ...
        >>> Dijkstra(graph, 0)
        ([0, 1, 2, 3, 2, 3], [1e+140, 0, 1, 4, 1, 2])
        >>> display_solution([1e+140, 0, 1, 4, 1, 2])
        5<2<1<0
    '''
    # Graph[u][v] is the weight from u to v (however 0 means infinity)
    infinity = float('infinity')
    n = len(graph)
    dist = [infinity]*n   # Unknown distance function from source to v
    previous = [infinity]*n # Previous node in optimal path from source
    dist[source] = 0        # Distance from source to source
    Q = list(range(n)) # All nodes in the graph are unoptimized - thus are in Q
    while Q:           # The main loop
        u = min(Q, key=lambda n:dist[n])                 # vertex in Q with smallest dist[]
        Q.remove(u)
        if dist[u] == infinity:
            break # all remaining vertices are inaccessible from source
        for v in range(n):               # each neighbor v of u
            if Graph[u][v] and (v in Q): # where v has not yet been visited
                alt = dist[u] + Graph[u][v]
                if alt < dist[v]:       # Relax (u,v,a)
                    dist[v] = alt
                    previous[v] = u
    return dist,previous

def display_solution(predecessor):
    cell = len(predecessor)-1
    while cell:
        print(cell,end='<')
        cell = predecessor[cell]
    print(0)


;; Returns a path connecting two given cells in the maze
;; find-path :: Maze Cell Cell -> (Listof Cell)
(define (find-path m p1 p2)
  (match-define (maze N M tbl) m)
  (define (alternatives p prev) (remove prev (connections tbl p)))
  (define (dead-end? p prev) (empty? (alternatives p prev)))
  (define ((next-turn route) p)
    (define prev (car route))
    (cond
      [(equal? p p2) (cons p2 route)]
      [(dead-end? p prev) '()]
      [else (append-map (next-turn (cons p route))
                        (alternatives p prev))]))
  (reverse
   (append-map (next-turn (list p1))
               (alternatives p1 (list p1)))))


constant mapping = :OPEN(' '),
		      :N< ╵ >,
		      :E< ╶ >,
		     :NE< └ >,
		      :S< ╷ >,
		     :NS< │ >,
		     :ES< ┌ >,
		    :NES< ├ >,
		      :W< ╴ >,
		     :NW< ┘ >,
		     :EW< ─ >,
		    :NEW< ┴ >,
		     :SW< ┐ >,
		    :NSW< ┤ >,
		    :ESW< ┬ >,
		   :NESW< ┼ >,
		   :TODO< x >,
	          :TRIED< · >;

enum Sym (mapping.map: *.key);
my @ch = mapping.map: *.value;

enum Direction <DeadEnd Up Right Down Left>;

sub gen_maze ( $X,
               $Y,
               $start_x = (^$X).pick * 2 + 1,
               $start_y = (^$Y).pick * 2 + 1 )
{
    my @maze;
    push @maze, $[ flat ES, -N, (ESW, EW) xx $X - 1, SW ];
    push @maze, $[ flat (NS, TODO) xx $X, NS ];
    for 1 ..^ $Y {
	push @maze, $[ flat NES, EW, (NESW, EW) xx $X - 1, NSW ];
	push @maze, $[ flat (NS, TODO) xx $X, NS ];
    }
    push @maze, $[ flat NE, (EW, NEW) xx $X - 1, -NS, NW ];
    @maze[$start_y][$start_x] = OPEN;

    my @stack;
    my $current = [$start_x, $start_y];
    loop {
        if my $dir = pick_direction( $current ) {
            @stack.push: $current;
            $current = move( $dir, $current );
        }
        else {
            last unless @stack;
            $current = @stack.pop;
        }
    }
    return @maze;

    sub pick_direction([$x,$y]) {
	my @neighbors =
	    (Up    if @maze[$y - 2][$x]),
	    (Down  if @maze[$y + 2][$x]),
	    (Left  if @maze[$y][$x - 2]),
	    (Right if @maze[$y][$x + 2]);
	@neighbors.pick or DeadEnd;
    }

    sub move ($dir, @cur) {
	my ($x,$y) = @cur;
	given $dir {
	    when Up    { @maze[--$y][$x] = OPEN; @maze[$y][$x-1] -= E; @maze[$y--][$x+1] -= W; }
	    when Down  { @maze[++$y][$x] = OPEN; @maze[$y][$x-1] -= E; @maze[$y++][$x+1] -= W; }
	    when Left  { @maze[$y][--$x] = OPEN; @maze[$y-1][$x] -= S; @maze[$y+1][$x--] -= N; }
	    when Right { @maze[$y][++$x] = OPEN; @maze[$y-1][$x] -= S; @maze[$y+1][$x++] -= N; }
	}
	@maze[$y][$x] = 0;
	[$x,$y];
    }
}

sub display (@maze) {
    for @maze -> @y {
	for @y.rotor(2) -> ($w, $c) {
	    print @ch[abs $w];
	    if $c >= 0 { print @ch[$c] x 3 }
	    else { print ' ', @ch[abs $c], ' ' }
	}
	say @ch[@y[*-1]];
    }
}

sub solve (@maze is copy, @from = [1, 1], @to = [@maze[0] - 2, @maze - 2]) {
    my ($x, $y) = @from;
    my ($xto, $yto) = @to;
    my @stack;

    sub drop-crumb($x,$y,$c) { @maze[$y][$x] = -$c }
    drop-crumb($x,$y,N);

    loop {
	my $dir = pick_direction([$x,$y]);
	if $dir {
	    ($x, $y) = move($dir, [$x,$y]);
	    return @maze if $x == $xto and $y == $yto;
	}
	else {
	    @maze[$y][$x] = -TRIED;
	    ($x,$y) = @stack.pop;
	    @maze[$y][$x] = -TRIED;
	    ($x,$y) = @stack.pop;
	}
    }

    sub pick_direction([$x,$y]) {
	my @neighbors =
	    (Up    unless @maze[$y - 1][$x]),
	    (Down  unless @maze[$y + 1][$x]),
	    (Left  unless @maze[$y][$x - 1]),
	    (Right unless @maze[$y][$x + 1]);
	@neighbors.pick or DeadEnd;
    }

    sub move ($dir, @cur) {
	my ($x,$y) = @cur;
	given $dir {
	    when Up    { for ^2 { push @stack, $[$x,$y--]; drop-crumb $x,$y,S; } }
	    when Down  { for ^2 { push @stack, $[$x,$y++]; drop-crumb $x,$y,N; } }
	    when Left  { for ^2 { push @stack, $[$x--,$y]; drop-crumb $x,$y,E; } }
	    when Right { for ^2 { push @stack, $[$x++,$y]; drop-crumb $x,$y,W; } }
	}
	$x,$y;
    }
}

display solve gen_maze( 29, 19 );


Red ["Maze solver"]

do %mazegen.red
print [
	"start:" start: random size - 1x1
	"end:" end: random size - 1x1
]
isnew?: function [pos] [not find visited pos]
open?: function [pos d] [
	o: pos/y * size/x + pos/x + 1
	0 = pick walls/:o d
]
expand: function [pos][
	either any [
		all [pos/x > 0 isnew? p: pos - 1x0 open? p 1]
		all [pos/x < (size/x - 1) isnew? p: pos + 1x0 open? pos 1]
		all [pos/y > 0 isnew? p: pos - 0x1 open? p 2]
		all [pos/y < (size/y - 1) isnew? p: pos + 0x1 open? pos 2]
	][append visited p insert path p][remove path]
	path/1
]
path: reduce [start]
visited: []
until [end = expand path/1]
print reverse path


class Maze
  # Solve via breadth-first algorithm.
  # Each queue entry is a path, that is list of coordinates with the
  # last coordinate being the one that shall be visited next.
  def solve

    # Clean up.
    reset_visiting_state

    # Enqueue start position.
    @queue = []
    enqueue_cell([], @start_x, @start_y)

    # Loop as long as there are cells to visit and no solution has
    # been found yet.
    path = nil
    until path || @queue.empty?
      path = solve_visit_cell
    end

    if path
      # Mark the cells that make up the shortest path.
      for x, y in path
        @path[x][y] = true
      end
    else
      puts "No solution found?!"
    end
  end

  private

  # Maze solving visiting method.
  def solve_visit_cell
    # Get the next path.
    path = @queue.shift
    # The cell to visit is the last entry in the path.
    x, y = path.last

    # Have we reached the end yet?
    return path  if x == @end_x && y == @end_y

    # Mark cell as visited.
    @visited[x][y] = true

    for dx, dy in DIRECTIONS
      if dx.nonzero?
        # Left / Right
        new_x = x + dx
        if move_valid?(new_x, y) && [email protected]_walls[ [x, new_x].min ][y]
          enqueue_cell(path, new_x, y)
        end
      else
        # Top / Bottom
        new_y = y + dy
        if move_valid?(x, new_y) && [email protected]_walls[x][ [y, new_y].min ]
          enqueue_cell(path, x, new_y)
        end
      end
    end

    nil         # No solution yet.
  end

  # Enqueue a new coordinate to visit.
  def enqueue_cell(path, x, y)
    # Add new coordinates to the current path and enqueue the new path.
    @queue << path + [[x, y]]
  end
end

# Demonstration:
maze = Maze.new 20, 10
maze.solve
maze.print


use rand::{thread_rng, Rng, rngs::ThreadRng};

const WIDTH: usize = 16;
const HEIGHT: usize = 16;

#[derive(Clone, Copy, PartialEq)]
struct Cell {
    col: usize,
    row: usize,
}

impl Cell {
    fn from(col: usize, row: usize) -> Cell {
        Cell {col, row}
    }
}

struct Maze {
    cells: [[bool; HEIGHT]; WIDTH],         //cell visited/non visited
    walls_h: [[bool; WIDTH]; HEIGHT + 1],   //horizontal walls existing/removed
    walls_v: [[bool; WIDTH + 1]; HEIGHT],   //vertical walls existing/removed
    thread_rng: ThreadRng,                  //Random numbers generator
}

impl Maze {

    ///Inits the maze, with all the cells unvisited and all the walls active
    fn new() -> Maze {
        Maze {
            cells: [[true; HEIGHT]; WIDTH],
            walls_h: [[true; WIDTH]; HEIGHT + 1],
            walls_v: [[true; WIDTH + 1]; HEIGHT],
            thread_rng: thread_rng(),
        }
    }

    ///Randomly chooses the starting cell
    fn first(&mut self) -> Cell {
        Cell::from(self.thread_rng.gen_range(0, WIDTH), self.thread_rng.gen_range(0, HEIGHT))
    }

    ///Opens the enter and exit doors
    fn open_doors(&mut self) {
        let from_top: bool = self.thread_rng.gen();
        let limit = if from_top { WIDTH } else { HEIGHT };
        let door = self.thread_rng.gen_range(0, limit);
        let exit = self.thread_rng.gen_range(0, limit);
        if from_top {
            self.walls_h[0][door] = false;
            self.walls_h[HEIGHT][exit] = false;
        } else {
            self.walls_v[door][0] = false;
            self.walls_v[exit][WIDTH] = false;
        }
    }

    ///Removes a wall between the two Cell arguments
    fn remove_wall(&mut self, cell1: &Cell, cell2: &Cell) {
        if cell1.row == cell2.row {
            self.walls_v[cell1.row][if cell1.col > cell2.col { cell1.col } else { cell2.col }] = false;
        } else {
            self.walls_h[if cell1.row > cell2.row { cell1.row } else { cell2.row }][cell1.col] = false;
        };
    }

    ///Returns a random non-visited neighbor of the Cell passed as argument
    fn neighbor(&mut self, cell: &Cell) -> Option<Cell> {
        self.cells[cell.col][cell.row] = false;
        let mut neighbors = Vec::new();
        if cell.col > 0 && self.cells[cell.col - 1][cell.row] { neighbors.push(Cell::from(cell.col - 1, cell.row)); }
        if cell.row > 0 && self.cells[cell.col][cell.row - 1] { neighbors.push(Cell::from(cell.col, cell.row - 1)); }
        if cell.col < WIDTH - 1 && self.cells[cell.col + 1][cell.row] { neighbors.push(Cell::from(cell.col + 1, cell.row)); }
        if cell.row < HEIGHT - 1 && self.cells[cell.col][cell.row + 1] { neighbors.push(Cell::from(cell.col, cell.row + 1)); }
        if neighbors.is_empty() {
            None
        } else {
            let next = neighbors.get(self.thread_rng.gen_range(0, neighbors.len())).unwrap();
            self.remove_wall(cell, next);
            Some(*next)
        }
    }

    ///Builds the maze (runs the Depth-first search algorithm)
    fn build(&mut self) {
        let mut cell_stack: Vec<Cell> = Vec::new();
        let mut next = self.first();
        loop {
            while let Some(cell) = self.neighbor(&next) {
                cell_stack.push(cell);
                next = cell;
            }
            match cell_stack.pop() {
                Some(cell) => next = cell,
                None => break,
            }
        }
    }

    ///MAZE SOLVING: Find the starting cell of the solution
    fn solution_first(&self) -> Option<Cell> {
        for (i, wall) in self.walls_h[0].iter().enumerate() {
            if !wall {
                return Some(Cell::from(i, 0));
            }
        }
        for (i, wall) in self.walls_v.iter().enumerate() {
            if !wall[0] {
                return Some(Cell::from(0, i));
            }
        }
        None
    }

    ///MAZE SOLVING: Find the last cell of the solution
    fn solution_last(&self) -> Option<Cell> {
        for (i, wall) in self.walls_h[HEIGHT].iter().enumerate() {
            if !wall {
                return Some(Cell::from(i, HEIGHT - 1));
            }
        }
        for (i, wall) in self.walls_v.iter().enumerate() {
            if !wall[WIDTH] {
                return Some(Cell::from(WIDTH - 1, i));
            }
        }
        None
    }

    ///MAZE SOLVING: Get the next candidate cell
    fn solution_next(&mut self, cell: &Cell) -> Option<Cell> {
        self.cells[cell.col][cell.row] = false;
        let mut neighbors = Vec::new();
        if cell.col > 0 && self.cells[cell.col - 1][cell.row] && !self.walls_v[cell.row][cell.col] { neighbors.push(Cell::from(cell.col - 1, cell.row)); }
        if cell.row > 0 && self.cells[cell.col][cell.row - 1] && !self.walls_h[cell.row][cell.col] { neighbors.push(Cell::from(cell.col, cell.row - 1)); }
        if cell.col < WIDTH - 1 && self.cells[cell.col + 1][cell.row] && !self.walls_v[cell.row][cell.col + 1] { neighbors.push(Cell::from(cell.col + 1, cell.row)); }
        if cell.row < HEIGHT - 1 && self.cells[cell.col][cell.row + 1] && !self.walls_h[cell.row + 1][cell.col] { neighbors.push(Cell::from(cell.col, cell.row + 1)); }
        if neighbors.is_empty() {
            None
        } else {
            let next = neighbors.get(self.thread_rng.gen_range(0, neighbors.len())).unwrap();
            Some(*next)
        }
    }

    ///MAZE SOLVING: solve the maze
    ///Uses self.cells to store the solution cells (true)
    fn solve(&mut self) {
        self.cells = [[true; HEIGHT]; WIDTH];
        let mut solution: Vec<Cell> = Vec::new();
        let mut next = self.solution_first().unwrap();
        solution.push(next);
        let last = self.solution_last().unwrap();
        'main: loop {
            while let Some(cell) = self.solution_next(&next) {
                solution.push(cell);
                if cell == last {
                    break 'main;
                }
                next = cell;
            }
            solution.pop().unwrap();
            next = *solution.last().unwrap();
        }
        self.cells = [[false; HEIGHT]; WIDTH];
        for cell in solution {
            self.cells[cell.col][cell.row] = true;
        }
    }

    ///MAZE SOLVING: Ask if cell is part of the solution (cells[col][row] == true)
    fn is_solution(&self, col: usize, row: usize) -> bool {
        self.cells[col][row]
    }

    ///Displays a wall
    ///MAZE SOLVING: Leave space for printing '*' if cell is part of the solution
    /// (only when painting vertical walls)
    ///
    // fn paint_wall(h_wall: bool, active: bool) {
    //     if h_wall {
    //         print!("{}", if active { "+---" } else { "+   " });
    //     } else {
    //         print!("{}", if active { "|   " } else { "    " });
    //     }
    // }
    fn paint_wall(h_wall: bool, active: bool, with_solution: bool) {
        if h_wall {
            print!("{}", if active { "+---" } else { "+   " });
        } else {
            print!("{}{}", if active { "|" } else { " " }, if with_solution { "" } else { "   " });
        }
    }

    ///MAZE SOLVING: Paint * if cell is part of the solution
    fn paint_solution(is_part: bool) {
        print!("{}", if is_part { " * " } else {"   "});
    }

    ///Displays a final wall for a row
    fn paint_close_wall(h_wall: bool) {
        if h_wall { println!("+") } else { println!() }
    }

    ///Displays a whole row of walls
    ///MAZE SOLVING: Displays a whole row of walls and, optionally, the included solution cells.
    // fn paint_row(&self, h_walls: bool, index: usize) {
    //     let iter = if h_walls { self.walls_h[index].iter() } else { self.walls_v[index].iter() };
    //     for &wall in iter {
    //         Maze::paint_wall(h_walls, wall);
    //     }
    //     Maze::paint_close_wall(h_walls);
    // }
    fn paint_row(&self, h_walls: bool, index: usize, with_solution: bool) {
        let iter = if h_walls { self.walls_h[index].iter() } else { self.walls_v[index].iter() };
        for (col, &wall) in iter.enumerate() {
            Maze::paint_wall(h_walls, wall, with_solution);
            if !h_walls && with_solution && col < WIDTH  {
                Maze::paint_solution(self.is_solution(col, index));
            }
        }
        Maze::paint_close_wall(h_walls);
    }

    ///Paints the maze
    ///MAZE SOLVING: Displaying the solution is an option
    // fn paint(&self) {
    //     for i in 0 .. HEIGHT {
    //         self.paint_row(true, i);
    //         self.paint_row(false, i);
    //     }
    //     self.paint_row(true, HEIGHT);
    // }
    fn paint(&self, with_solution: bool) {
        for i in 0 .. HEIGHT {
            self.paint_row(true, i, with_solution);
            self.paint_row(false, i, with_solution);
        }
        self.paint_row(true, HEIGHT, with_solution);
    }
}

fn main() {
    let mut maze = Maze::new();
    maze.build();
    maze.open_doors();

    println!("The maze:");
    maze.paint(false);

    maze.solve();
    println!("The maze, solved:");
    maze.paint(true);
}


enum errors: Error {
    //Occurs when a start or end position is given that is outside of bounds
    case IndexOutsideMazeBoundary
}
///Used to solve any maze generated by the swift implementation of maze generator at:
///https://www.rosettacode.org/wiki/Maze_generation#Swift
class MazeSolver {
    ///your starting index position in the maze
    var startPos: (Int, Int)
    /// the end index position you are trying to reach within the maze
    var endPos: (Int, Int)
    /// a maze of [[INT]] where each value is between 1 and 14.
    /// each number representing a
    var maze: [[Int]]
    var visitedSpaces: [(Int, Int)]

    init(maze: [[Int]]) {
        self.startPos = (0, 0)
        self.endPos = (0, 0)
        /// a maze that consists of a array of ints from 1-14 organized to
        /// create a maze like structure.
        /// Each number represents where the walls and paths will be
        /// ex. 2 only has a path south and 12 has paths E or W
        self.maze = maze
        /// spaces each branch has visited to stop from searching previously explored
        /// areas of the maze
        self.visitedSpaces = []
    }

    /// determine if the user has previously visited the given location within the maze
    func visited(position: (Int, Int)) -> Bool {
        return visitedSpaces.contains(where: {$0 == position})
    }

    /// used to translate the mazes number scheme to a list of directions available
    ///  for each number. DirectionDiff for N would be  (0, -1) for example
    func getDirections(positionValue: Int) -> [(Int, Int)] {

        switch positionValue {
        case 1:
            return [Direction.north.diff]
        case 2:
            return [Direction.south.diff]
        case 3:
            return [Direction.north.diff, Direction.south.diff]
        case 4:
            return [Direction.east.diff]
        case 5:
            return [Direction.north.diff, Direction.east.diff]
        case 6:
            return [Direction.south.diff, Direction.east.diff]
        case 7:
            return [Direction.north.diff, Direction.east.diff, Direction.south.diff]
        case 8:
            return [Direction.west.diff]
        case 9:
            return [Direction.north.diff, Direction.west.diff]
        case 10:
            return [Direction.west.diff, Direction.south.diff]
        case 11:
            return [Direction.north.diff, Direction.south.diff, Direction.west.diff]
        case 12:
            return [Direction.west.diff, Direction.east.diff]
        case 13:
            return [Direction.north.diff, Direction.east.diff, Direction.west.diff]
        case 14:
            return [Direction.east.diff, Direction.south.diff, Direction.west.diff]
        default:
            return []
        }
    }

    /// calculate and return all paths branching from the current position that haven't been explored yet
    func availablePaths(currentPosition: (Int, Int), lastPos: (Int, Int)) -> [(Int, Int)] {
        /// all available paths to be returned
        var paths: [(Int, Int)] = []
        /// the maze contents at the given position (ie the maze number)
        let positionValue = maze[currentPosition.0][ currentPosition.1]
        /// used to build the new path positions and check them before they
        /// are added to paths
        var workingPos: (Int, Int)

        // is the maze at a dead end and not our first position
        if ([1, 2, 4, 8].contains(positionValue) && currentPosition != lastPos) {
            return []
        }

        /// the directions available based on the maze contents of our
        /// current position
        let directions: [(Int, Int)] = getDirections(positionValue: positionValue)

        /// build the paths
        for pos in directions {
            workingPos = (currentPosition.0 + pos.0, currentPosition.1 + pos.1)

            if (currentPosition == lastPos || (workingPos != lastPos && !visited(position: workingPos))) {
                paths.append(workingPos)
            }
        }

        return paths
    }

    /// prints a model of the maze with
    /// O representing the start point
    /// X representing the end point
    /// * representing traveled space
    /// NOTE: starting pos and end pos represent indexes and thus start at 0
    func solveAndDisplay(startPos: (Int, Int), endPos: (Int, Int)) throws {
        if (startPos.0 >= maze.count || startPos.1 >= maze[0].count) {
            throw errors.IndexOutsideMazeBoundary
        }

        /// the position you are beginning at in the maze
        self.startPos = startPos
        /// the position you wish to get to within the maze
        self.endPos = endPos
        /// spaces each branch has visited to stop from searching previously explored
        /// areas of the maze
        self.visitedSpaces = []

        self.displaySolvedMaze(finalPath: solveMaze(startpos: startPos, pathSoFar: [])!)

    }

    /// recursively solves our maze
    private func solveMaze(startpos: (Int, Int), pathSoFar: [(Int, Int)]) -> [(Int, Int)]? {
        /// marks our current position in the maze
        var currentPos: (Int, Int) = startpos
        /// saves the spaces visited on this current branch
        var currVisitedSpaces : [(Int, Int)] = [startpos]
        /// the final path (or the path so far if the end pos has not been reached)
        /// from the start position to the end position
        var finalPath: [(Int, Int)] = pathSoFar

        /// all possible paths from our current position that have not been explored
        var possiblePaths: [(Int, Int)] = availablePaths(currentPosition: startpos, lastPos: pathSoFar.last ?? startpos)

        // move through the maze until you come to a position that has been explored, the end position, or a dead end (the
        // possible paths array is empty)
        while (!possiblePaths.isEmpty) {
            // found the final path
            guard (currentPos != self.endPos) else {
                finalPath.append(contentsOf: currVisitedSpaces)
                return finalPath
            }
            // multiple valid paths from current position found
            // recursively call new branches for each valid path
            if (possiblePaths.count > 1) {
                visitedSpaces.append(contentsOf: currVisitedSpaces)
                finalPath.append(contentsOf: currVisitedSpaces)
                // for each valid path recursively call each path
                // and if it contains the final path
                // ie it found the endpoint, return that path
                for pos in possiblePaths {
                    let path = solveMaze(startpos: pos, pathSoFar: finalPath)

                    if (path != nil) {
                        return path
                    }
                }
                // otherwise this branch and it's sub-branches
                // are dead-ends so kill this branch
                return nil
            }
            // continue moving along the only path available
            else {
                // update current path to our only available next
                // position
                currentPos = possiblePaths[0]

                // calculate the available paths from our new
                // current position
                possiblePaths = availablePaths(currentPosition: currentPos, lastPos: currVisitedSpaces.last ?? currentPos)
                // update the visited spaces for this branch
                currVisitedSpaces.append(currentPos)
            }
        }

        // if our new current position is the endpos return our final path
        if (currentPos == self.endPos) {
            finalPath.append(contentsOf: currVisitedSpaces)
            return finalPath
        }
        else {
            return nil
        }
    }

    // Reference: https://www.rosettacode.org/wiki/Maze_generation#Swift
    // adapts the display method used in the swift implementation of the maze generator we used for this app to display the maze
    // solved
    func displaySolvedMaze(finalPath: [(Int, Int)]) {
        /// all cells are 3 wide in the x axis
        let cellWidth = 3
        for j in 0..<y {
            // Draw top edge
            // if mark corner with +, add either (cell width) spaces or - to the topedge var dependin on if it is a wall or not
            var topEdge = ""
            for i in 0..<x {
                topEdge += "+"
                topEdge += String(repeating: (maze[i][j] & Direction.north.rawValue) == 0 ? "-" : " ", count: cellWidth)
            }
            topEdge += "+"
            print(topEdge)

            // Draw left edge
            //through the center of the cell if is a wall add |  if not a space
            // then if it is travelled add " * " otherwise just 3 spaces then
            //cap it with a | at the end for the right most wall
            var leftEdge = ""
            for i in 0..<x {
                leftEdge += (maze[i][j] & Direction.west.rawValue) == 0 ? "|" : " "

                if (finalPath.first! == (i, j)) {
                    leftEdge += " O "
                }
                else if (finalPath.last! == (i, j)) {
                    leftEdge += " X "
                }
                else {
                    if (finalPath.contains(where: {$0 == (i, j)})) {
                        leftEdge += " * "
                    }
                    else {
                        leftEdge += String(repeating: " ", count: cellWidth)
                    }
                }
            }
            leftEdge += "|"
            print(leftEdge)
        }

        // Draw bottom edge
        // adds + on corners and _ everywhere else
        var bottomEdge = ""
        for _ in 0..<x {
            bottomEdge += "+"
            bottomEdge += String(repeating: "-", count: cellWidth)
        }
        bottomEdge += "+"
        print(bottomEdge)
    }

}


oo::define maze {
    method solve {} {
	### Initialization of visited matrix and location/path queue
	set visited [lrepeat $x [lrepeat $y 0]]
	set queue {0 0 {}}

	### Loop to do the searching ###
	while 1 {
	    # Check for running out of path; an error in maze construction
	    if {[llength $queue] == 0} {
		error "cannot reach finish"
	    }
	    # Visit the next square from the queue
	    set queue [lassign $queue cx cy path]
	    if {[lindex $visited $cx $cy]} continue
	    lset visited $cx $cy 1
	    lappend path $cx $cy
	    # Check for reaching the goal
	    if {$cx == $x-1 && $cy == $y-1} break
	    # Add the square in each direction to the queue if a move there is legal
	    foreach {dx dy} {0 1  1 0  0 -1  -1 0} {
		set nx [expr {$cx + $dx}]; set ny [expr {$cy + $dy}]
		if {
		    $nx >= 0 && $nx < $x && $ny >= 0 && $ny < $y
		    && ($dx && idx($verti, min($cx,$nx), $cy) ||
			$dy && idx($horiz, $cx, min($cy,$ny)))
		} then {
		    lappend queue $nx $ny $path
		}
	    }
	}

	### Loop to set up the path rendering ###
	# (-2,-2) is just a marker that isn't next to the maze at all, so
	# guaranteeing the use of the last 'else' clause
	foreach {cx cy} $path {nx ny} [concat [lrange $path 2 end] -2 -2] {
	    if {$nx-$cx == 1} {
		lset content $cx $cy "v"
	    } elseif {$nx-$cx == -1} {
		lset content $cx $cy "^"
	    } elseif {$ny-$cy == -1} {
		lset content $cx $cy "<"
	    } else {
		lset content $cx $cy ">"
	    }
	}

	### Return the path ###
	return $path
    }
}

# Do the solution (we ignore the returned path here...)
m solve
# Print it out
puts [m view]


import "os" for Process
import "/ioutil" for File, FileUtil

/*
    * Makes the maze half as wide (i. e. "+---+" becomes "+-+"), so that
    * each cell in the maze is the same size horizontally as vertically.
    * (Versus the expanded version, which looks better visually.)
    * Also, converts each line of the maze from a string to a
    * character list, because we'll want mutability when drawing the solution later.
*/
var decimateHorizontally = Fn.new { |lines|
    var width = ((lines[0].count + 1)/2).floor
    var c = List.filled(lines.count, null)
    for (i in 0...lines.count) {
        c[i] = List.filled(width, null)
        for (j in 0...width) c[i][j] = lines[i][j*2]
    }
    return c
}

/*
    * Given the maze, the x and y coordinates (which must be odd),
    * and the direction we came from, return true if the maze is
    * solvable, and draw the solution if so.
*/
var solveMazeRecursively  // recursive function
solveMazeRecursively = Fn.new { |maze, x, y, d|
    var ok = false
    var i = 0
    while (i < 4 && !ok) {
        if (i != d) {
            // 0 = up, 1 = right, 2 = down, 3 = left
            if (i == 0) {
                if (maze[y - 1][x] == " ") ok = solveMazeRecursively.call(maze, x, y - 2, 2)
            } else if (i == 1) {
                if (maze[y][x + 1] == " ") ok = solveMazeRecursively.call(maze, x + 2, y, 3)
            } else if (i == 2) {
                if (maze[y + 1][x] == " ") ok = solveMazeRecursively.call(maze, x, y + 2, 0)
            } else if (i == 3) {
                if (maze[y][x - 1] == " ") ok = solveMazeRecursively.call(maze, x - 2, y, 1)
            }
        }
        i = i + 1
    }

    // check for end condition
    if (x == 1 && y == 1) ok = true

    // once we have found a solution, draw it as we unwind the recursion
    if (ok) {
        maze[y][x] = "*"
        if (d == 0) {
            maze[y - 1][x] = "*"
        } else if (d == 1) {
            maze[y][x + 1] = "*"
        } else if (d == 2) {
            maze[y + 1][x] = "*"
        } else if (d == 3) {
            maze[y][x - 1] = "*"
        }
    }
    return ok
}

/*
    * Solve the maze and draw the solution. For simplicity,
    * assumes the starting point is the lower right, and the
    * ending point is the upper left.
*/
var solveMaze = Fn.new { |maze|
    solveMazeRecursively.call(maze, maze[0].count - 2, maze.count - 2, -1)
}

/*
    * Opposite of decimateHorizontally(). Adds extra characters to make
    * the maze "look right", and converts each line from a char list to a
    * string at the same time.
*/
var expandHorizontally = Fn.new { |maze|
    var tmp = List.filled(3, " ")
    var lines = List.filled(maze.count, null)
    for (i in 0...maze.count) {
        var sb = ""
        for (j in 0...maze[i].count) {
            if (j % 2 == 0) {
                sb = sb + maze[i][j]
            } else {
                for (k in 0..2) tmp[k] = maze[i][j]
                if (tmp[1] == "*") {
                    tmp[0] = " "
                    tmp[2] = " "
                }
                sb = sb + tmp.join()
            }
        }
        lines[i] = sb
    }
    return lines
}

/*
    * Accepts a maze as generated by:
    * http://rosettacode.org/wiki/Maze_generation#Wren
    * in a file whose name is specified as a command-line argument.
*/
var args = Process.arguments
if (args.count != 1) {
    System.print("The maze file to be read should be passed as a single command line argument.")
    return
}
if (!File.exists(args[0])) {
    System.print("Sorry %(args[0]) does not exist.")
    return
}
var lines = FileUtil.readLines(args[0]).where { |l| l != "" }.toList
var maze = decimateHorizontally.call(lines)
solveMaze.call(maze)
var solvedLines = expandHorizontally.call(maze)
System.print(solvedLines.join("\n"))


ver,hor:=make_maze();  // see above link for this code

fcn munge(a,b){ String(a[0,2],b,a[3,*]) } // "+---" --> "+-*-"

fcn solveMaze(ver,hor, a,b, u,v, w,h){
   if (a==u and b==v) return(True);

   sh:=hor[b][a]; sv:=ver[b][a];
   hor[b][a]=munge(sh,"*"); ver[b][a]=munge(sv,"*");   // drop breadcrumb

   foreach dx,dy in (T( T(0,-1),T(1,0),T(0,1),T(-1,0) )){
      x:=a+dx; y:=b+dy; hy:=hor[y]; vy:=ver[y];
      if ( (0<=x<w) and (0<=y<h) and // (x,y) in bounds
	   (dx==0 or (dx== 1 and vy[x]=="    ") or   // horizontal
		     (dx==-1 and vy[a][0]==" " and vy[x][2]!="*")) and
	   (dy==0 or (dy== 1 and hy[x]=="+   ") or   // vertical
		     (dy==-1 and hor[b][a][1]==" " and hy[x][2]!="*"))
         )
      {
	 sh:=hy[x];
	 if(solveMaze(ver,hor, x,y, u,v, w,h)){
	    hy[x]=sh; // remove splat on wall but not floor while backing out
	    return(True);
	 }
      }
   }
   hor[b][a]=sh; ver[b][a]=sv;  // pick up breadcrumb when backtracking
   return(False);
}

w:=(hor[0].len() - 1); h:=(hor.len() - 1);
startx:=(0).random(w); starty:=(0).random(h);
endx  :=(0).random(w); endy  :=(0).random(h);

sh:=hor[starty][startx];
if (not solveMaze(ver,hor, startx,starty, endx,endy, w,h))
   println("No solution path found.");
else{
   hor[starty][startx]=sh;
   ver[starty][startx]=munge(ver[starty][startx],"S");
   ver[endy][endx]    =munge(ver[endy][endx],"E");
   foreach a,b in (hor.zip(ver)) { println(a.concat(),"\n",b.concat()) }
}


-V
   rotate_amounts = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
                     5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
                     4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
                     6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]

   constants = (0.<64).map(i -> UInt32(UInt64(abs(sin(i + 1)) * 2.0 ^ 32) [&] FFFF'FFFF))

   init_values = (UInt32(6745'2301), UInt32(EFCD'AB89), UInt32(98BA'DCFE), UInt32(1032'5476))

[((UInt32, UInt32, UInt32) -> UInt32)] functions
functions [+]= (b, c, d) -> (b [&] c) [|] ((-)b [&] d)
functions [+]= (b, c, d) -> (d [&] b) [|] ((-)d [&] c)
functions [+]= (b, c, d) -> b (+) c (+) d
functions [+]= (b, c, d) -> c (+) (b [|] (-)d)

[(Int -> Int)] index_functions
index_functions [+]= i -> i
index_functions [+]= i -> (5 * i + 1) % 16
index_functions [+]= i -> (3 * i + 5) % 16
index_functions [+]= i -> (7 * i) % 16

F md5(=message)
   V orig_len_in_bits = UInt64(8) * message.len
   message.append(8'0)
   L message.len % 64 != 56
      message.append(0)
   message.extend(bytes_from_int(orig_len_in_bits))

   V hash_pieces = init_values

   L(chunk_ofst) (0 .< message.len).step(64)
      V (a, b, c, d) = hash_pieces
      V chunk = message[chunk_ofst .+ 64]
      L(i) 64
         V f = :functions[i I/ 16](b, c, d)
         V g = :index_functions[i I/ 16](i)
         V to_rotate = a + f + :constants[i] + UInt32(bytes' chunk[4 * g .+ 4])
         V new_b = UInt32(b + rotl(to_rotate, :rotate_amounts[i]))
         (a, b, c, d) = (d, new_b, b, c)
      L(val) (a, b, c, d)
         hash_pieces[L.index] += val

   [Byte] r
   L(x) hash_pieces
      r.extend([x [&] F'F, (x >> 8) [&] F'F, (x >> 16) [&] F'F, (x >> 24) [&] F'F])
   R r

F md5_to_hex(digest)
   V s = ‘’
   L(d) digest
      s ‘’= hex(d).lowercase().zfill(2)
   R s

V demo = [Bytes(‘’), Bytes(‘a’), Bytes(‘abc’), Bytes(‘message digest’), Bytes(‘abcdefghijklmnopqrstuvwxyz’),
          Bytes(‘ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789’),
          Bytes(‘12345678901234567890123456789012345678901234567890123456789012345678901234567890’)]
L(message) demo
   print(md5_to_hex(md5(message))‘ <= "’message.decode(‘ascii’)‘"’)


package MD5 is

   type Int32 is mod 2 ** 32;
   type MD5_Hash is array (1 .. 4) of Int32;
   function MD5 (Input : String) return MD5_Hash;

   -- 32 hexadecimal characters + '0x' prefix
   subtype MD5_String is String (1 .. 34);
   function To_String (Item : MD5_Hash) return MD5_String;

end MD5;


      PRINT FN_MD5("")
      PRINT FN_MD5("a")
      PRINT FN_MD5("abc")
      PRINT FN_MD5("message digest")
      PRINT FN_MD5("abcdefghijklmnopqrstuvwxyz")
      PRINT FN_MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
      PRINT FN_MD5(STRING$(8,"1234567890"))
      END

      DEF FN_MD5(message$)
      LOCAL a%, b%, c%, d%, f%, g%, h0%, h1%, h2%, h3%, i%, bits%, chunk%, temp%
      LOCAL r&(), k%(), w%()
      DIM r&(63), k%(63), w%(15)

      REM r specifies the per-round shift amounts:
      r&() = 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, \
      \      5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, \
      \      4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, \
      \      6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21

      REM Use binary integer part of the sines of integers (Radians) as constants:
      FOR i% = 0 TO 63
        k%(i%) = FN32(INT(ABS(SIN(i% + 1.0#)) * 2^32))
      NEXT

      REM Initialize variables:
      h0% = &67452301
      h1% = &EFCDAB89
      h2% = &98BADCFE
      h3% = &10325476

      bits% = LEN(message$)*8

      REM Append '1' bit to message:
      message$ += CHR$&80

      REM Append '0' bits until message length in bits = 448 (mod 512):
      WHILE (LEN(message$) MOD 64) <> 56
        message$ += CHR$0
      ENDWHILE

      REM Append length of message (before pre-processing), in bits, as
      REM 64-bit little-endian integer:
      FOR i% = 0 TO 56 STEP 8
        message$ += CHR$(bits% >>> i%)
      NEXT

      REM Process the message in successive 512-bit chunks:
      FOR chunk% = 0 TO LEN(message$) DIV 64 - 1

        REM Break chunk into sixteen 32-bit little-endian words:
        FOR i% = 0 TO 15
          w%(i%) = !(PTR(message$) + 64*chunk% + 4*i%)
        NEXT i%

        REM Initialize hash value for this chunk:
        a% = h0%
        b% = h1%
        c% = h2%
        d% = h3%

        REM Main loop:
        FOR i% = 0 TO 63
          CASE TRUE OF
            WHEN i% <= 15:
              f% = d% EOR (b% AND (c% EOR d%))
              g% = i%
            WHEN 16 <= i% AND i% <= 31:
              f% = c% EOR (d% AND (b% EOR c%))
              g% = (5 * i% + 1) MOD 16
            WHEN 32 <= i% AND i% <= 47:
              f% = b% EOR c% EOR d%
              g% = (3 * i% + 5) MOD 16
            OTHERWISE:
              f% = c% EOR (b% OR (NOT d%))
              g% = (7 * i%) MOD 16
          ENDCASE

          temp% = d%
          d% = c%
          c% = b%
          b% = FN32(b% + FNlrot(FN32(a% + f%) + FN32(k%(i%) + w%(g%)), r&(i%)))
          a% = temp%

        NEXT i%

        REM Add this chunk's hash to result so far:
        h0% = FN32(h0% + a%)
        h1% = FN32(h1% + b%)
        h2% = FN32(h2% + c%)
        h3% = FN32(h3% + d%)

      NEXT chunk%

      = FNrevhex(h0%) + FNrevhex(h1%) + FNrevhex(h2%) + FNrevhex(h3%)

      DEF FNrevhex(A%)
      SWAP ?(^A%+0),?(^A%+3)
      SWAP ?(^A%+1),?(^A%+2)
      = RIGHT$("0000000"+STR$~A%,8)

      DEF FNlrot(n#, r%)
      n# = FN32(n#)
      = (n# << r%) OR (n# >>> (32 - r%))

      DEF FN32(n#)
      WHILE n# > &7FFFFFFF : n# -= 2^32 : ENDWHILE
      WHILE n# < &80000000 : n# += 2^32 : ENDWHILE
      = n#


	/// Represent digest with ABCD
	sealed public class Digest
	{
		public uint A;
		public uint B;
		public uint C;
		public uint D;

		public Digest()
		{
			A=(uint)MD5InitializerConstant.A;
			B=(uint)MD5InitializerConstant.B;
			C=(uint)MD5InitializerConstant.C;
			D=(uint)MD5InitializerConstant.D;
       	        }

		public override string ToString()
		{
			string st ;
			st= MD5Helper.ReverseByte(A).ToString("X8")+
			    MD5Helper.ReverseByte(B).ToString("X8")+
                            MD5Helper.ReverseByte(C).ToString("X8")+
			    MD5Helper.ReverseByte(D).ToString("X8");
			return st;

		}
	}

	public class MD5
	{
		/***********************VARIABLES************************************/

		/***********************Statics**************************************/
		/// <summary>
		/// lookup table 4294967296*sin(i)
		/// </summary>
		protected readonly static uint []  T =new uint[64]
			{	0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
				0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
                0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
                0x6b901122,0xfd987193,0xa679438e,0x49b40821,
				0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
                0xd62f105d,0x2441453,0xd8a1e681,0xe7d3fbc8,
                0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
				0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
                0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
                0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
                0x289b7ec6,0xeaa127fa,0xd4ef3085,0x4881d05,
				0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
                0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
                0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
                0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
				0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};

		/*****instance variables**************/
		/// <summary>
		/// X used to proces data in
		///	512 bits chunks as 16 32 bit word
		/// </summary>
		protected  uint [] X = new uint [16];

		/// <summary>
		/// the finger print obtained.
		/// </summary>
		protected Digest dgFingerPrint;

		/// <summary>
		/// the input bytes
		/// </summary>
		protected	byte [] m_byteInput;

		/**********************EVENTS AND DELEGATES*******************************************/

		public delegate void ValueChanging (object sender,MD5ChangingEventArgs Changing);
		public delegate void ValueChanged (object sender,MD5ChangedEventArgs Changed);

		public event ValueChanging OnValueChanging;
		public event ValueChanged  OnValueChanged;

		/********************************************************************/
		/***********************PROPERTIES ***********************/
		/// <summary>
		///gets or sets as string
		/// </summary>
		public string Value
		{
			get
			{
				string st ;
				char [] tempCharArray= new Char[m_byteInput.Length];

				for(int i =0; i<m_byteInput.Length;i++)
					tempCharArray[i]=(char)m_byteInput[i];

				st= new String(tempCharArray);
				return st;
			}
			set
			{
				/// raise the event to notify the changing
				if (this.OnValueChanging !=null)
					this.OnValueChanging(this,new MD5ChangingEventArgs(value));

				m_byteInput=new byte[value.Length];
				for (int i =0; i<value.Length;i++)
					m_byteInput[i]=(byte)value[i];
				dgFingerPrint=CalculateMD5Value();

				/// raise the event to notify the change
				if (this.OnValueChanged !=null)
					this.OnValueChanged(this,new MD5ChangedEventArgs(value,dgFingerPrint.ToString()));

			}
		}

		/// <summary>
		/// get/sets as  byte array
		/// </summary>
		public byte [] ValueAsByte
		{
			get
			{
				byte [] bt = new byte[m_byteInput.Length];
				for (int i =0; i<m_byteInput.Length;i++)
					bt[i]=m_byteInput[i];
				return bt;
          }
			set
			{
				/// raise the event to notify the changing
				if (this.OnValueChanging !=null)
					this.OnValueChanging(this,new MD5ChangingEventArgs(value));

				m_byteInput=new byte[value.Length];
				for (int i =0; i<value.Length;i++)
					m_byteInput[i]=value[i];
				dgFingerPrint=CalculateMD5Value();

				/// notify the changed  value
				if (this.OnValueChanged !=null)
					this.OnValueChanged(this,new MD5ChangedEventArgs(value,dgFingerPrint.ToString()));
			}
		}

		//gets the signature/figner print as string
		public  string FingerPrint
		{
			get
			{
				return dgFingerPrint.ToString();
			}
		}

		/*************************************************************************/
		/// <summary>
		/// Constructor
		/// </summary>
		public MD5()
		{
			Value="";
		}

		/******************************************************************************/
		/*********************METHODS**************************/

		/// <summary>
		/// calculat md5 signature of the string in Input
		/// </summary>
		/// <returns> Digest: the finger print of msg</returns>
		protected Digest CalculateMD5Value()
		{
			/***********vairable declaration**************/
			byte [] bMsg;	//buffer to hold bits
			uint N;			//N is the size of msg as  word (32 bit)
			Digest dg =new Digest();			//  the value to be returned

			// create a buffer with bits padded and length is alos padded
			bMsg=CreatePaddedBuffer();

			N=(uint)(bMsg.Length*8)/32;		//no of 32 bit blocks

			for (uint  i=0; i<N/16;i++)
			{
				CopyBlock(bMsg,i);
				PerformTransformation(ref dg.A,ref dg.B,ref dg.C,ref dg.D);
			}
			return dg;
		}

		/********************************************************
		 * TRANSFORMATIONS :  FF , GG , HH , II  acc to RFC 1321
		 * where each Each letter represnets the aux function used
		 *********************************************************/

		/// <summary>
		/// perform transformatio using f(((b&c) | (~(b)&d))
		/// </summary>
		protected void TransF(ref uint a, uint b, uint c, uint d,uint k,ushort s, uint i )
		{
			a = b + MD5Helper.RotateLeft((a + ((b&c) | (~(b)&d)) + X[k] + T[i-1]), s);
		}

		/// <summary>
		/// perform transformatio using g((b&d) | (c & ~d) )
		/// </summary>
		protected void TransG(ref uint a, uint b, uint c, uint d,uint k,ushort s, uint i )
		{
			a = b + MD5Helper.RotateLeft((a + ((b&d) | (c & ~d) ) + X[k] + T[i-1]), s);
		}

		/// <summary>
		/// perform transformatio using h(b^c^d)
		/// </summary>
		protected void TransH(ref uint a, uint b, uint c, uint d,uint k,ushort s, uint i )
		{
			a = b + MD5Helper.RotateLeft((a + (b^c^d) + X[k] + T[i-1]), s);
		}

		/// <summary>
		/// perform transformatio using i (c^(b|~d))
		/// </summary>
		protected void TransI(ref uint a, uint b, uint c, uint d,uint k,ushort s, uint i )
		{
			a = b + MD5Helper.RotateLeft((a + (c^(b|~d))+ X[k] + T[i-1]), s);
		}

		/// <summary>
		/// Perform All the transformation on the data
		/// </summary>
		/// <param name="A">A</param>
		/// <param name="B">B </param>
		/// <param name="C">C</param>
		/// <param name="D">D</param>
		protected void PerformTransformation(ref uint A,ref uint B,ref uint C, ref uint D)
		{
			//// saving  ABCD  to be used in end of loop

			uint AA,BB,CC,DD;

			AA=A;
			BB=B;
			CC=C;
			DD=D;

			/* Round 1
				* [ABCD  0  7  1]  [DABC  1 12  2]  [CDAB  2 17  3]  [BCDA  3 22  4]
				* [ABCD  4  7  5]  [DABC  5 12  6]  [CDAB  6 17  7]  [BCDA  7 22  8]
				* [ABCD  8  7  9]  [DABC  9 12 10]  [CDAB 10 17 11]  [BCDA 11 22 12]
				* [ABCD 12  7 13]  [DABC 13 12 14]  [CDAB 14 17 15]  [BCDA 15 22 16]
				*  * */
			TransF(ref A,B,C,D,0,7,1);TransF(ref D,A,B,C,1,12,2);TransF(ref C,D,A,B,2,17,3);TransF(ref B,C,D,A,3,22,4);
			TransF(ref A,B,C,D,4,7,5);TransF(ref D,A,B,C,5,12,6);TransF(ref C,D,A,B,6,17,7);TransF(ref B,C,D,A,7,22,8);
			TransF(ref A,B,C,D,8,7,9);TransF(ref D,A,B,C,9,12,10);TransF(ref C,D,A,B,10,17,11);TransF(ref B,C,D,A,11,22,12);
			TransF(ref A,B,C,D,12,7,13);TransF(ref D,A,B,C,13,12,14);TransF(ref C,D,A,B,14,17,15);TransF(ref B,C,D,A,15,22,16);
			/** rOUND 2
				**[ABCD  1  5 17]  [DABC  6  9 18]  [CDAB 11 14 19]  [BCDA  0 20 20]
				*[ABCD  5  5 21]  [DABC 10  9 22]  [CDAB 15 14 23]  [BCDA  4 20 24]
				*[ABCD  9  5 25]  [DABC 14  9 26]  [CDAB  3 14 27]  [BCDA  8 20 28]
				*[ABCD 13  5 29]  [DABC  2  9 30]  [CDAB  7 14 31]  [BCDA 12 20 32]
			*/
			TransG(ref A,B,C,D,1,5,17);TransG(ref D,A,B,C,6,9,18);TransG(ref C,D,A,B,11,14,19);TransG(ref B,C,D,A,0,20,20);
			TransG(ref A,B,C,D,5,5,21);TransG(ref D,A,B,C,10,9,22);TransG(ref C,D,A,B,15,14,23);TransG(ref B,C,D,A,4,20,24);
			TransG(ref A,B,C,D,9,5,25);TransG(ref D,A,B,C,14,9,26);TransG(ref C,D,A,B,3,14,27);TransG(ref B,C,D,A,8,20,28);
			TransG(ref A,B,C,D,13,5,29);TransG(ref D,A,B,C,2,9,30);TransG(ref C,D,A,B,7,14,31);TransG(ref B,C,D,A,12,20,32);
			/*  rOUND 3
				* [ABCD  5  4 33]  [DABC  8 11 34]  [CDAB 11 16 35]  [BCDA 14 23 36]
				* [ABCD  1  4 37]  [DABC  4 11 38]  [CDAB  7 16 39]  [BCDA 10 23 40]
				* [ABCD 13  4 41]  [DABC  0 11 42]  [CDAB  3 16 43]  [BCDA  6 23 44]
				* [ABCD  9  4 45]  [DABC 12 11 46]  [CDAB 15 16 47]  [BCDA  2 23 48]
			 * */
			TransH(ref A,B,C,D,5,4,33);TransH(ref D,A,B,C,8,11,34);TransH(ref C,D,A,B,11,16,35);TransH(ref B,C,D,A,14,23,36);
			TransH(ref A,B,C,D,1,4,37);TransH(ref D,A,B,C,4,11,38);TransH(ref C,D,A,B,7,16,39);TransH(ref B,C,D,A,10,23,40);
			TransH(ref A,B,C,D,13,4,41);TransH(ref D,A,B,C,0,11,42);TransH(ref C,D,A,B,3,16,43);TransH(ref B,C,D,A,6,23,44);
			TransH(ref A,B,C,D,9,4,45);TransH(ref D,A,B,C,12,11,46);TransH(ref C,D,A,B,15,16,47);TransH(ref B,C,D,A,2,23,48);
			/*ORUNF  4
				*[ABCD  0  6 49]  [DABC  7 10 50]  [CDAB 14 15 51]  [BCDA  5 21 52]
				*[ABCD 12  6 53]  [DABC  3 10 54]  [CDAB 10 15 55]  [BCDA  1 21 56]
				*[ABCD  8  6 57]  [DABC 15 10 58]  [CDAB  6 15 59]  [BCDA 13 21 60]
				*[ABCD  4  6 61]  [DABC 11 10 62]  [CDAB  2 15 63]  [BCDA  9 21 64]
						 * */
			TransI(ref A,B,C,D,0,6,49);TransI(ref D,A,B,C,7,10,50);TransI(ref C,D,A,B,14,15,51);TransI(ref B,C,D,A,5,21,52);
			TransI(ref A,B,C,D,12,6,53);TransI(ref D,A,B,C,3,10,54);TransI(ref C,D,A,B,10,15,55);TransI(ref B,C,D,A,1,21,56);
			TransI(ref A,B,C,D,8,6,57);TransI(ref D,A,B,C,15,10,58);TransI(ref C,D,A,B,6,15,59);TransI(ref B,C,D,A,13,21,60);
			TransI(ref A,B,C,D,4,6,61);TransI(ref D,A,B,C,11,10,62);TransI(ref C,D,A,B,2,15,63);TransI(ref B,C,D,A,9,21,64);

			A=A+AA;
			B=B+BB;
			C=C+CC;
			D=D+DD;

		}

		/// <summary>
		/// Create Padded buffer for processing , buffer is padded with 0 along
		/// with the size in the end
		/// </summary>
		/// <returns>the padded buffer as byte array</returns>
		protected byte[] CreatePaddedBuffer()
		{
			uint pad;		//no of padding bits for 448 mod 512
			byte [] bMsg;	//buffer to hold bits
			ulong sizeMsg;		//64 bit size pad
			uint sizeMsgBuff;	//buffer size in multiple of bytes
			int temp=(448-((m_byteInput.Length*8)%512)); //temporary

			pad = (uint )((temp+512)%512);		//getting no of bits to  be pad
			if (pad==0)				///pad is in bits
				pad=512;			//at least 1 or max 512 can be added

			sizeMsgBuff= (uint) ((m_byteInput.Length)+ (pad/8)+8);
			sizeMsg=(ulong)m_byteInput.Length*8;
			bMsg=new byte[sizeMsgBuff];	///no need to pad with 0 coz new bytes
			// are already initialize to 0 :)

			////copying string to buffer
			for (int i =0; i<m_byteInput.Length;i++)
				bMsg[i]=m_byteInput[i];

			bMsg[m_byteInput.Length]|=0x80;		///making first bit of padding 1,

			//wrting the size value
			for (int i =8; i >0;i--)
				bMsg[sizeMsgBuff-i]=(byte) (sizeMsg>>((8-i)*8) & 0x00000000000000ff);

			return bMsg;
		}

		/// <summary>
		/// Copies a 512 bit block into X as 16 32 bit words
		/// </summary>
		/// <param name="bMsg"> source buffer</param>
		/// <param name="block">no of block to copy starting from 0</param>
		protected void CopyBlock(byte[] bMsg,uint block)
		{

			block=block<<6;
			for (uint j=0; j<61;j+=4)
			{
				X[j>>2]=(((uint) bMsg[block+(j+3)]) <<24 ) |
						(((uint) bMsg[block+(j+2)]) <<16 ) |
						(((uint) bMsg[block+(j+1)]) <<8 ) |
						(((uint) bMsg[block+(j)]) ) ;

			}
		}
	}


# Array sum helper function.
sum = (array) ->
  array.reduce (x, y) -> x + y

md5 = do ->
  # Per-round shift amounts.
  s = [738695, 669989, 770404, 703814]
  s = (s[i >> 4] >> i % 4 * 5 & 31 for i in [0..63])

  # Constants cache generated by sine.
  K = (Math.floor 2**32 * Math.abs Math.sin i for i in [1..64])

  # Bitwise left rotate helper function.
  lrot = (x, y) ->
    x << y | x >>> 32 - y;

  (input) ->
    # Initialize values.
    d0 = 0x10325476;
    a0 = 0x67452301;
    b0 = ~d0
    c0 = ~a0;

    # Convert the message to 32-bit words, little-endian.
    M =
      for i in [0...input.length] by 4
        sum (input.charCodeAt(i + j) << j*8 for j in [0..3])

    # Pre-processing: append a 1 bit, then message length % 2^64.
    len = input.length * 8
    M[len >> 5] |= 128 << len % 32
    M[(len + 64 >>> 9 << 4) + 14] = len

    # Process the message in chunks of 16 32-bit words.
    for x in [0...M.length] by 16
      [A, B, C, D] = [a0, b0, c0, d0]

      # Main loop.
      for i in [0..63]
        if i < 16
          F = B & C | ~B & D
          g = i
        else if i < 32
          F = B & D | C & ~D
          g = i * 5 + 1
        else if i < 48
          F = B ^ C ^ D
          g = i * 3 + 5
        else
          F = C ^ (B | ~D)
          g = i * 7

        [A, B, C, D] =
          [D, B + lrot(A + F + K[i] + (M[x + g % 16] ? 0), s[i]), B, C]

      a0 += A
      b0 += B
      c0 += C
      d0 += D

    # Convert the four words back to a string.
    return (
      for x in [a0, b0, c0, d0]
        (String.fromCharCode x >>> 8 * y & 255 for y in [0..3]).join ''
    ).join ''


(defpackage #:md5
  (:use #:cl))

(in-package #:md5)

(require :babel)

(deftype word () '(unsigned-byte 32))
(deftype octet () '(unsigned-byte 8))
(deftype octets () '(vector octet))

(defparameter *s*
  (make-array 16 :element-type 'word
                 :initial-contents '(7 12 17 22
                                     5  9 14 20
                                     4 11 16 23
                                     6 10 15 21)))

(defun s (i)
  (declare ((integer 0 63) i))
  (aref *s* (+ (ash (ash i -4) 2)
               (ldb (byte 2 0) i))))

(defparameter *k*
  (loop with result = (make-array 64 :element-type 'word)
        for i from 0 below 64
        do (setf (aref result i) (floor (* (ash 1 32) (abs (sin (1+ (float i 1d0)))))))
        finally (return result)))

(defun wrap (bits integer)
  (declare (fixnum bits) (integer integer))
  (ldb (byte bits 0) integer))

(defun integer->8octets (integer)
  (declare (integer integer))
  (loop for n = (wrap 64 integer) then (ash n -8)
        repeat 8
        collect (wrap 8 n)))

(defun pad-octets (octets)
  (declare (octets octets))
  (let* ((octets-length (length octets))
         (zero-pad-length (- 64 (mod (+ octets-length 9) 64)))
         (zero-pads (loop repeat zero-pad-length collect 0)))
    (concatenate 'octets octets '(#x80) zero-pads (integer->8octets (* 8 octets-length)))))

(defun octets->words (octets)
  (declare (octets octets))
  (loop with result = (make-array (/ (length octets) 4) :element-type 'word)
        for n from 0 below (length octets) by 4
        for i from 0
        do (setf (aref result i)
                 (dpb (aref octets (+ n 3)) (byte 8 24)
                      (dpb (aref octets (+ n 2)) (byte 8 16)
                           (dpb (aref octets (1+ n)) (byte 8 8)
                                (dpb (aref octets n) (byte 8 0) 0)))))
        finally (return result)))

(defun words->octets (&rest words)
  (loop for word of-type word in words
        collect (ldb (byte 8 0)  word)
        collect (ldb (byte 8 8)  word)
        collect (ldb (byte 8 16) word)
        collect (ldb (byte 8 24) word)))

(defun left-rotate (x c)
  (declare (integer x) (fixnum c))
  (let ((x (wrap 32 x)))
    (wrap 32 (logior (ash x c)
                     (ash x (- c 32))))))

(defun md5 (string)
  (declare (string string))
  (loop with m = (octets->words (pad-octets (babel:string-to-octets string)))
        with a0 of-type word = #x67452301
        with b0 of-type word = #xefcdab89
        with c0 of-type word = #x98badcfe
        with d0 of-type word = #x10325476
        for j from 0 below (length m) by 16
        do (loop for a of-type word = a0 then d
                 and b of-type word = b0 then new-b
                 and c of-type word = c0 then b
                 and d of-type word = d0 then c
                 for i from 0 below 64
                 for new-b = (multiple-value-bind (f g)
                                 (ecase (ash i -4)
                                   (0 (values (wrap 32 (logior (logand b c)
                                                               (logand (lognot b) d)))
                                              i))
                                   (1 (values (wrap 32 (logior (logand d b)
                                                               (logand (lognot d) c)))
                                              (wrap 4 (1+ (* 5 i)))))
                                   (2 (values (wrap 32 (logxor b c d))
                                              (wrap 4 (+ (* 3 i) 5))))
                                   (3 (values (wrap 32 (logxor c
                                                               (logior b (lognot d))))
                                              (wrap 4 (* 7 i)))))
                               (declare (word f g))
                               (wrap 32 (+ b (left-rotate (+ a f (aref *k* i) (aref m (+ j g)))
                                                          (s i)))))
                 finally (setf a0 (wrap 32 (+ a0 a))
                               b0 (wrap 32 (+ b0 b))
                               c0 (wrap 32 (+ c0 c))
                               d0 (wrap 32 (+ d0 d))))
        finally (return (with-output-to-string (s)
                          (dolist (o (words->octets a0 b0 c0 d0))
                            (format s "~(~2,'0X~)" o))))))

(defun test-cases ()
  (assert (string= "d41d8cd98f00b204e9800998ecf8427e"
                   (md5 "")))
  (assert (string= "0cc175b9c0f1b6a831c399e269772661"
                   (md5 "a")))
  (assert (string= "900150983cd24fb0d6963f7d28e17f72"
                   (md5 "abc")))
  (assert (string= "f96b697d7cb7938d525a2f31aaf161d0"
                   (md5 "message digest")))
  (assert (string= "c3fcd3d76192e4007dfb496cca67e13b"
                   (md5 "abcdefghijklmnopqrstuvwxyz")))
  (assert (string= "d174ab98d277d9f5a5611c2c9f419d9f"
                   (md5 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")))
  (assert (string= "57edf4a22be3c955ac49da2e2107b67a"
                   (md5 "12345678901234567890123456789012345678901234567890123456789012345678901234567890"))))


import std.bitmanip, core.stdc.string, std.conv, std.math, std.array,
       std.string;

version (D_InlineAsm_X86) {} else {
    static assert(false, "For X86 machine only.");
}

// CTFE construction of transform expressions.
uint S(in uint n) pure nothrow @safe @nogc {
    static immutable aux = [7u, 12, 17, 22, 5, 9, 14, 20, 4, 11,
                            16, 23, 6, 10, 15, 21];
    return aux[(n / 16) * 4 + (n % 4)];
}

uint K(in uint n) pure nothrow @safe @nogc {
    uint r = 0;
    if (n <= 15)
        r = n;
    else if (n <= 31)
        r = 5 * n + 1;
    else if (n <= 47)
        r = 3 * n + 5;
    else
        r = 7 * n;
    return r % 16;
}

uint T(in uint n) pure nothrow @nogc {
    return cast(uint)(abs(sin(n + 1.0L)) * (2UL ^^ 32));
}

string[] ABCD(in int n) pure nothrow {
    enum abcd = ["EAX", "EBX", "ECX", "EDX"];
    return abcd[(64 - n) % 4 .. 4] ~ abcd[0 .. (64 - n) % 4];
}

string SUB(in int n, in string s) pure nothrow {
    return s
           .replace("ax", n.ABCD[0])
           .replace("bx", n.ABCD[1])
           .replace("cx", n.ABCD[2])
           .replace("dx", n.ABCD[3]);
}

// FF, GG, HH & II expressions part 1 (F, G, H, I).
string fghi1(in int n) pure nothrow @nogc {
    switch (n / 16) {
        case 0:
            // (bb & cc) | (~bb & dd)
            return q{
                        mov ESI, bx;
                        mov EDI, bx;
                        not ESI;
                        and EDI, cx;
                        and ESI, dx;
                        or EDI, ESI;
                        add ax, EDI;
                    };
        case 1:
            // (dd & bb) | (~dd & cc)
            return q{
                        mov ESI, dx;
                        mov EDI, dx;
                        not ESI;
                        and EDI, bx;
                        and ESI, cx;
                        or EDI, ESI;
                        add ax, EDI;
                    };
        case 2: // (bb ^ cc ^ dd)
            return q{
                        mov EDI, bx;
                        xor EDI, cx;
                        xor EDI, dx;
                        add ax, EDI;
                    };
        case 3: // (cc ^ (bb | ~dd))
            return q{
                       mov EDI, dx;
                       not EDI;
                       or EDI, bx;
                       xor EDI, cx;
                       add ax, EDI;
                    };
        default:
            assert(false);
    }
}

// FF, GG, HH & II expressions part 2.
string fghi2(in int n) pure nothrow {
    return q{
                add ax, [EBP + 4 * KK];
                add ax, TT;
            } ~ n.fghi1;
}

// FF, GG, HH & II expressions prepended with previous parts
// & subsitute ABCD.
string FGHI(in int n) pure nothrow {
    // aa = ((aa << SS)|( aa >>> (32 - SS))) + bb = ROL(aa, SS) + bb
    return SUB(n, n.fghi2 ~ q{
                                rol ax, SS;
                                add ax, bx;
                             });
}

string genExpr(uint n) pure nothrow {
    return FGHI(n)
           .replace("SS", n.S.text)
           .replace("KK", n.K.text)
           .replace("TT", "0x" ~ to!string(n.T, 16));
}

string genTransformCode(int n) pure nothrow {
    return (n < 63) ? n.genExpr ~ genTransformCode(n + 1) : n.genExpr;
}

enum string coreZMD5 = 0.genTransformCode;

struct ZMD5 {
    uint[4] state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
    ulong count;
    ubyte[64] buffer;

    ubyte[64] padding = [
      0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
      0, 0];

    private void transform(ubyte* block) pure nothrow @nogc {
        uint[16] x = void;

        version (BigEndian) {
            foreach (immutable i; 0 .. 16)
                x[i] = littleEndianToNative!uint(*cast(ubyte[4]*)&block[i * 4]);
        } else {
            (cast(ubyte*)x.ptr)[0 .. 64] = block[0 .. 64];
        }

        auto pState = state.ptr;
        auto pBuffer = x.ptr;

        asm pure nothrow @nogc {
            mov  ESI, pState[EBP];
            mov  EDX, [ESI + 3 * 4];
            mov  ECX, [ESI + 2 * 4];
            mov  EBX, [ESI + 1 * 4];
            mov  EAX, [ESI + 0 * 4];
            push EBP;
            push ESI;

            mov  EBP, pBuffer[EBP];
        }

        mixin("asm pure nothrow @nogc { " ~ coreZMD5 ~ "}");

        asm pure nothrow @nogc {
            pop ESI;
            pop EBP;
            add [ESI + 0 * 4], EAX;
            add [ESI + 1 * 4], EBX;
            add [ESI + 2 * 4], ECX;
            add [ESI + 3 * 4], EDX;
        }
        x[] = 0;
    }

    void update(in void[] input) pure nothrow @nogc {
        auto inputLen = input.length;
        uint index = (count >> 3) & 0b11_1111U;
        count += inputLen * 8;
        immutable uint partLen = 64 - index;

        uint i;
        if (inputLen >= partLen) {
            memcpy(&buffer[index], input.ptr, partLen);
            transform(buffer.ptr);
            for (i = partLen; i + 63 < inputLen; i += 64)
                transform((cast(ubyte[])input)[i .. i + 64].ptr);
            index = 0;
        } else
            i = 0;

        if (inputLen - i)
            memcpy(&buffer[index], &input[i], inputLen - i);
    }

    void finish(ref ubyte[16] digest) pure nothrow @nogc {
        ubyte[8] bits = void;
        bits[0 .. 8] = nativeToLittleEndian(count)[];

        immutable uint index = (count >> 3) & 0b11_1111U;
        immutable uint padLen = (index < 56) ?
                                (56 - index) : (120 - index);
        update(padding[0 .. padLen]);
        update(bits);

        digest[0 .. 4]   = nativeToLittleEndian(state[0])[];
        digest[4 .. 8]   = nativeToLittleEndian(state[1])[];
        digest[8 .. 12]  = nativeToLittleEndian(state[2])[];
        digest[12 .. 16] = nativeToLittleEndian(state[3])[];

        // Zeroize sensitive information.
        memset(&this, 0, ZMD5.sizeof);
    }
}

string getDigestString(in void[][] data...) pure {
    ZMD5 ctx;
    foreach (datum; data)
        ctx.update(datum);
    ubyte[16] digest;
    ctx.finish(digest);
    return format("%-(%02X%)", digest);
}

void main() { // Benchmark code --------------
    import std.stdio, std.datetime, std.digest.md;

    writefln(`md5  digest("")  = %-(%02X%)`, "".md5Of);
    writefln(`zmd5 digest("")  = %s`, "".getDigestString);

    enum megaBytes = 512;
    writefln("\nTest performance / message size %dMBytes:", megaBytes);
    auto data = new float[megaBytes * 0x40000 + 13];

    StopWatch sw;
    sw.start;
    immutable d1 = data.md5Of;
    sw.stop;
    immutable time1 = sw.peek.msecs / 1000.0;
    writefln("digest(data) = %-(%02X%)", d1);
    writefln("std.md5: %8.2f M/sec  ( %8.2f secs)",
             megaBytes / time1, time1);

    sw.reset;
    sw.start;
    immutable d2 = data.getDigestString;
    sw.stop;
    immutable time2 = sw.peek.msecs / 1000.0;
    writefln("digest(data) = %s", d2);
    writefln("zmd5   : %8.2f M/sec  ( %8.2f secs)",
             megaBytes / time2, time2);
}


program MD5Implementation;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Classes;

type
  TTestCase = record
    hashCode: string;
    _: string;
  end;

var
  testCases: array[0..6] of TTestCase = ((
    hashCode: 'D41D8CD98F00B204E9800998ECF8427E';
    _: ''
  ), (
    hashCode: '0CC175B9C0F1B6A831C399E269772661';
    _: 'a'
  ), (
    hashCode: '900150983CD24FB0D6963F7D28E17F72';
    _: 'abc'
  ), (
    hashCode: 'F96B697D7CB7938D525A2F31AAF161D0';
    _: 'message digest'
  ), (
    hashCode: 'C3FCD3D76192E4007DFB496CCA67E13B';
    _: 'abcdefghijklmnopqrstuvwxyz'
  ), (
    hashCode: 'D174AB98D277D9F5A5611C2C9F419D9F';
    _: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  ), (
    hashCode: '57EDF4A22BE3C955AC49DA2E2107B67A';
    _: '12345678901234567890123456789' + '012345678901234567890123456789012345678901234567890'
  ));
  shift: array of UInt32 = [7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21];
  table: array[0..63] of UInt32;

procedure Init();
var
  i: integer;

  function fAbs(x: Extended): Extended;
  begin
    if x < 0 then
      exit(-x);
    exit(x);
  end;

begin
  for i := 0 to High(table) do
    table[i] := Trunc((UInt64(1) shl 32) * fAbs(Sin(i + 1.0)));
end;

function Md5(s: string): TBytes;
const
  BUFFER_SIZE = 16;
var
  binary: TBytesStream;
  buffer: Tarray<UInt32>;
  messageLenBits: UInt64;
  i, j, bufferIndex, count: integer;
  byte_data: byte;
  string_data: ansistring;
  k, k1: Tarray<UInt32>;
  f, rnd, sa: UInt32;
  tmp: UInt64;
begin
  k := [$67452301, $EFCDAB89, $98BADCFE, $10325476];

  binary := TBytesStream.Create();

  if not s.IsEmpty then
  begin
    string_data := Utf8ToAnsi(s);
    binary.Write(Tbytes(string_data), length(string_data));
  end;

  byte_data := $80;
  binary.Write(byte_data, 1);

  messageLenBits := UInt64(s.Length * 8);
  count := s.Length + 1;

  while (count mod 64) <> 56 do
  begin
    byte_data := $00;
    binary.Write(byte_data, 1);
    inc(count);
  end;

  binary.Write(messageLenBits, sizeof(messageLenBits));

  SetLength(buffer, BUFFER_SIZE);
  SetLength(k1, length(k));

  binary.Seek(0, soFromBeginning);

  while binary.Read(buffer[0], BUFFER_SIZE * 4) > 0 do
  begin
    for i := 0 to 3 do
      k1[i] := k[i];

    for i := 0 to 63 do
    begin
      f := 0;
      bufferIndex := i;
      rnd := i shr 4;
      case rnd of
        0:
          f := (k1[1] and k1[2]) or (not k1[1] and k1[3]);
        1:
          begin
            f := (k1[1] and k1[3]) or (k1[2] and not k1[3]);
            bufferIndex := (bufferIndex * 5 + 1) and $0F
          end;
        2:
          begin
            f := k1[1] xor k1[2] xor k1[3];
            bufferIndex := (bufferIndex * 3 + 5) and $0F;
          end;
        3:
          begin
            f := k1[2] xor (k1[1] or not k1[3]);
            bufferIndex := (bufferIndex * 7) and $0F;
          end;
      end;

      sa := shift[(rnd shl 2) or (i and 3)];

      k1[0] := k1[0] + f + buffer[bufferIndex] + table[i];

      tmp := k1[0];

      k1[0] := k1[3];
      k1[3] := k1[2];
      k1[2] := k1[1];

      k1[1] := ((tmp shl sa) or (tmp shr (32 - sa))) + k1[1];
    end;

    for i := 0 to 3 do
      k[i] := k[i] + k1[i];
  end;

  SetLength(result, BUFFER_SIZE);

  binary.Clear;
  for i := 0 to 3 do
    binary.Write(k[i], 4);

  binary.Seek(0, soBeginning);

  binary.Read(Result, BUFFER_SIZE);

  binary.Free;
end;

function BytesToString(b: TBytes): string;
var
  v: byte;
begin
  Result := '';
  for v in b do
    Result := Result + v.ToHexString(2);
end;

var
  tc: TTestCase;

begin
  Init;

  for tc in testCases do
    Writeln(Format('%s'#10'%s'#10, [tc.hashCode, BytesToString(md5(tc._))]));
  Readln;
end.


let fxyz x y z : uint32 = (x &&& y) ||| (~~~x &&& z)
let gxyz x y z : uint32 = (z &&& x) ||| (~~~z &&& y)
let hxyz x y z : uint32 = x ^^^ y ^^^ z
let ixyz x y z : uint32 = y ^^^ (x ||| ~~~z)
let fghi = [ fxyz; gxyz; hxyz; ixyz ] |> List.collect (List.replicate 16)
let g1Idx = id
let g2Idx i = (5 * i + 1) % 16
let g3Idx i = (3 * i + 5) % 16
let g4Idx i = (7 * i) % 16

let gIdxs =
  [ g1Idx; g2Idx; g3Idx; g4Idx ]
  |> List.collect (List.replicate 16)
  |> List.map2 (fun idx func -> func idx) [ 0..63 ]

let s =
  [ [ 7; 12; 17; 22 ]
    [ 5; 9; 14; 20 ]
    [ 4; 11; 16; 23 ]
    [ 6; 10; 15; 21 ] ]
  |> List.collect (List.replicate 4)
  |> List.concat

let k =
  [ 1...64. ] |> List.map (sin
                           >> abs
                           >> ((*) (2. ** 32.))
                           >> floor
                           >> uint32)

type MD5 =
  { a : uint32
    b : uint32
    c : uint32
    d : uint32 }

let initialMD5 =
  { a = 0x67452301u
    b = 0xefcdab89u
    c = 0x98badcfeu
    d = 0x10325476u }

let md5round (msg : uint32 []) { MD5.a = a; MD5.b = b; MD5.c = c; MD5.d = d } i =
  let rotateL32 r x = (x <<< r) ||| (x >>> (32 - r))
  let f = fghi.[i] b c d
  let a' = b + (a + f + k.[i] + msg.[gIdxs.[i]]
                |> rotateL32 s.[i])
  { a = d
    b = a'
    c = b
    d = c }

let md5plus m (bs : byte []) =
  let msg =
    bs
    |> Array.chunkBySize 4
    |> Array.take 16
    |> Array.map (fun elt -> System.BitConverter.ToUInt32(elt, 0))

  let m' = List.fold (md5round msg) m [ 0..63 ]
  { a = m.a + m'.a
    b = m.b + m'.b
    c = m.c + m'.c
    d = m.d + m'.d }

let padMessage (msg : byte []) =
  let msgLen = Array.length msg
  let msgLenInBits = (uint64 msgLen) * 8UL

  let lastSegmentSize =
    let m = msgLen % 64
    if m = 0 then 64
    else m

  let padLen =
    64 - lastSegmentSize + (if lastSegmentSize >= 56 then 64
                            else 0)

  [| yield 128uy
     for i in 2..padLen - 8 do
       yield 0uy
     for i in 0..7 do
       yield ((msgLenInBits >>> (8 * i)) |> byte) |]
  |> Array.append msg

let md5sum (msg : string) =
  System.Text.Encoding.ASCII.GetBytes msg
  |> padMessage
  |> Array.chunkBySize 64
  |> Array.fold md5plus initialMD5
  |> (fun { MD5.a = a; MD5.b = b; MD5.c = c; MD5.d = d } ->
    System.BitConverter.GetBytes a
    |> (fun x -> System.BitConverter.GetBytes b |> Array.append x)
    |> (fun x -> System.BitConverter.GetBytes c |> Array.append x)
    |> (fun x -> System.BitConverter.GetBytes d |> Array.append x))
  |> Array.map (sprintf "%02X")
  |> Array.reduce (+)


package main

import (
    "fmt"
    "math"
    "bytes"
    "encoding/binary"
)

type testCase struct {
    hashCode string
    string
}

var testCases = []testCase{
    {"d41d8cd98f00b204e9800998ecf8427e", ""},
    {"0cc175b9c0f1b6a831c399e269772661", "a"},
    {"900150983cd24fb0d6963f7d28e17f72", "abc"},
    {"f96b697d7cb7938d525a2f31aaf161d0", "message digest"},
    {"c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz"},
    {"d174ab98d277d9f5a5611c2c9f419d9f",
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
    {"57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890" +
        "123456789012345678901234567890123456789012345678901234567890"},
}

func main() {
    for _, tc := range testCases {
        fmt.Printf("%s\n%x\n\n", tc.hashCode, md5(tc.string))
    }
}

var shift = [...]uint{7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21}
var table [64]uint32

func init() {
    for i := range table {
        table[i] = uint32((1 << 32) * math.Abs(math.Sin(float64(i + 1))))
    }
}

func md5(s string) (r [16]byte) {
    padded := bytes.NewBuffer([]byte(s))
    padded.WriteByte(0x80)
    for padded.Len() % 64 != 56 {
        padded.WriteByte(0)
    }
    messageLenBits := uint64(len(s)) * 8
    binary.Write(padded, binary.LittleEndian, messageLenBits)

    var a, b, c, d uint32 = 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476
    var buffer [16]uint32
    for binary.Read(padded, binary.LittleEndian, buffer[:]) == nil { // read every 64 bytes
        a1, b1, c1, d1 := a, b, c, d
        for j := 0; j < 64; j++ {
            var f uint32
            bufferIndex := j
            round := j >> 4
            switch round {
            case 0:
                f = (b1 & c1) | (^b1 & d1)
            case 1:
                f = (b1 & d1) | (c1 & ^d1)
                bufferIndex = (bufferIndex*5 + 1) & 0x0F
            case 2:
                f = b1 ^ c1 ^ d1
                bufferIndex = (bufferIndex*3 + 5) & 0x0F
            case 3:
                f = c1 ^ (b1 | ^d1)
                bufferIndex = (bufferIndex * 7) & 0x0F
            }
            sa := shift[(round<<2)|(j&3)]
            a1 += f + buffer[bufferIndex] + table[j]
            a1, d1, c1, b1 = d1, c1, b1, a1<<sa|a1>>(32-sa)+b1
        }
        a, b, c, d = a+a1, b+b1, c+c1, d+d1
    }

    binary.Write(bytes.NewBuffer(r[:0]), binary.LittleEndian, []uint32{a, b, c, d})
    return
}


class MD5 {

    private static final int INIT_A = 0x67452301
    private static final int INIT_B = (int)0xEFCDAB89L
    private static final int INIT_C = (int)0x98BADCFEL
    private static final int INIT_D = 0x10325476

    private static final int[] SHIFT_AMTS = [
            7, 12, 17, 22,
            5,  9, 14, 20,
            4, 11, 16, 23,
            6, 10, 15, 21
    ]

    private static final int[] TABLE_T = new int[64]
    static
    {
        for (int i in 0..63)
            TABLE_T[i] = (int)(long)((1L << 32) * Math.abs(Math.sin(i + 1)))
    }

    static byte[] computeMD5(byte[] message)
    {
        int messageLenBytes = message.length
        int numBlocks = ((messageLenBytes + 8) >>> 6) + 1
        int totalLen = numBlocks << 6
        byte[] paddingBytes = new byte[totalLen - messageLenBytes]
        paddingBytes[0] = (byte)0x80

        long messageLenBits = (long)messageLenBytes << 3
        for (int i in 0..7)
        {
            paddingBytes[paddingBytes.length - 8 + i] = (byte)messageLenBits
            messageLenBits >>>= 8
        }

        int a = INIT_A
        int b = INIT_B
        int c = INIT_C
        int d = INIT_D
        int[] buffer = new int[16]
        for (int i in 0..(numBlocks - 1))
        {
            int index = i << 6
            for (int j in 0..63) {
                buffer[j >>> 2] = ((int) ((index < messageLenBytes) ? message[index] : paddingBytes[index - messageLenBytes]) << 24) | (buffer[j >>> 2] >>> 8)
                index++
            }
            int originalA = a
            int originalB = b
            int originalC = c
            int originalD = d
            for (int j in 0..63)
            {
                int div16 = j >>> 4
                int f = 0
                int bufferIndex = j
                switch (div16)
                {
                    case 0:
                        f = (b & c) | (~b & d)
                        break

                    case 1:
                        f = (b & d) | (c & ~d)
                        bufferIndex = (bufferIndex * 5 + 1) & 0x0F
                        break

                    case 2:
                        f = b ^ c ^ d
                        bufferIndex = (bufferIndex * 3 + 5) & 0x0F
                        break

                    case 3:
                        f = c ^ (b | ~d)
                        bufferIndex = (bufferIndex * 7) & 0x0F
                        break
                }
                int temp = b + Integer.rotateLeft(a + f + buffer[bufferIndex] + TABLE_T[j], SHIFT_AMTS[(div16 << 2) | (j & 3)])
                a = d
                d = c
                c = b
                b = temp
            }

            a += originalA
            b += originalB
            c += originalC
            d += originalD
        }

        byte[] md5 = new byte[16]
        int count = 0
        for (int i in 0..3)
        {
            int n = (i == 0) ? a : ((i == 1) ? b : ((i == 2) ? c : d))
            for (int j in 0..3)
            {
                md5[count++] = (byte)n
                n >>>= 8
            }
        }
        return md5
    }

    static String toHexString(byte[] b)
    {
        StringBuilder sb = new StringBuilder()
        for (int i in 0..(b.length - 1))
        {
            sb.append(String.format("%02X", b[i] & 0xFF))
        }
        return sb.toString()
    }

    static void main(String[] args)
    {
        String[] testStrings = ["", "a", "abc", "message digest", "abcdefghijklmnopqrstuvwxyz",
                                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
                                "12345678901234567890123456789012345678901234567890123456789012345678901234567890" ]
        for (String s : testStrings)
            System.out.println("0x" + toHexString(computeMD5(s.getBytes())) + " <== \"" + s + "\"")
    }

}