///|
/// Deterministically render the supported motion subset for review and fixtures.
pub fn serialize_gcode(program : GCodeProgram) -> String {
  let mut output = ""
  for motion in program.motions() {
    let command = match motion.kind() {
      Rapid => "G0"
      Linear => "G1"
      ClockwiseArc => "G2"
      CounterclockwiseArc => "G3"
    }
    output = output +
      command +
      " X" +
      motion.end().x().to_string() +
      " Y" +
      motion.end().y().to_string() +
      " Z" +
      motion.end().z().to_string()
    match motion.kind() {
      ClockwiseArc | CounterclockwiseArc =>
        output = output +
          " I" +
          motion.i().to_string() +
          " J" +
          motion.j().to_string()
      Rapid | Linear => ()
    }
    match motion.feed() {
      Some(feed) => output = output + " F" + feed.to_string()
      None => ()
    }
    output = output + "\n"
  }
  output
}

///|
pub fn canonical_equal(left : GCodeProgram, right : GCodeProgram) -> Bool {
  serialize_gcode(left) == serialize_gcode(right)
}