fn make_jac(baseMVA, bus, branch, gen, fullJac) {
    // build Ybus
    [Ybus, _, _] = make_y_bus(baseMVA, bus, branch);
    
    // extract voltage
    V = slice(bus, [0], [VM-1,VM]) .* exp(c(0,1) * pi/180 * slice(bus, [0], [VA-1,VA]));
    nb = length(V);
    
    // make sure we use generator setpoint voltage for PV and slack buses
    on = find(slice(gen, [0], [GEN_STATUS-1, GEN_STATUS]) > 0);  // which generators are on?
    gbus = get_multi(slice(gen, [0], [GEN_BUS-1, GEN_BUS]), on) - 1; // what buses are they at?
    bus_type = slice(bus, [0], [BUS_TYPE-1,BUS_TYPE]);
    gbus_type = get_multi(bus_type, gbus);
    k = find(gbus_type == PV || gbus_type == REF);
    index = get_multi(gbus, k);
    voltage_g = get_multi(V, index);
    on_k = get_multi(on, k);
    gen_vg = get_multi(slice(gen, [0], [VG-1,VG]), on_k);
    V = set(V, index, gen_vg ./ abs(voltage_g) .* voltage_g);

    // build Jacobian
    ds_dv = dSbus_dV(Ybus, V, 0);

    dSbus_dVa = slice(ds_dv, [0], [0,nb]);
    dSbus_dVm = slice(ds_dv, [0], [nb,2*nb]);

    // fulljac
    if fullJac {
        j11 = real(dSbus_dVa);
        j12 = real(dSbus_dVm);
        j21 = imag(dSbus_dVa);
        j22 = imag(dSbus_dVm);
    } else {
        pv = bustypes_pv(bus, gen);
        pq = bustypes_pq(bus, gen);
        pv_pq_i = horzcat(pv, pq);
        j11 = real(select(dSbus_dVa, pv_pq_i, pv_pq_i));
        j12 = real(select(dSbus_dVm, pv_pq_i, pq));
        j21 = imag(select(dSbus_dVa, pq, pv_pq_i));
        j22 = imag(select(dSbus_dVm, pq, pq));
    }

    jac = vertcat(
        horzcat(j11, j12),
        horzcat(j21, j22)
    );

    return jac;
}