Procházet zdrojové kódy

Modernize advent of code examples (#6496)

* Use generics in more places.
* Use `ref` in more places.
* Use `for` in more places.
* Use a little bit of C++ interop.
* Use an adapter for the `char`-or-EOF result of reading a char instead
of pure `i32`, and use char literals where possible.

---------

Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
Richard Smith před 4 měsíci
rodič
revize
4ddba4ab1e

+ 1 - 1
examples/advent2024/day10_common.carbon

@@ -15,7 +15,7 @@ class Terrain {
     returned var me: Terrain;
     for (y: i32 in Core.Range(43)) {
       for (x: i32 in Core.Range(43)) {
-        me.height[x][y] = ReadChar() - 0x30;
+        me.height[x][y] = ReadChar() - '0';
       }
       SkipNewline();
     }

+ 1 - 1
examples/advent2024/day10_part2.carbon

@@ -58,5 +58,5 @@ fn Run() {
   for (i: i32 in Core.InclusiveRange(0, 8)) {
     total = paths.AddLevel(terrain, 8 - i);
   }
-  PrintInt64(total);
+  PrintInt(total);
 }

+ 1 - 1
examples/advent2024/day11_part1.carbon

@@ -12,7 +12,7 @@ import library "io_utils";
 fn Run() {
   var n: i64;
   var total: i32 = 0;
-  while (ReadInt64(&n)) {
+  while (ReadInt(ref n)) {
     total += Count(n, 25);
     SkipSpaces();
   }

+ 5 - 5
examples/advent2024/day11_part2.carbon

@@ -27,7 +27,7 @@ class Digits {
       Core.PrintChar(':');
       for (depth: i32 in Core.Range(max_depth)) {
         Core.PrintChar(' ');
-        PrintInt64NoNewline(self.count[digit][depth]);
+        PrintIntNoNewline(self.count[digit][depth]);
       }
       Core.PrintChar('\n');
     }
@@ -56,16 +56,16 @@ fn Run() {
   var digits: Digits = Digits.Make();
 
   var n: i64;
-  while (ReadInt64(&n)) {
+  while (ReadInt(ref n)) {
     total += ReduceToDigits(n, max_depth, 1, &digits);
-    // PrintInt64(total);
+    // PrintInt(total);
     // digits.Print(max_depth - 1);
     SkipSpaces();
   }
 
   var depth: i32 = max_depth - 1;
   while (depth >= 0) {
-    // PrintInt64(total);
+    // PrintInt(total);
     // digits.Print(depth);
     for (digit: i32 in Core.Range(10)) {
       let m: i64 = digits.count[digit][depth];
@@ -78,5 +78,5 @@ fn Run() {
     --depth;
   }
 
-  PrintInt64(total);
+  PrintInt(total);
 }

+ 1 - 1
examples/advent2024/day12_common.carbon

@@ -15,7 +15,7 @@ class Map {
     returned var me: Self;
     for (y: i32 in Core.Range(140)) {
       for (x: i32 in Core.Range(140)) {
-        me.kind[x][y] = ReadChar();
+        me.kind[x][y] = ReadChar() as i32;
       }
       SkipNewline();
     }

+ 6 - 6
examples/advent2024/day13_common.carbon

@@ -27,22 +27,22 @@ class Machine {
     returned var me: Machine;
     // "Button A: X+"
     SkipNChars(12);
-    ReadInt64(&me.a.0);
+    ReadInt(ref me.a.0);
     // ", Y+"
     SkipNChars(4);
-    ReadInt64(&me.a.1);
+    ReadInt(ref me.a.1);
     // "\nButton B: X+"
     SkipNChars(13);
-    ReadInt64(&me.b.0);
+    ReadInt(ref me.b.0);
     // ", Y+"
     SkipNChars(4);
-    ReadInt64(&me.b.1);
+    ReadInt(ref me.b.1);
     // "\nPrize: X="
     SkipNChars(10);
-    ReadInt64(&me.prize.0);
+    ReadInt(ref me.prize.0);
     // ", Y="
     SkipNChars(4);
-    ReadInt64(&me.prize.1);
+    ReadInt(ref me.prize.1);
     SkipNewline();
     return var;
   }

+ 1 - 1
examples/advent2024/day13_part1.carbon

@@ -18,5 +18,5 @@ fn Run() {
       break;
     }
   }
-  PrintInt64(total_cost);
+  PrintInt(total_cost);
 }

+ 1 - 1
examples/advent2024/day13_part2.carbon

@@ -20,5 +20,5 @@ fn Run() {
       break;
     }
   }
-  PrintInt64(total_cost);
+  PrintInt(total_cost);
 }

+ 13 - 9
examples/advent2024/day1_common.carbon

@@ -6,18 +6,22 @@
 
 library "day1_common";
 
+import Core library "range";
+
 import library "io_utils";
 
 // Read a sequence of lines each containing a pair of numbers into two arrays.
 // Returns the number of lines read.
-fn ReadInputs(ap: array(i32, 1000)*, bp: array(i32, 1000)*) -> i32 {
-  var n: i32 = 0;
-  var a: i32;
-  var b: i32;
-  while (n < 1000 and ReadInt(&a) and SkipSpaces() and ReadInt(&b) and SkipNewline()) {
-    (*ap)[n] = a;
-    (*bp)[n] = b;
-    ++n;
+fn ReadInputs[N:! Core.IntLiteral()](ref a: array(i32, N), ref b: array(i32, N)) -> i32 {
+  for (i: i32 in Core.Range(N)) {
+    var av: i32;
+    var bv: i32;
+    if (ReadInt(ref av) and SkipSpaces() and ReadInt(ref bv) and SkipNewline()) {
+      a[i] = av;
+      b[i] = bv;
+    } else {
+      return i;
+    }
   }
-  return n;
+  return N;
 }

+ 3 - 3
examples/advent2024/day1_part1.carbon

@@ -15,9 +15,9 @@ fn Abs(n: i32) -> i32 { return if n < 0 then -n else n; }
 fn Run() {
   var a: array(i32, 1000);
   var b: array(i32, 1000);
-  var n: i32 = ReadInputs(&a, &b);
-  Quicksort(&a, 0, n);
-  Quicksort(&b, 0, n);
+  let n: i32 = ReadInputs(ref a, ref b);
+  Quicksort(ref a, 0, n);
+  Quicksort(ref b, 0, n);
 
   var difference: i32 = 0;
   for (i: i32 in Core.Range(n)) {

+ 3 - 3
examples/advent2024/day1_part2.carbon

@@ -12,9 +12,9 @@ import library "sort";
 fn Run() {
   var a: array(i32, 1000);
   var b: array(i32, 1000);
-  var n: i32 = ReadInputs(&a, &b);
-  Quicksort(&a, 0, n);
-  Quicksort(&b, 0, n);
+  let n: i32 = ReadInputs(ref a, ref b);
+  Quicksort(ref a, 0, n);
+  Quicksort(ref b, 0, n);
 
   var i: i32 = 0;
   var j: i32 = 0;

+ 1 - 1
examples/advent2024/day2_part1.carbon

@@ -43,7 +43,7 @@ class ReportState {
 fn ReadReport() -> ReportState {
   returned var report: ReportState = ReportState.Make();
   var n: i32;
-  while (ReadInt(&n)) {
+  while (ReadInt(ref n)) {
     report.Add(n);
     SkipSpaces();
   }

+ 1 - 1
examples/advent2024/day2_part2.carbon

@@ -100,7 +100,7 @@ fn ReadAndCheckReport() -> bool {
   var increasing: ReportState = ReportState.Make(true);
   var decreasing: ReportState = ReportState.Make(false);
   var n: i32;
-  while (ReadInt(&n)) {
+  while (ReadInt(ref n)) {
     window.Add(n);
     increasing.OnAdd(window);
     decreasing.OnAdd(window);

+ 7 - 7
examples/advent2024/day3_common.carbon

@@ -14,9 +14,9 @@ import library "io_utils";
 fn ReadMul() -> (bool, i32, i32) {
   var a: i32;
   var b: i32;
-  if (ConsumeChar(0x6D) and ConsumeChar(0x75) and ConsumeChar(0x6C) and
-      ConsumeChar(0x28) and ReadInt(&a) and ConsumeChar(0x2C) and
-      ReadInt(&b) and ConsumeChar(0x29)) {
+  if (ConsumeChar('m') and ConsumeChar('u') and ConsumeChar('l') and
+      ConsumeChar('(') and ReadInt(ref a) and ConsumeChar(',') and
+      ReadInt(ref b) and ConsumeChar(')')) {
     return (true, a, b);
   }
   return (false, 0, 0);
@@ -26,21 +26,21 @@ fn ReadMul() -> (bool, i32, i32) {
 // On error, stops before the first invalid character and returns (false, false).
 fn ReadDoOrDont() -> (bool, bool) {
   // "do"
-  if (not ConsumeChar(0x64) or not ConsumeChar(0x6F)) {
+  if (not ConsumeChar('d') or not ConsumeChar('o')) {
     return (false, false);
   }
 
   var do: bool = true;
   // "n't"
-  if (ConsumeChar(0x6E)) {
-    if (not ConsumeChar(0x27) or not ConsumeChar(0x74)) {
+  if (ConsumeChar('n')) {
+    if (not ConsumeChar('\'') or not ConsumeChar('t')) {
       return (false, false);
     }
     do = false;
   }
 
   // "()"
-  if (not ConsumeChar(0x28) or not ConsumeChar(0x29)) {
+  if (not ConsumeChar('(') or not ConsumeChar(')')) {
     return (false, false);
   }
   return (true, do);

+ 2 - 2
examples/advent2024/day3_part1.carbon

@@ -11,8 +11,8 @@ import library "io_utils";
 
 fn Run() {
   var total: i32 = 0;
-  while (PeekChar() != Core.EOF()) {
-    if (PeekChar() == 0x6D) {
+  while (PeekChar() != CharOrEOF.EOF()) {
+    if (PeekChar() == 'm') {
       // TODO: Use `if let` when available.
       let result: (bool, i32, i32) = ReadMul();
       if (result.0) {

+ 3 - 3
examples/advent2024/day3_part2.carbon

@@ -12,14 +12,14 @@ import library "io_utils";
 fn Run() {
   var total: i32 = 0;
   var enabled: bool = true;
-  while (PeekChar() != Core.EOF()) {
-    if (PeekChar() == 0x6D) {
+  while (PeekChar() != CharOrEOF.EOF()) {
+    if (PeekChar() == 'm') {
       // TODO: Use `if let` when available.
       let result: (bool, i32, i32) = ReadMul();
       if (result.0 and enabled) {
         total += result.1 * result.2;
       }
-    } else if (PeekChar() == 0x64) {
+    } else if (PeekChar() == 'd') {
       // TODO: Use `if let` when available.
       let result: (bool, bool) = ReadDoOrDont();
       if (result.0) {

+ 1 - 1
examples/advent2024/day4_common.carbon

@@ -17,7 +17,7 @@ class Wordsearch {
       var x: i32 = 0;
       while (x < 140) {
         // TODO: Assert on failure.
-        s.grid[x][y] = ReadChar();
+        s.grid[x][y] = ReadChar() as i32;
         ++x;
       }
       // TODO: Assert on failure.

+ 4 - 4
examples/advent2024/day5_common.carbon

@@ -24,7 +24,7 @@ class Rules {
 
     var a: i32;
     var b: i32;
-    while (ReadInt(&a) and ConsumeChar(0x7C) and ReadInt(&b)) {
+    while (ReadInt(ref a) and ConsumeChar('|') and ReadInt(ref b)) {
       rules.disallowed_before[a] |= PageMask(b);
       SkipNewline();
     }
@@ -49,12 +49,12 @@ class PageList {
     returned var me: PageList = Empty();
 
     var page: i32;
-    if (not ReadInt(&page)) {
+    if (not ReadInt(ref page)) {
       return var;
     }
     me.Add(page);
-    while (ConsumeChar(0x2C)) {
-      ReadInt(&page);
+    while (ConsumeChar(',')) {
+      ReadInt(ref page);
       me.Add(page);
     }
     SkipNewline();

+ 4 - 4
examples/advent2024/day7_common.carbon

@@ -26,10 +26,10 @@ class Equation {
   fn Read() -> Equation {
     returned var me: Equation;
     me.num_operands = 0;
-    ReadInt64(&me.result);
-    ConsumeChar(0x3A);
-    while (ConsumeChar(0x20)) {
-      ReadInt64(&me.operands[me.num_operands]);
+    ReadInt(ref me.result);
+    ConsumeChar(':');
+    while (ConsumeChar(' ')) {
+      ReadInt(ref me.operands[me.num_operands]);
       ++me.num_operands;
     }
     while (SkipNewline()) {}

+ 2 - 2
examples/advent2024/day7_part1.carbon

@@ -11,11 +11,11 @@ import library "io_utils";
 
 fn Run() {
   var total: i64 = 0;
-  while (PeekChar() != Core.EOF()) {
+  while (PeekChar() != CharOrEOF.EOF()) {
     var eq: Equation = Equation.Read();
     if (eq.Solve(false)) {
       total += eq.result;
     }
   }
-  PrintInt64(total);
+  PrintInt(total);
 }

+ 2 - 2
examples/advent2024/day7_part2.carbon

@@ -11,11 +11,11 @@ import library "io_utils";
 
 fn Run() {
   var total: i64 = 0;
-  while (PeekChar() != Core.EOF()) {
+  while (PeekChar() != CharOrEOF.EOF()) {
     var eq: Equation = Equation.Read();
     if (eq.Solve(true)) {
       total += eq.result;
     }
   }
-  PrintInt64(total);
+  PrintInt(total);
 }

+ 1 - 1
examples/advent2024/day8_common.carbon

@@ -15,7 +15,7 @@ class Grid {
     returned var me: Grid;
     for (y: i32 in Core.Range(50)) {
       for (x: i32 in Core.Range(50)) {
-        me.data[x][y] = ReadChar();
+        me.data[x][y] = ReadChar() as i32;
       }
       SkipNewline();
     }

+ 3 - 3
examples/advent2024/day9_common.carbon

@@ -17,10 +17,10 @@ class SectorList {
     var sector: i32 = 0;
     var used: bool = true;
     // TODO: An assignment expression would be useful here.
-    var c: i32 = ReadChar();
-    while (c != Core.EOF() and c != 0xA) {
+    var c: CharOrEOF = ReadChar();
+    while (c != CharOrEOF.EOF() and c != '\n') {
       let v: i32 = if used then sector else -1;
-      for (_: i32 in Core.Range(c - 0x30)) {
+      for (_: i32 in Core.Range(c - '0')) {
         me.data[me.size] = v;
         ++me.size;
       }

+ 1 - 1
examples/advent2024/day9_part1.carbon

@@ -12,5 +12,5 @@ import library "io_utils";
 fn Run() {
   var list: SectorList = SectorList.Read();
   list.DefragBlocks();
-  PrintInt64(list.Checksum());
+  PrintInt(list.Checksum());
 }

+ 1 - 1
examples/advent2024/day9_part2.carbon

@@ -12,5 +12,5 @@ import library "io_utils";
 fn Run() {
   var list: SectorList = SectorList.Read();
   list.DefragFiles();
-  PrintInt64(list.Checksum());
+  PrintInt(list.Checksum());
 }

+ 106 - 54
examples/advent2024/io_utils.carbon

@@ -7,90 +7,142 @@ library "io_utils";
 import Core library "io";
 import Core library "range";
 
-// If non-zero, this is the most recently read character plus 2.
-// The +2 is necessary to distinguish the case of no unread character
-// from the case of unreading an EOF.
-var unread_char: i32 = 0;
-
-fn ReadChar() -> i32 {
-  if (unread_char != 0) {
-    var result: i32 = unread_char - 2;
-    unread_char = 0;
-    return result;
+class EOFType {}
+
+class CharOrEOF {
+  adapt i32;
+
+  fn EOF() -> Self;
+}
+
+impl CharOrEOF as Core.Copy {
+  fn Op[self: Self]() -> Self {
+    return (self as i32).(Core.Copy.Op)() as Self;
   }
+}
 
-  return Core.ReadChar();
+fn CharOrEOF.EOF() -> Self {
+  // Ordering between `CharOrEOF` and `char` treats EOF as less than all `char`
+  // values.
+  return (-1 as i32) as CharOrEOF;
 }
 
-fn UnreadChar(c: i32) {
-  // TODO: assert(unread_char == 0);
-  unread_char = c + 2;
+impl forall [U:! Core.ImplicitAs(char)] U as Core.ImplicitAs(CharOrEOF) {
+  fn Convert[self: char]() -> CharOrEOF {
+    return ((self as u8) as i32) as CharOrEOF;
+  }
 }
 
-// TODO: Make generic.
-fn ReadInt(p: i32*) -> bool {
-  var read_any_digits: bool = false;
-  *p = 0;
+impl forall [U:! Core.ImplicitAs(CharOrEOF)]
+    CharOrEOF as Core.EqWith(U) {
+  fn Equal[self: Self](other: CharOrEOF) -> bool {
+    return (self as i32) == (other as i32);
+  }
+  fn NotEqual[self: Self](other: CharOrEOF) -> bool {
+    return (self as i32) != (other as i32);
+  }
+}
 
-  while (true) {
-    var c: i32 = ReadChar();
-    if (c < 0x30 or c > 0x39) {
-      UnreadChar(c);
-      break;
-    }
-    // TODO: Check for overflow.
-    *p *= 10;
-    *p += c - 0x30;
-    read_any_digits = true;
+impl forall [U:! Core.ImplicitAs(CharOrEOF)]
+    CharOrEOF as Core.OrderedWith(U) {
+  fn Less[self: Self](other: CharOrEOF) -> bool {
+    return (self as i32) < (other as i32);
   }
-  return read_any_digits;
+  fn LessOrEquivalent[self: Self](other: CharOrEOF) -> bool {
+    return (self as i32) <= (other as i32);
+  }
+  fn Greater[self: Self](other: CharOrEOF) -> bool {
+    return (self as i32) > (other as i32);
+  }
+  fn GreaterOrEquivalent[self: Self](other: CharOrEOF) -> bool {
+    return (self as i32) >= (other as i32);
+  }
+}
+
+impl forall [U:! Core.ImplicitAs(char)]
+    CharOrEOF as Core.SubWith(U) where .Result = i32 {
+  fn Op[self: Self](other: char) -> i32 {
+    return (self as i32) - ((other as u8) as i32);
+  }
+}
+
+var unread_char: Core.Optional(CharOrEOF)
+  = Core.Optional(CharOrEOF).None();
+
+fn ReadChar() -> CharOrEOF {
+  if (unread_char.HasValue()) {
+    var result: CharOrEOF = unread_char.Get();
+    unread_char = Core.Optional(CharOrEOF).None();
+    return result;
+  }
+
+  var value: i32 = Core.ReadChar();
+  if (value == Core.EOF()) {
+    return CharOrEOF.EOF();
+  } else {
+    return value as CharOrEOF;
+  }
+}
+
+fn UnreadChar(c: CharOrEOF) {
+  // TODO: assert(unread_char == 0);
+  // TODO: unread_char = c;
+  unread_char = Core.Optional(CharOrEOF).Some(c);
 }
 
-fn ReadInt64(p: i64*) -> bool {
+fn ReadInt[N:! Core.IntLiteral()](ref p: Core.Int(N)) -> bool {
   var read_any_digits: bool = false;
-  *p = 0;
+  // TODO: `p = 0;` crashes the toolchain, see
+  // https://github.com/carbon-language/carbon-lang/issues/6500.
+  p = (0 as i32) as Core.Int(N);
 
   while (true) {
-    var c: i32 = ReadChar();
-    if (c < 0x30 or c > 0x39) {
+    var c: CharOrEOF = ReadChar();
+    if (c < '0' or c > '9') {
       UnreadChar(c);
       break;
     }
     // TODO: Check for overflow.
-    *p = *p * 10;
-    *p = *p + ((c - 0x30) as i64);
+    // TODO: p *= 10; crashes the toolchain.
+    p *= (10 as i32) as Core.Int(N);
+    p += (c - '0') as Core.Int(N);
     read_any_digits = true;
   }
   return read_any_digits;
 }
 
-fn PrintInt64NoNewline(n_val: i64) {
-  var pow10: i64 = 1;
-  var n: i64 = n_val;
-  while (n / 10 >= pow10) {
-    pow10 = pow10 * 10;
+fn PrintIntNoNewline[N:! Core.IntLiteral()](n_val: Core.Int(N)) {
+  // TODO: var pow10: Core.Int(N) = 1; crashes the toolchain.
+  var pow10: Core.Int(N) = (1 as i32) as Core.Int(N);
+  var n: Core.Int(N) = n_val;
+  // TODO: let ten: Core.Int(N) = 10;
+  let ten: Core.Int(N) = (10 as i32) as Core.Int(N);
+  while (n / ten >= pow10) {
+    pow10 = pow10 * ten;
   }
-  while (pow10 != 0) {
-    let d: i64 = n / pow10;
-    Core.PrintChar(((d + 0x30) as u8) as char);
+  // TODO: while (pow10 != 0) {
+  while (pow10 != ((0 as i32) as Core.Int(N))) {
+    let d: Core.Int(N) = n / pow10;
+    // TODO: Core.PrintChar('0' + d);
+    Core.PrintChar(((d as u8) + (('0' as char) as u8)) as char);
     n = n % pow10;
-    pow10 = pow10 / 10;
+    pow10 = pow10 / ten;
   }
 }
 
-fn PrintInt64(n_val: i64) {
-  PrintInt64NoNewline(n_val);
+fn PrintInt[N:! Core.IntLiteral()](n_val: Core.Int(N)) {
+  PrintIntNoNewline(n_val);
   Core.PrintChar('\n');
 }
 
-fn PeekChar() -> i32 {
-  var next: i32 = ReadChar();
+fn PeekChar() -> CharOrEOF {
+  var next: CharOrEOF = ReadChar();
   UnreadChar(next);
   return next;
 }
 
-fn ConsumeChar(c: i32) -> bool {
-  var next: i32 = ReadChar();
+fn ConsumeChar(c: char) -> bool {
+  var next: CharOrEOF = ReadChar();
   if (next != c) {
     UnreadChar(next);
     return false;
@@ -100,7 +152,7 @@ fn ConsumeChar(c: i32) -> bool {
 
 fn SkipSpaces() -> bool {
   var skipped_any_spaces: bool = false;
-  while (ConsumeChar(0x20)) {
+  while (ConsumeChar(' ')) {
     skipped_any_spaces = true;
   }
   return skipped_any_spaces;
@@ -108,15 +160,15 @@ fn SkipSpaces() -> bool {
 
 fn SkipNewline() -> bool {
   // Optional carriage return.
-  ConsumeChar(0x0D);
+  ConsumeChar('\r');
   // Newline.
   // TODO: Unread the CR if it was present?
-  return ConsumeChar(0x0A);
+  return ConsumeChar('\n');
 }
 
 fn SkipNChars(n: i32) -> bool {
   for (_: i32 in Core.Range(n)) {
-    if (ReadChar() == Core.EOF()) {
+    if (ReadChar() == CharOrEOF.EOF()) {
       return false;
     }
   }

+ 35 - 17
examples/advent2024/sort.carbon

@@ -4,40 +4,58 @@
 
 library "sort";
 
-// TODO: Generalize this for other container types once we implement lowering
-// for generic functions.
+interface Ordered {
+  // extend require impls Core.OrderedWith(Self);
+  fn Less[self: Self](other: Self) -> bool;
+  fn LessOrEquivalent[self: Self](other: Self) -> bool;
+  fn Greater[self: Self](other: Self) -> bool;
+  fn GreaterOrEquivalent[self: Self](other: Self) -> bool;
+}
+
+impl i32 as Ordered {
+  fn Less[self: Self](other: Self) -> bool { return self < other; }
+  fn LessOrEquivalent[self: Self](other: Self) -> bool { return self <= other; }
+  fn Greater[self: Self](other: Self) -> bool { return self > other; }
+  fn GreaterOrEquivalent[self: Self](other: Self) -> bool { return self >= other; }
+}
 
-fn Swap(p: array(i32, 1000)*, from: i32, to: i32) {
-  var tmp: i32 = (*p)[from];
-  (*p)[from] = (*p)[to];
-  (*p)[to] = tmp;
+fn Swap[T:! Core.Copy & Core.Destroy](ref from: T, ref to: T) {
+  var tmp: T = from;
+  from = to;
+  to = tmp;
 }
 
-fn Partition(p: array(i32, 1000)*, from_in: i32, to_in: i32) -> i32 {
+fn Partition
+    [T:! Core.Copy & Core.Destroy & Ordered, N:! Core.IntLiteral()]
+    (ref a: array(T, N), from_in: i32, to_in: i32) -> i32 {
   var pivot_index: i32 = from_in;
-  var pivot: i32 = (*p)[pivot_index];
+  let pivot: T = a[pivot_index];
   var from: i32 = from_in + 1;
   var to: i32 = to_in;
   while (from < to) {
-    if ((*p)[from] <= pivot) {
+    // TODO: if (a[from] <= pivot) {
+    if (a[from].(Ordered.LessOrEquivalent)(pivot)) {
       ++from;
-    } else if ((*p)[to - 1] > pivot) {
+    // TODO: } else if ((*p)[to - 1] > pivot) {
+    } else if (a[to - 1].(Ordered.Greater)(pivot)) {
       --to;
     } else {
       // Element at `from` is > pivot, and
-      // element at `to` is <= pivot.
-      Swap(p, from, to - 1);
+      // element at `to - 1` is <= pivot.
+      Swap(ref a[from], ref a[to - 1]);
       ++from;
       --to;
     }
   }
-  Swap(p, pivot_index, from - 1);
+  Swap(ref a[pivot_index], ref a[from - 1]);
   return from - 1;
 }
 
-fn Quicksort(p: array(i32, 1000)*, from: i32, to: i32) {
+fn Quicksort
+    [T:! Core.Copy & Core.Destroy & Ordered, N:! Core.IntLiteral()]
+    (ref a: array(T, N), from: i32, to: i32) {
   if (from + 1 >= to) { return; }
-  var pivot: i32 = Partition(p, from, to);
-  Quicksort(p, from, pivot);
-  Quicksort(p, pivot + 1, to);
+  var pivot: i32 = Partition(ref a, from, to);
+  Quicksort(ref a, from, pivot);
+  Quicksort(ref a, pivot + 1, to);
 }