def build_thru_packs(packs, max_dv=1, thru_split=3):
    """
    Applies THRU and BY to packs to shorten output as Nastran does on
    cards like the SPOINT

    Parameters
    ----------
    packs : List[pack]
       pack : List[id_low, id_high, delta_id]
           a list representation of the min/max/delta id values
    max_dv : int; default=1
        maximum allowed delta between two values
    thru_split : int; default=3
        the length to not write THRU
        3 : [10, 11, 12] will write as '10 THRU 12'
        4 : [10, 11, 12] will write as '10 11 12'

    Returns
    -------
    singles : List[int]
        the list of singles
    doubles : List[pack]
        pack : List[varies]
            [3, THRU, 13]
            [3, THRU, 13, BY, 5]
        the double packs

    # invalid
    SET1,4000, 1, 3, THRU, 10, 20, THRU, 30

    # valid
    SET1,4000, 1
    SET1,4000, 3,  THRU, 10
    SET1,4000, 20, THRU, 30

    returns
      singles = [1]
      doubles = [[3, 'THRU', 10], [20, 'THRU', 30]]

    """
    singles = []
    doubles = []
    for first_val, last_val, by in packs:
        if first_val == last_val:
            singles.append(first_val)
        elif by == 1:
            if last_val - first_val < thru_split:
                singlei = list(range(first_val, last_val + 1, 1))
                singles += singlei
            else:
                double = [first_val, 'THRU', last_val]
                doubles.append(double)
        else:
            diff = last_val - first_val
            if max_dv == 1 or diff == by:
                singlei = list(range(first_val, last_val + by, by))
                singles += singlei
            else:
                double = [first_val, 'THRU', last_val, 'BY', by]
                doubles.append(double)
    return singles, doubles
