def generate_sequencer_program(num_measurements, task, waveform_slot=0):
    """Generates sequencer code for a given task.

    Arguments:

        num_measurements (int): number of measurements per qubit

        task (string): specifies the task the sequencer program will do
                       "dig_trigger_play_single": trigger a single waveform via dig trigger
                       "zsync_trigger_play_single": trigger a single waveform via zsync
                       "dig_trigger_play_all": trigger all waveforms via dig trigger
                       "zsync_trigger_play_all": trigger all waveforms via zsync

        waveform_slot (optional int): waveform slot to be played

    Returns:

        sequencer_program (string): sequencer program to be uploaded to the SHF

    """

    if task.lower() == "dig_trigger_play_single":
        return f"""
                repeat({num_measurements}) {{
                    waitDigTrigger(1);
                    startQA(QA_GEN_{waveform_slot}, 0x0, true,  0, 0x0);
                }}
                """
    if task.lower() == "zsync_trigger_play_single":
        return f"""
                repeat({num_measurements}) {{
                    waitZSyncTrigger();
                    startQA(QA_GEN_{waveform_slot}, 0x0, true,  0, 0x0);
                }}
                """
    if task.lower() == "dig_trigger_play_all":
        return f"""
                repeat({num_measurements}) {{
                    waitDigTrigger(1);
                    startQA(QA_GEN_ALL, QA_INT_ALL, true, 0, 0x0);
                }}
                """
    if task.lower() == "zsync_trigger_play_all":
        return f"""
                repeat({num_measurements}) {{
                    waitZSyncTrigger();
                    startQA(QA_GEN_ALL, QA_INT_ALL, true, 0, 0x0);
                }}
                """
    raise ValueError(
        f"Unsupported task: {task.lower()}. Please refer to the docstring for "
        f"more information on valid inputs."
    )