Jamie Balfour

Welcome to my personal website.

Find out more about me, my personal projects, reviews, courses and much more here.

Run ZPE/YASS online

Run ZPE/YASS

Run ZPE/YASS code right here! ZPE compiles the code into a YASS Executable (YEX) file then runs it directly, taking advantage of ZPE's cache feature.

You can also find out the exact version information of this version by typing version into the command box.

It should also be noted that the performance of running this is not as high as it would be if it was run natively on your computer system. This is due to a number of factors, for example, how quickly the result is sent across and how quickly the output is read and then displayed by PHP.

Here are some examples of code you can run (click the 'play' button to insert them into the textbox above and then run them):

YASS
print("Hello world")
	
YASS
print(5 + 5 * 2)
	
YASS
function main()
  for($i = 0 to 1000)
	  print($i)
  end for
end function
	
YASS
$x = function($m){print($m + 2)}
for($i = 0; $i < 10; $i++){
  $x(10 * $i)
}
	
YASS
$v =
function()
 print("Hi")
end function

//Lambda call
$v()

$v = ($x) => print($x)
$v("Hello world")
	
YASS
structure person

  //Using strong typing here
  public string $first_name = "Jamie"
	public string $second_name = "Balfour"

	function _construct($f, $s)

    //One way of doing this
    if(is_set($f))
      this->$first_name = $f
		end if

    //Another way of doing this
		if(is_set($s))
      this->$second_name = $s
		end if

	end function

	public function getFirstName()
	  return this->$first_name
	end function

	public function getSecondName()
	  return this->$second_name
	end function
end structure

function main()
  $p = new person()
	print($p->getFirstName(), $p->getSecondName())
	$p = new person("John", "Addams")
	print($p->getFirstName(), $p->getSecondName())
end function
	
YASS
//Get the number of hours between now and 25-10-2015 14:00:24
$now = new Date()
$d1 = string_to_date("25-10-2015 14:00:24", "dd-MM-yyyy hh:mm:ss")
print("The number of hours since 25-10-2015 14:00:24 is " & $now->date_diff($d1, "hours") & ".")
	
YASS
function fibonacci_generator($n)
  $x = 1
  $y = 2
	$results = [1, 1, 2]
  $i = 0
  //$n - 3 is used because we add the first three to the list ourselves
	while($i < $n - 3)
	  $t = fibonacci_inner($x, $y)
		$results.put($t)
		$x = $y
		$y = $t
		$i++
	end while

	return $results
end function

function fibonacci_inner($i, $j)
  return $i + $j
end function

function main()
  print(fibonacci_generator(10))
end function
	
YASS
for($i = 0 to 100)
  run_in_thread(function(){
    print("Hello " & $i)
  })
end for
	
YASS
structure Animal

  public function identify()
    return "animal"
  end function

  public function act()
    print("I am acting")
  end function

  public static function alertMe()
    print("Alert!")
  end function

end structure

structure Dog inherits from Animal

  public function identify()
    return "dog"
  end function

  public function act()
    print("woof")
  end function

end structure

structure Cat inherits from Animal

  public function identify()
    return super->identify()
  end function

end structure

structure Fox inherits Dog

  public function identify()
    return "Fox"
  end function

end structure

Animal $animal = new Dog() // accepted
$animal->act()
print($animal->identify())

Animal $animal2 = new Cat()
print($animal2->identify())

Animal::alertMe()

Animal $animal3 = new Fox()
print($animal3->identify())
$animal3->act()

print($animal is type of Animal)
print($animal is type of Dog)

print(type($animal))

print($animal inherits Dog)
	
YASS
module stdAlgorithms
  /*
    This is the main standard library of algorithms for ZPE, written entirely in YASS.
    The main purpose behind it is to performance test Jamie Balfour's ZPE/YASS language and
    runtime. There are some very complex algorithms here, such as sorting algorithms.
    Code is expected to take no longer than 1 second to parse and compile.
    Runtime should be no more than 1 second.
    It is necessary that this code is as efficient as possible to test ZPE/YASS.
    All functions should provide a return type in all versions after ZPE 1.11.1.
    This version is only compatible with ZPE version 1.14.5+ due to the use of internal modules
    such as Math. This version was updated to 1.14.8 due to the $begin in the timer now requiring
    the keyword this preceeding it.
  */


  @author "Jamie Balfour"
  @date "December 2015"
  @doc "A timer that uses a start point and end point to calculate timings."
  public class timer

    namespace jamiebalfour

    private $begin = 0
    private $finish = 0
    private $stopped = false

    public function getTime()
      if(this->$stopped)
        return this->$finish - this->$begin
      else
        return time() - this->$begin
      end if
      //return ZPE::get_zpe_time()
    end function

    public function go()
      this->$stopped = false
      this->$begin = time()
      return "Started"
    end function

    public function stop()
      this->$stopped = true
      this->$finish = time()
      return "Stopped"
    end function

    public function getStart()
      return this->$begin
    end function

    public function getEnd()
      return this->$finish
    end function

  end class

  @author "Jamie Balfour"
  @date "July 2026"
  @doc "A singly linked-list node, implemented in pure YASS"
  class linked_list_node

    namespace jamiebalfour

    private $next = null
    private $value = null

    public function _construct($v)

      this->$value = $v

    end function

    public function getValue()

      return this->$value

    end function

    public function setValue($v)

      this->$value = $v

    end function

    public function getNext()

      return this->$next

    end function

    public function setNext($node)

      this->$next = $node

    end function

  end class


  @author "Jamie Balfour"
  @date "March 2024"
  @doc "A singly linked list, implemented in pure YASS"
  public class linked_list

    namespace jamiebalfour

    private $head = null
    private $tail = null
    private $size = 0

    public function _construct($v)

      $node = new stdAlgorithms::jamiebalfour/linked_list_node($v)

      this->$head = $node
      this->$tail = $node
      this->$size = 1

    end function

    public function getValue()

      if(this->$head == null)
        return null
      end if

      return this->$head->getValue()

    end function

    public function setValue($v)

      if(this->$head == null)
        $node = new stdAlgorithms::jamiebalfour/linked_list_node($v)

        this->$head = $node
        this->$tail = $node
        this->$size = 1
      else
        this->$head->setValue($v)
      end if

    end function

    public function addValue($v)

      $node = new stdAlgorithms::jamiebalfour/linked_list_node($v)

      if(this->$head == null)
        this->$head = $node
        this->$tail = $node
      else
        this->$tail->setNext($node)
        this->$tail = $node
      end if

      this->$size = this->$size + 1

    end function

    public function addFirst($v)

      $node = new stdAlgorithms::jamiebalfour/linked_list_node($v)

      if(this->$head == null)
        this->$head = $node
        this->$tail = $node
      else
        $node->setNext(this->$head)
        this->$head = $node
      end if

      this->$size = this->$size + 1

    end function

    public function getFirst()

      if(this->$head == null)
        return null
      end if

      return this->$head->getValue()

    end function

    public function getLast()

      if(this->$tail == null)
        return null
      end if

      return this->$tail->getValue()

    end function

    public function getSize()

      return this->$size

    end function

    public function isEmpty()

      return this->$size == 0

    end function

    public function contains($value)

      $current = this->$head

      while($current != null)

        if($current->getValue() == $value)
          return true
        end if

        $current = $current->getNext()

      end while

      return false

    end function

    public function removeFirst()

      if(this->$head == null)
        return null
      end if

      $value = this->$head->getValue()
      this->$head = this->$head->getNext()
      this->$size = this->$size - 1

      if(this->$head == null)
        this->$tail = null
      end if

      return $value

    end function

    public function clear()

      this->$head = null
      this->$tail = null
      this->$size = 0

    end function

    public function _output()

      $output = ""
      $current = this->$head

      while($current != null)

        if($output != "")
          $output = $output & ", "
        end if

        $output = $output & $current->getValue()
        $current = $current->getNext()

      end while

      return $output

    end function

  end class

  @author "Jamie Balfour"
  @date "March 2023"
  @doc "This is a fairly simple queue structure, implemented in pure YASS"
  public class queue

    namespace jamiebalfour

    private $items = []

    public function enqueue($x)

      this->$items = list_add_element(this->$items, $x)

    end function

    public function dequeue()

      if(count(this->$items) > 0)
        $r = this->$items[0]
        this->$items = list_remove_element(this->$items, 0)
        return $r
      else
        return null
      end if

    end function

  end class

  @author "Jamie Balfour"
  @date "March 2023"
  @doc "Like the above, this is a fairly simple implementation of a stack structure, implemented in pure YASS"
  public class stack

    namespace jamiebalfour

    private $items = []

    public function push($x)

      this->$items = list_add_element(this->$items, $x)

    end function

    public function pop()

      if(count(this->$items) > 0)
        $r = this->$items[count(this->$items) - 1]
        this->$items = list_remove_element(this->$items, count(this->$items) - 1)
        return $r
      else
        return null
      end if

    end function

  end class

  @author "Jamie Balfour"
  @date "December 2015"
  @doc "If users choose to run this application this method is invoked."
  function main()

    print("Welcome")

    //Change to false to hide all timing
    $display_timings = true

    function showTime($t)
      if($display_timings == true)
        print("  Time: " & $t & "ms")
      end if
    end function

    $timer = new stdAlgorithms::jamiebalfour/timer()
    $timer->go()
    print("Timer started.")

    $l = [43, 12, 8, 23, 99, 301, 41, 22, 91, 451, 27, 83, 106, 11, 13, 32, 98, 68, 103, 207, 201, 23, 64, 13, 309, 434, 1, 76, 7]
    $n = ["John", "Fiona", "Maureen", "Roger", "James", "Gillian", "Jamie", "Peter", "Richard", "Boyd", "Adam", "Malcolm", "Rosanna", "Yve", "Jonah", "William"]

    for($i = 0; $i < 5; $i++)
      list_add_element($l, Math::random_number(100, 0))
    end for

    //An ordered associative array
    $m = [77 => "Jack", 41 => "Emma", 98 => "James", 21 => "Jamie"]

    showTime($timer->getTime())

    $sll = new stdAlgorithms::jamiebalfour/linked_list(10)
    $sll->addValue(15)
    for($i = 0 to 100)
      $sll->addValue($i)
    end for
    print("$sll: " & $sll)
    showTime($timer->getTime())


    $q = new stdAlgorithms::jamiebalfour/queue()
    print("$q->enqueue(13)")
    $q->enqueue(13)
    print("$q->enqueue(10)")
    $q->enqueue(10)
    print("$q->enqueue(21)")
    $q->enqueue(21)
    print("$q->dequeue(): " & $q->dequeue())
    print("$q->dequeue(): " & $q->dequeue())
    showTime($timer->getTime())

    $s = new stdAlgorithms::jamiebalfour/stack()
    print("$s->push(13)")
    $s->push(13)
    print("$s->push(10)")
    $s->push(10)
    print("$s->push(21)")
    $s->push(21)
    print("$s->pop(): " & $s->pop())
    print("$s->pop(): " & $s->pop())
    showTime($timer->getTime())


    print("combination_counter(3, 3): " & stdAlgorithms::combination_counter(3, 3))
    print("combination_counter(10, 2): " & stdAlgorithms::combination_counter(10, 2))
    print("gcd(54, 5): " & stdAlgorithms::gcd(54, 5))
    showTime($timer->getTime())
    print("fibonacci_generator(10): " & stdAlgorithms::fibonacci_generator(10))
    print("calculate_area([[type => square, width => 20, height => 20], [type => triangle, width => 5, height => 5]]):" & stdAlgorithms::calculate_area([["type" => "square", "width" => 20, "height" => 20], ["type" => "triangle", "width" => 5, "height" => 5]]))
    print("$l: " & $l)
    print("$m: " & $m)
    print("$n: " & $n)


    showTime($timer->getTime())
    print("bubble_sort($l): " & stdAlgorithms::bubble_sort($l))
    showTime($timer->getTime())
    print("bubble_sort($n): " & stdAlgorithms::bubble_sort($n))
    showTime($timer->getTime())
    print("bubble_sort($m): " & stdAlgorithms::bubble_sort($m))
    showTime($timer->getTime())

    //Insertion sorts
    print("insertion_sort($l): " & stdAlgorithms::insertion_sort($l))
    showTime($timer->getTime())
    print("insertion_sort($n): " & stdAlgorithms::insertion_sort($n))
    showTime($timer->getTime())
    print("insertion_sort($m): " & stdAlgorithms::insertion_sort($m))
    showTime($timer->getTime())

    //Quick sorts
    print("quicksort($l): " & stdAlgorithms::quicksort($l))
    showTime($timer->getTime())
    print("quicksort($n): " & stdAlgorithms::quicksort($n))
    showTime($timer->getTime())
    print("quicksort($m): " & stdAlgorithms::quicksort($m))
    showTime($timer->getTime())

    //Merge sorts
    print("mergesort($l): " & stdAlgorithms::mergesort($l))
    showTime($timer->getTime())
    print("mergesort($n): " & stdAlgorithms::mergesort($n))
    showTime($timer->getTime())
    print("mergesort($m): " & stdAlgorithms::mergesort($m))
    showTime($timer->getTime())

    $l1 = [[32, 49], [32, 49], [37, 23]]
    print("$l1: " & $l1)
    print("is_injective($l1): ? Yes : No: " & stdAlgorithms::is_injective($l1) ? "Yes" : "No")
    $l2 = [[32, 49], [32, 42], [37, 23]]
    print("$l2: " & $l2)
    print("is_injective($l2): ? Yes : No: " & stdAlgorithms::is_injective($l2) ? "Yes" : "No")
    $l3 = [[32, 49], [88, 36], [21, 90]]
    print("$l3: " & $l3)
    print("is_injective($l3): ? Yes : No: " & stdAlgorithms::is_injective($l3) ? "Yes" : "No")
    showTime($timer->getTime())
    print("infinite_product_calculator(1, 2, 3, 4, 5, 6, 7, 8): " & stdAlgorithms::infinite_product_calculator(1, 2, 3, 4, 5, 6, 7, 8))
    showTime($timer->getTime())
    $l = [43, 12, 8, 23, 99, 301, 41, 22, 91, 451, 27, 83, 106, 11, 13]

    print("$l: " & $l)
    print("linear_search($l, 23): " & stdAlgorithms::linear_search($l, 23))
    //Binary searches
    showTime($timer->getTime())
    print("binary_search($l, 1): " & stdAlgorithms::binary_search($l, 1) != -1 ? "Exists" : "Does not exist")
    showTime($timer->getTime())
    print("binary_search($l, 12): " & stdAlgorithms::binary_search($l, 12) != -1 ? "Exists" : "Does not exist")
    showTime($timer->getTime())
    print("binary_search($l, 22): " & stdAlgorithms::binary_search($l, 22) != -1 ? "Exists" : "Does not exist")
    showTime($timer->getTime())


    print("calculateDayOfWeek(13, 7, 1991): " & stdAlgorithms::calculateDayOfWeek(13, 7, 1991))
    showTime($timer->getTime())
    print("calculateDayOfWeek(2, 10, 1993): " & stdAlgorithms::calculateDayOfWeek(2, 10, 1993))
    showTime($timer->getTime())
    print("calculateDayOfWeek(28, 8, 1957): " & stdAlgorithms::calculateDayOfWeek(28, 8, 1957))
    showTime($timer->getTime())
    print("calculateDayOfWeek(27, 3, 1956): " & stdAlgorithms::calculateDayOfWeek(27, 3, 1956))
    showTime($timer->getTime())

    print('isAnagramOf("take", "Kate"): ' & stdAlgorithms::isAnagramOf("take", "Kate"))
    print('isAnagramOf("take", "Kates"): ' & stdAlgorithms::isAnagramOf("take", "Kates"))
    print('isAnagramOf("Joir Flambeau", "Jamie Balfour"): ' & stdAlgorithms::isAnagramOf("Joir Flambeau", "Jamie Balfour"))
    showTime($timer->getTime())

    print("flip_number(-5): ", stdAlgorithms::flip_number(-5))

    print("meshCalculator(5): ", stdAlgorithms::meshCalculator(5))
    showTime($timer->getTime())

    print('caesarCipherEncrypt("Hello", 5, false): ' & stdAlgorithms::caesarCipherEncrypt("Hello", 5, false))
    print('caesarCipherEncrypt("Hello there world", "JamesBond", true): ' & stdAlgorithms::caesarCipherEncrypt("Hello there world", "JamesBond", true))
    showTime($timer->getTime())
    $enc = stdAlgorithms::caesarCipherEncrypt("Hello there world", "JamesBond", true)
    print('$enc = caesarCipherEncrypt("Hello there world", "JamesBond", true)')
    showTime($timer->getTime())
    print('caesarCipherDecryptAdvanced($enc, "JamesBond"): ' & stdAlgorithms::caesarCipherDecryptAdvanced($enc, "JamesBond"))
    showTime($timer->getTime())
    print('calculateRootMeanSquare([11, 22, 33]): ' & stdAlgorithms::calculateRootMeanSquare([11, 22, 33]))
    showTime($timer->getTime())
    print('morseCodeEncode("SOS"): ' & stdAlgorithms::morseCodeEncode("SOS"))
    print('morseCodeDecode(".--- .- -- .. ."): ' & stdAlgorithms::morseCodeDecode(".--- .- -- .. ."))
    showTime($timer->getTime())
    $mat1 = [[1, 2, 7, 1], [12, 6, 8, 2], [13, 7, 19, 3], [4, 3, 5, 8)]]
    $mat2 = [[5, 4, 1, 9], [2, 1, 7, 20], [6, 3, 1, 5], [1, 3, 7, 2)]]
    print("multiplySquareMatrices($mat1, $mat2): " & stdAlgorithms::multiplySquareMatrices($mat1, $mat2))
    showTime($timer->getTime())
    print("ackermann_function(2, 2): " & stdAlgorithms::ackermann_function(2, 2))
    print("ackermann_function(3, 1): " & stdAlgorithms::ackermann_function(3, 1))
    showTime($timer->getTime())
    print('kmp_indexOf("abracadabra", "abra"): ' & stdAlgorithms::kmp_indexOf("abracadabra", "abra"))
    print('kmp_find_all("aaaaa", "aa"): ' & stdAlgorithms::kmp_find_all("aaaaa", "aa"))
    showTime($timer->getTime())
    $G = {=>}
    $G = $G.put("A", [["B", 4], ["C", 2]])
    $G = $G.put("B", [["C", 5], ["D", 10]])
    $G = $G.put("C", [["E", 3]])
    $G = $G.put("E", [["D", 4]])
    $G = $G.put("D", []) // optional

    $r = stdAlgorithms::dijkstra_path($G, "A", "D")
    print("Distance A->D: " & $r["distance"])
    print("Path A->D: " & $r["path"])

    $timer->stop()

    print("Timer finished : " & $timer->getTime() & "ms")


  end function

  @author "Jamie Balfour"
  @date "December 2015"
  @doc "Calculates the whole area of a room with multiple areas in it."
  public function calculate_area(list $l) : number

    $total = 0
    for each($l as $section)
      if($section["type"] == "triangle")
        $section_size = ($section["width"] * $section["height"]) / 2
      else
        $section_size = $section["width"] * $section["height"])
      end if
      $total += $section_size
    end for

    return $total

  end function

  @author "Jamie Balfour"
  @date "December 2015"
  @doc "Obtains the greatest common divisor of two numbers, $a and $b."
  public function gcd(number $a, number $b) : number

    if($b == 0)
      return $a
    end if

    return gcd($b, $a % $b)

  end function

  private function isBefore($a, $b) : boolean

    if(type($a) == type(0))
      return $a < $b
    elseif(type($a) == type(""))
      return string_compare($a, $b) == -1
    end if

    return false

  end function

  @author "Jamie Balfour"
  @date "December 2015"
  @doc "A simple bubble sort."
  public function bubble_sort(mixed $l) : list | map | boolean

    if(type($l) == type([=>]))

      $skeys = bubble_sort(map_get_keys($l))
      $output = {=>}

      for each($skeys as $k)
        $v = $l[$k]
        $output[$k] = $v

      end for

      return $output

    end if

    if(type($l) != type([]))
      throw_error("Incorrect type given")
      return false
    end if

    $len = list_get_length($l)
    $temp = 0

    for ($i = 0; $i < $len; $i++)
      for($j = 0; $j < ($len - 1 - $i); $j++)
        if($j + 1 < $len && isBefore($l[$j + 1], $l[$j]))
          $l = list_swap_elements($l, $j, $j + 1)
        end if
      end for
    end for

    return $l
  end function

  @author "Jamie Balfour"
  @date "July 2016"
  @doc "A simple insertion sort. Jamie Balfour July 2016"
  public function insertion_sort(mixed $l) : list | map | boolean


    if(type($l) == type([=>]))

      $skeys = insertion_sort(map_get_keys($l))
      $output = {=>}

      for each($skeys as $k)
        $v = $l[$k]
        $output[$k] = $v
      end for

      return $output

    end if

    if(type($l) != type([]))
      throw_error("Incorrect type given")
      return 0
    end if



    for ($i = 1; $i < $l.length(); $i++)
      $element = $l[$i]
      $j = $i

      while($j > 0 && isBefore($element, $l[$j - 1]))
        //move value to right and key to previous smaller index
        $l = list_set_at_index($l, $j, $l[$j - 1])
        $j--
      end while

      //Put the element at index $j

      $l = list_set_at_index($l, $j, $element)

    end for

    return $l

  end function

  @author "Jamie Balfour"
  @date "July 2016"
  @doc "A simple quicksort algorithm that uses recursion."
  public function quicksort(mixed $l) : list | map | boolean

    function quicksort_inner($l, $low, $high)

      $i = $low
      $j = $high

      $pivot = $l[floor($low + ($high - $low) / 2)]

      while($i <= $j)
        while(isBefore($l[$i], $pivot))
          $i++
        end while

        while(isBefore($pivot, $l[$j]))
          $j--
        end while

        if($i <= $j)
          $l = list_swap_elements($l, $i, $j)//swap($l, $i, $j)
          $i++
          $j--
        end if
      end while

      if($low < $j)
        $l = quicksort_inner($l, $low, $j)
      end if

      if($i < $high)
        $l = quicksort_inner($l, $i, $high)
      end if

      return $l

    end function

    if(type($l) == type([=>]))

      $skeys = quicksort(map_get_keys($l))
      $output = {=>}

      for each($skeys as $k)
        $v = $l[$k]
        $output[$k] = $v
      end for

      return $output

    end if

    if(type($l) != type([]))
      throw_error("Incorrect type given")
      return 0
    end if

    if(empty($l))
      return []
    end if

    $numb = list_get_length($l)

    return quicksort_inner($l, 0, $numb - 1)


  end function


  @author "Jamie Balfour"
  @date "April 2019"
  @doc "A merge sort algorithm."
  public function mergesort(mixed $l) : list | map | boolean

    //Nested function designed to perform merge
    function merge(mixed $left, mixed $right)

      $res = [];

      while (list_get_length($left) > 0 && list_get_length($right) > 0)
        if(isBefore($right[0], $left[0]))
          list_add_element(&$res, $right[0])
          $right = list_slice($right , 1)
        else
          list_add_element(&$res, $left[0])
          $left = list_slice($left, 1)
        end if
      end while

      while (list_get_length($left) > 0)
        list_add_element(&$res, $left[0])
        $left = list_slice($left, 1)
      end while

      while (list_get_length($right) > 0)
        list_add_element(&$res, $right[0])
        $right = list_slice($right, 1)
      end while

      return $res

    end function

    if(type($l) == type([=>]))

      $skeys = mergesort(map_get_keys($l))

      $output = {=>}
      for each($skeys as $k)

        $v = $l[$k]
        $output[$k] = $v

      end for

      return $output

    end if

    if(list_get_length($l) == 1)
      return $l
    end if

    $mid = floor(list_get_length($l) / 2)
    $left = list_slice($l, 0, $mid)
    $right = list_slice($l, $mid)


    $left = mergesort($left)
    $right = mergesort($right)

    return merge($left, $right)

  end function


  @author "Jamie Balfour"
  @date "December 2015"
  @doc "A function to detect if a list of lists (considered as objects) is injective. For instance, [[32, 43], [31, 33], [22, 99]] is injective whereas [[32, 43], [43, 21], [32, 21]] is not"
  public function is_injective(list $l) : boolean

    if(type($l) != type([]))
      throw_error("Incorrect type given")
      return 0
    end if

    //This version of isInjective uses maps

    $domList = [null => null]


    $len = list_get_length($l)

    $i = 0

    while($i < $len)
      //Get the pair inside the list at the current index, $i
      $t = $l[$i]

      //Check that it is a list
      if(type($t) != type([]))
        return false
      end if

      //Domain is the first element of the list
      $d = $t[0]
      //Range is the second element of the list
      $r = $t[1]

      if(map_contains($domList, $d))
        $temp = $domList[$d]
        if($temp != $r)
          return(false)
        end if
      end if

      $domList = $domList.put($d, $r)

      $i++
    end while

    return(true)

  end function

  @author "Jamie Balfour"
  @date "July 2016"
  @doc "Calculates how many combinations of items there are for $count_of_positions
  positions without repetition. E.g. 123 and 124 are combinations, but 213 is not (since it is a repeat)."
  public function combination_counter(number $count_of_items, number $count_of_positions) : number

    $n = $count_of_items
    $r = $count_of_positions

    return factorial($n + $r - 1) / (factorial($r) * factorial($n - 1))

  end function

  @author "Jamie Balfour"
  @date "January 2016"
  @doc "Given a list of length n, the product is the total of each element added to the next. Updated for ZPE 1.8.11 with infinite parameters."
  public function infinite_product_calculator($l ...) : number

    if(type($l) != type([]))
      throw_error("Incorrect type given")
      return 0
    end if

    $total = 0
    for each($l as $x)
      $total = $total + $x
    end for

    return $total

  end function

  @author "Jamie Balfour"
  @date "July 2016"
  @doc "This function will generate n instances of the Fibonacci numbers"
  public function fibonacci_generator(number $n) : list


    $x = 1
    $y = 2
    $results = [1, 1, 2]
    $i = 0
    while($i < $n - 3)
      let t = $x + $y
      $results.put(t)
      $x = $y
      $y = t
      $i++
    end while

    return $results

  end function



  @author "Jamie Balfour"
  @date "November 2020"
  @doc "Performs a linear search on a list"
  public function linear_search(list $l, mixed $search) : number

    if(type($l) != type([]))
      throw_error("Incorrect type given")
    end if

    $pos = 0
    $len = list_get_length($l)

    while($pos < $len)

      $current = $l[$pos]
      if($current == $search)
        return $pos
      end if

      $pos++

    end while

    return -1

  end function


  @author "Jamie Balfour"
  @date "April 2019"
  @doc "Binary search program to return the index"
  public function binary_search(list $l, mixed $search_term) : number

    if(type($l) == type([]))
      if(empty($l))
        return -1
      end if
      //Must sort it first
      $l = quicksort($l)

      $low = 0
      $high = list_get_length($l) - 1

      while ($low <= $high)
        //Middle index
        $mid = floor(($low + $high) / 2)

        $cur = $l[$mid]

        // element found at mid
        if($cur == $search_term)
            return $mid
        end if

        if ($search_term < $cur)
          // search the left side of the array
          $high = $mid -1
        else
          // search the right side of the array
          $low = $mid + 1;
        end if
      end while
    end if

    return -1

  end function

  @author "Jamie Balfour"
  @date "21 April 2022"
  @doc "Program designed to calculate the day of the week from a specified day, month and year"
  public function calculateDayOfWeek(number $day, number $month, number $year) : string

    if(string_get_length($year) != 4)
      print_error("Year length incorrect")
      return null
    end if


    $year1 = value(string_get_substring($year, 0, 2))
    $year2 = value(string_get_substring($year, 2, 4))

    $century_part = match($year1 :
      17 => 4;
      18 => 2;
      19 => 0;
      20 => 6;
      21 => 4;
      22 => 2;
      23 => 0;
    )

    $month_part = match($month :
      1 => 0;
      2 => 3;
      3 => 3;
      4 => 6;
      5 => 1;
      6 => 4;
      7 => 6;
      8 => 2;
      9 => 5;
      10 => 0;
      11 => 3;
      12 => 5;
    )

    $year_part = (($year2 / 4) + $year2) % 7

    $res = floor($year_part + $century_part + $month_part + $day) % 7

    $out = -1

    when($res)
      is 0
        $out = "Sunday"
      is 1
        $out = "Monday"
      is 2
        $out = "Tuesday"
      is 3
        $out = "Wednesday"
      is 4
        $out = "Thursday"
      is 5
        $out = "Friday"
      is 6
        $out = "Saturday"
    end when

    return $out

  end function

  @author "Jamie Balfour"
  @date "21 April 2023"
  @doc "Checks if a number is an anagram of another word"
  public function isAnagramOf(string $word1, string $word2) : boolean
    if(string_get_length($word1) > string_get_length($word2))
      for each(string_to_lowercase($word1) as $char)
        if(!string_contains(string_to_lowercase($word2), $char))
      	  return false
        end if
      end for
    else
      for each(string_to_lowercase($word2) as $char)
        if(!string_contains(string_to_lowercase($word1), $char))
      	  return false
        end if
      end for
    end if

    return true

  end function

  @author "Jamie Balfour"
  @date "21 April 2023"
  @doc "Flips any number into it's opposite form"
  public function flip_number(mixed $n) : number

    return 0 - $n

  end function

  @author "Jamie Balfour"
  @date "21 April 2023"
  @doc "Used to calculate how many channels there are in a mesh of n items"
  public function meshCalculator(number $n) : number

    $channels = ($n /2 ) * ($n - 1)
    return $channels

  end function

  @author "Jamie Balfour"
  @date "11 November 2023"
  @doc "A Caesar Cipher algorithm - either simple or advanced"
  public function caesarCipherEncrypt($word, $key, $advanced)


    function caesarCipherEncryptSimple($word, $shift) : string

      $output = ""
      $upperA = character_to_integer("A")

      for each($word as $char)

        //Take upper A's ASCII value away as we only want alphabetic symbols
        $i = (character_to_integer($char) - $upperA + $shift) % 52
        //Now readd the upper A's ASCII value to get an alphabetic symbol
        $output = $output & integer_to_character($i  + $upperA)

      end for

      return $output

    end function

    function caesarCipherEncryptAdvanced($word, $key) : string

      $output = ""
      $position = 0

      for each($word as $char)

        if($char == " ")
          $output = $output & " "
        else
          $shift = character_to_integer($key[$position % string_get_length($key)])

          $i = character_to_integer($char) + $shift
          //Now readd the upper A's ASCII value to get an alphabetic symbol
          $output = $output & integer_to_character($i)

          $position++
        end if



      end for

      return $output

    end function

    if($advanced)
      return caesarCipherEncryptAdvanced($word, $key)
    else
      return caesarCipherEncryptSimple($word, $key)
    end if

  end function

  @author "Jamie Balfour"
  @date "11 November 2023"
  @doc "An advanced Caesar Cipher decryption that uses an list of shifts from a word"
  public function caesarCipherDecryptAdvanced($word, $key) : string

    $output = ""
    $position = 0

    for each($word as $char)

      if($char == " ")
        $output = $output & " "
      else


        $shift = character_to_integer($key[$position % string_get_length($key)])

        $i = character_to_integer($char) - $shift
        //Now re-add the upper A's ASCII value to get an alphabetic symbol
        $output = $output & integer_to_character($i)

        $position++
      end if

    end for

    return $output

  end function

  @author "Jamie Balfour"
  @date "16 December 2023"
  @doc "Calculates the root mean square of a set of numbers"
  public function calculateRootMeanSquare($l) : number

    $total = 0
    for each($l as $num)

      $total = $total + ($num^2)

    end for

    $total = (1 / count($l)) * $total

    return square_root($total)

  end function

  @author "Jamie Balfour"
  @date "17 May 2024"
  @doc "Encodes some text in Morse Code"
  public function morseCodeEncode(string $text) : string
    $text = string_to_uppercase($text)
    $MORSE_CODE = [
     "A" => ".-",
     "B" => "-...",
     "C" => "-.-.",
     "D" => "-..",
     "E" => ".",
     "F" => "..-.",
     "G" => "--.",
     "H" => "....",
     "I" => "..",
     "J" => ".---",
     "K" => "-.-",
     "L" => ".-..",
     "M" => "--",
     "N" => "-.",
     "O" => "---",
     "P" => ".--.",
     "Q" => "--.-",
     "R" => ".-.",
     "S" => "...",
     "T" => "-",
     "U" => "..-",
     "V" => "...-",
     "W" => ".--",
     "X" => "-..-",
     "Y" => "-.--",
     "Z" => "--..",
     "1" => ".----",
     "2" => "..---",
     "3" => "...--",
     "4" => "....-",
     "5" => ".....",
     "6" => "-....",
     "7" => "--...",
     "8" => "---..",
     "9" => "----.",
     "0" => "-----",
     " " => "/"
    ]

    $encoded = ""
    for each($ch in $text)
      if(map_contains($MORSE_CODE, $ch))
        $encoded = $encoded & $MORSE_CODE[$ch] & " "

      end if

    end for

    return $encoded
  end function

  @author "Jamie Balfour"
  @date "17 May 2024"
  @doc "Decodes some Morse Code to text"
  public function morseCodeDecode(string $encoded) : string
    $MORSE_CODE =[
     ".-" => "A",
     "-..." => "B",
     "-.-." => "C",
     "-.." => "D",
     "." => "E",
     "..-." => "F",
     "--." => "G",
     "...." => "H",
     ".." => "I",
     ".---" => "J",
     "-.-" => "K",
     ".-.." => "L",
     "--" => "M",
     "-." => "N",
     "---" => "O",
     ".--." => "P",
     "--.-" => "Q",
     ".-." => "R",
     "..." => "S",
     "-" => "T",
     "..-" => "U",
     "...-" => "V",
     ".--" => "W",
     "-..-" => "X",
     "-.--" => "Y",
     "--.." => "Z",
     ".----" => "1",
     "..---" => "2",
     "...--" => "3",
     "....-" => "4",
     "....." => "5",
     "-...." => "6",
     "--..." => "7",
     "---.." => "8",
     "----." => "9",
     "-----" => "0",
     "/" => " "
    ]
    $text = ""
    for each($seq in string_split($encoded, " "))
      if(map_contains($MORSE_CODE, $seq))
        $text = $text & $MORSE_CODE[$seq]
      end if

    end for

    return $text
  end function

  @author "Jamie Balfour"
  @date "17 May 2024"
  @doc "Multiplies two precisely square matrices together, producing a third matrix with the results"
  public function multiplySquareMatrices($mat1, $mat2) : list
    $result = []
    $num = count($mat1);
    for($i = 0 to $num){
      $result[$i] = []
    }
    for ($i = 0 to $num)
      for ($j = 0 to $num)
        $result[$i][$j] = 0
        for ($k = 0 to $num)
          $result[$i][$j] = $result[$i][$j] + ($mat1[$i][$k] * $mat2[$k][$j])
        end for
      end for
    end for

    return $result
  end function

  @author "Jamie Balfour"
  @date "29 June 2025"
  @doc "Simple Ackermann function"
  public function ackermann_function($m, $n) : number
    if ($m == 0)
      // A(0,n) = n + 1
      return $n + 1

    elseif ($n == 0)
      // A(m,0) = A(m-1,1)
      return ackermann_function($m - 1, 1)

    else
      // A(m,n) = A(m-1, A(m,n-1))
      return ackermann_function($m - 1, ackermann_function($m, $n - 1))
    end if
  end function

  @author "Jamie Balfour"
  @date "04 October 2025"
  @doc "KMP string search: returns the first index of pattern in text, or -1 if not found."
  public function kmp_indexOf(string $text, string $pattern) : number

    $n = string_get_length($text)
    $m = string_get_length($pattern)

    if($m == 0)
      return 0
    end if
    if($n == 0 || $m > $n)
      return -1
    end if

    // Build LPS (longest proper prefix which is also suffix) array for pattern
    function build_lps(string $p) : list
      $m = string_get_length($p)
      $lps = []
      // initialise with zeros
      for($i = 0; $i < $m; $i++)
        $lps = list_add_element($lps, 0)
      end for

      $len = 0    // length of current longest prefix-suffix
      $i = 1
      while($i < $m)
        if($p[$i] == $p[$len])
          $len++
          $lps = list_set_at_index($lps, $i, $len)
          $i++
        else
          if($len != 0)
            $len = $lps[$len - 1]
          else
            // no prefix-suffix, stays 0
            $lps = list_set_at_index($lps, $i, 0)
            $i++
          end if
        end if
      end while

      return $lps
    end function

    $lps = build_lps($pattern)

    // Scan text using the lps table
    $i = 0 // index in text
    $j = 0 // index in pattern
    while($i < $n)
      if($text[$i] == $pattern[$j])
        $i++
        $j++
        if($j == $m)
          return $i - $j
        end if
      else
        if($j != 0)
          $j = $lps[$j - 1]
        else
          $i++
        end if
      end if
    end while

    return -1
  end function

  @author "Jamie Balfour"
  @date "04 October 2025"
  @doc "KMP string search (all matches): returns a list of all start indices of pattern in text."
  public function kmp_find_all(string $text, string $pattern) : list

    $n = string_get_length($text)
    $m = string_get_length($pattern)

    $results = []
    if($m == 0)
      // by convention, empty pattern matches at every position; here we return [0]
      return [0]
    end if
    if($n == 0 || $m > $n)
      return []
    end if

    // Build LPS
    function build_lps_all(string $p) : list
      $m = string_get_length($p)
      $lps = []
      for($i = 0; $i < $m; $i++)
        $lps = list_add_element($lps, 0)
      end for

      $len = 0
      $i = 1
      while($i < $m)
        if($p[$i] == $p[$len])
          $len++
          $lps = list_set_at_index($lps, $i, $len)
          $i++
        else
          if($len != 0)
            $len = $lps[$len - 1]
          else
            $lps = list_set_at_index($lps, $i, 0)
            $i++
          end if
        end if
      end while
      return $lps
    end function

    $lps = build_lps_all($pattern)

    // Scan
    $i = 0
    $j = 0
    while($i < $n)
      if($text[$i] == $pattern[$j])
        $i++
        $j++
        if($j == $m)
          $results = list_add_element($results, $i - $j)
          // Continue searching for next match (allows overlaps)
          $j = $lps[$j - 1]
        end if
      else
        if($j != 0)
          $j = $lps[$j - 1]
        else
          $i++
        end if
      end if
    end while

    return $results
  end function



  @author "Jamie Balfour"
  @date "04 October 2025"
  @doc "Convenience: returns {path, distance} from source to target using outputs of Dijkstra. Built with ChatGPT."
  public function dijkstra_path(map $graph, mixed $source, mixed $target) : map

    function dijkstra_shortest_paths(map $graph, mixed $source) : map

      // Collect nodes
      $nodes = map_get_keys($graph)

      // Distances, predecessors, visited set
      $INF = max_integer() // 1e15 as a practical 'infinity'
      $dist = {=>}
      $prev = {=>}
      $visited = {=>}

      for each($nodes as $u)
        $dist = $dist.put($u, $INF)
        $prev = $prev.put($u, null)
        $visited = $visited.put($u, false)
      end for

      if(map_contains($dist, $source) == false)
        // If source isn't in graph, initialise it
        $dist = $dist.put($source, 0)
        $prev = $prev.put($source, null)
        $nodes = list_add_element($nodes, $source)
      else
        $dist = $dist.put($source, 0)
      end if

      // Unvisited list supports linear min-extract
      $unvisited = $nodes

      while(list_get_length($unvisited) > 0)

        // Find $u in unvisited with minimum dist[$u]
        $minIdx = 0
        $minNode = $unvisited[0]
        $minDist = $dist[$minNode]

        for($i = 1; $i < list_get_length($unvisited); $i++)
          $cand = $unvisited[$i]
          if($dist[$cand] < $minDist)
            $minDist = $dist[$cand]
            $minNode = $cand
            $minIdx = $i
          end if
        end for

        // Remove $u from unvisited
        $u = $minNode
        $unvisited = list_remove_element($unvisited, $minIdx)

        //if($visited[$u] == true)
        //  continue
        //end if
        $visited = $visited.put($u, true)

        // Early stop if remaining are unreachable
        if($dist[$u] >= $INF)
          break
        end if

        // Relax edges u -> (v, w)
        if(map_contains($graph, $u))
          $m = $graph[$u]
          for each($m as $edge)
            $v = $edge[0]
            $w = $edge[1]

            // Ensure keys exist in maps for unseen vertices
            if(map_contains($dist, $v) == false)
              $dist = $dist.put($v, $INF)
              $prev = $prev.put($v, null)
              $visited = $visited.put($v, false)
            end if

            $alt = $dist[$u] + $w
            if($alt < $dist[$v])
              $dist = $dist.put($v, $alt)
              $prev = $prev.put($v, $u)
            end if
          end for
        end if

      end while

      return {"dist" => $dist, "prev" => $prev}
    end function

    $res = dijkstra_shortest_paths($graph, $source)
    $dist = $res["dist"]
    $prev = $res["prev"]

    // Rebuild path by walking predecessors from target back to source
    $rev = []
    $cur = $target

    if(map_contains($dist, $cur) == false || $dist[$cur] == max_integer())
      // Unreachable or not present
      return {"path" => [], "distance" => -1}
    end if

    while($cur != null)
      $rev = list_add_element($rev, $cur)
      if(map_contains($prev, $cur) == false)
        break
      end if
      $cur = $prev[$cur]
    end while

    // Reverse $rev into $path
    $path = []
    for($i = list_get_length($rev) - 1; $i >= 0; $i--)
      $path = list_add_element($path, $rev[$i])
    end for

    return {"path" => $path, "distance" => $dist[$target]}
  end function

end module
	
Output