///|
fn clone_pins(pins : Map[String, Int]) -> Map[String, Int] {
  let copy : Map[String, Int] = Map([])
  for signal, pin in pins {
    copy[signal] = pin
  }
  copy
}

///|
/// Enumerate deterministic assignments that satisfy a device's pin/instance locks.
/// Call validate_devices first when consuming untrusted declarations.
pub fn candidates(
  d : Device,
  reserved_pins? : Array[Int] = [],
) -> Array[Assignment] {
  let result : Array[Assignment] = []
  let instances = match d.instance {
    Some(n) => [n]
    None => if d.kind == Pwm { [0, 1, 2, 3, 4, 5, 6, 7] } else { [0, 1] }
  }
  for instance in instances {
    let mut partials : Array[Map[String, Int]] = [Map([])]
    for signal in signals(d.kind) {
      let next : Array[Map[String, Int]] = []
      for pin in available_pins() {
        let matches_lock = match d.pins.get(signal) {
          Some(locked) => locked == pin
          None => true
        }
        if !reserved_pins.contains(pin) &&
          matches_lock &&
          supports(d.kind, instance, signal, pin) {
          for partial in partials {
            let copy = clone_pins(partial)
            copy[signal] = pin
            next.push(copy)
          }
        }
      }
      partials = next
    }
    for pins in partials {
      result.push({
        id: d.id,
        kind: d.kind,
        instance,
        pins,
        bus: d.bus,
        frequency_hz: d.frequency_hz,
        i2c_address: d.i2c_address,
      })
    }
  }
  result
}