///|
fn valid_name(name : String) -> Bool {
  name.code_units().all(fn(c) { c >= 32 && c != 65534 && c != 65535 })
}

///|
/// Verify model invariants without modifying the net.
pub fn PetriNet::validate(self : PetriNet) -> Result[Unit, Array[PetriError]] {
  let errors : Array[PetriError] = []
  if self.initial.length() != self.places.length() {
    errors.push(InvalidMarking)
  }
  let names : Map[String, Bool] = Map([])
  for name in self.places {
    if name.trim() == "" {
      errors.push(EmptyName)
    }
    if !valid_name(name) {
      errors.push(InvalidName)
    }
    if names.contains(name) {
      errors.push(DuplicateName(name))
    }
    names[name] = true
  }
  for tokens in self.initial {
    if tokens < 0 {
      errors.push(NegativeTokens)
    }
  }
  let transitions : Map[String, Bool] = Map([])
  for t in 0..= self.places.length() {
          errors.push(InvalidPlace)
        }
        if arc.transition != t {
          errors.push(InvalidTransition)
        }
        if arc.weight <= 0 {
          errors.push(InvalidWeight)
        }
        if seen.contains(arc.place) {
          errors.push(InvalidWeight)
        }
        seen[arc.place] = true
      }
    }
  }
  if errors.is_empty() {
    Ok(())
  } else {
    Err(errors)
  }
}

///|
/// Resolve a transition's unique domain name, not its PNML serialization ID.
pub fn PetriNet::find_transition(self : PetriNet, name : String) -> Int? {
  for t in 0.. Int? {
  self.pnml_transition_ids.get(id)
}