parse_matrix_line := input -> {
  vals := input.str_split(" ")
  vals_len := vals.len
  // reverse vals
  vals_rev := {() -> &vals.pop}.as_list
  nums := vals_rev.filter_map(v -> v.parse_float).as_list
  if nums.len.eq(vals_len)
    if nums.len.signum.eq(1)
      (nums)
    else ()
  else ()
}

read_matrix_line := width -> {
  ().read_line.trim.parse_matrix_line.try(
    (line) -> {
      line := [List<Float>] line
      w := width.deref
      if w.eq(0) {
        width = line.len
      } else {
        loop {
          if line.len.subtract(w).signum.eq(-1) {
            &line.push(0.0)
          } else (())
        }
      }
      (line)
    }
    () -> ()
  )
}

read_matrix := () -> {
  width := 0
  if false &width.read_matrix_line
  { () -> &width.read_matrix_line }.as_list
}

matrix_get := (matrix, (line, col)) -> {
  matrix.get(line).try(
    () -> ()
    (line) -> line.get(col)
  )
}

leftpad := (str, l) -> {
  str := (str).concat
  d := l.subtract(str.len)
  loop {
    if d.signum.eq(1) {
      &str = (" ", str).concat
      &d = d.subtract(1)
    } else (())
  }
  str
}

matrix_print := matrix -> {
  height := matrix.len
  width := 0
  val_len := 0
  matrix.for_each(line -> {
    l := line.len
    if width.subtract(l).signum.eq(-1)
      &width = l
    line.for_each(val -> {
      l := (val).concat.len
      if val_len.subtract(l).eq(-1)
        &val_len = l
    })
  })
  &val_len = val_len.sum(1)
  (height, "x", width, "-matrix").concat.println
  matrix.for_each(line -> {
    "(".print
    line.for_each(val -> val.leftpad(val_len).print)
    " )".println
  })
}

fget := v -> v.get.try(
  () -> {
    "called fget but get returned ():".println
    v.println
    5.panic
  }
  (v) -> v
)

matrix_height := a -> a.len

matrix_width := a -> a.get(0).try(
  () -> 0
  (v) -> v.len
)

multiply_matrix := (a, b) -> {
  if a.matrix_width.eq(b.matrix_height) {
    a.map(line -> {
      {() -> (())} // an infinite iterator repeatedly returning ()
        .enumerate // an infinite iterator returning (i, ())
        .take(b.matrix_width) // an iterator returning (i, ()), i < b.matrix_width
        .map((x, ()) -> line
          .enumerate
          .map((y, val) -> val.product(b.fget(y).fget(x)))
          .sum
        ) // an iterator returning floats
        .as_list // becomes a row in the output matrix
    }).as_list
  } else {
    "cannot multiply A and B because A's width isn't B's height."
  }
}

"Enter matrix A".println
a := ().read_matrix
"Enter matrix B".println
b := ().read_matrix

"A = ".print
a.matrix_print
"B = ".print
b.matrix_print

"A * B = ".print
a.multiply_matrix(b).try(
  m -> m.matrix_print
  e -> e.println
)
