<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def shortproc(st, lowcut, highcut, filt_order, samp_rate, debug=0, parallel=False, num_cores=False, starttime=None, endtime=None, seisan_chan_names=False, fill_gaps=True):
 """ Basic function to bandpass and downsample. Works in place on data. This is employed to ensure all parts of the data are processed in the same way. :type st: obspy.core.stream.Stream :param st: Stream to process :type lowcut: float :param lowcut: Low cut for bandpass in Hz :type highcut: float :param highcut: High cut for bandpass in Hz :type filt_order: int :param filt_order: Number of corners for bandpass filter :type samp_rate: float :param samp_rate: Sampling rate desired in Hz :type debug: int :param debug: Debug flag from 0-5, higher numbers = more output :type parallel: bool :param parallel: Set to True to process traces in parallel, for small numbers of traces this is often slower than serial processing, defaults to False :type num_cores: int :param num_cores: Control the number of cores for parallel processing, if set to False then this will use all the cores available. :type starttime: obspy.core.utcdatetime.UTCDateTime :param starttime: Desired data start time, will trim to this before processing :type endtime: obspy.core.utcdatetime.UTCDateTime :param endtime: Desired data end time, will trim to this before processing :type seisan_chan_names: bool :param seisan_chan_names: Whether channels are named like seisan channels (which are two letters rather than SEED convention of three) - defaults to True. :type fill_gaps: bool :param fill_gaps: Whether to pad any gaps found with zeros or not. :return: Processed stream :rtype: :class:`obspy.core.stream.Stream` .. note:: If your data contain gaps you should *NOT* fill those gaps before using the pre-process functions. The pre-process functions will fill the gaps internally prior to processing, process the data, then re-fill the gaps with zeros to ensure correlations are not incorrectly calculated within gaps. If your data have gaps you should pass a merged stream without the `fill_value` argument (e.g.: `st = st.merge()`). .. warning:: If you intend to use this for processing templates you should consider how resampling will impact your cross-correlations. Minor differences in resampling between day-long files (which you are likely to use for continuous detection) and shorter files will reduce your cross-correlations! .. rubric:: Example, bandpass AF.LABE..SHZ | 2013-09-01T04:10:35.700000Z - 2013-09-01T04:12:05.650000Z \ | 20.0 Hz, 1800 samples .. rubric:: Example, low-pass AF.LABE..SHZ | 2013-09-01T04:10:35.700000Z - 2013-09-01T04:12:05.650000Z \ | 20.0 Hz, 1800 samples .. rubric:: Example, high-pass AF.LABE..SHZ | 2013-09-01T04:10:35.700000Z - 2013-09-01T04:12:05.650000Z \ | 20.0 Hz, 1800 samples """

    if isinstance(st, Trace):
        tracein = True
        st = Stream(st)
    else:
        tracein = False
    # Add sanity check for filter
    if highcut and highcut >= 0.5 * samp_rate:
        raise IOError('Highcut must be lower than the nyquist')
    if debug > 4:
        parallel = False
    length = None
    clip = False
    if starttime is not None and endtime is not None:
        for tr in st:
            tr.trim(starttime, endtime)
            if len(tr.data) == ((endtime - starttime) *
                                tr.stats.sampling_rate) + 1:
                tr.data = tr.data[1:len(tr.data)]
        length = endtime - starttime
        clip = True
    elif starttime:
        for tr in st:
            tr.trim(starttime=starttime)
    elif endtime:
        for tr in st:
            tr.trim(endtime=endtime)
    for tr in st:
        if len(tr.data) == 0:
            st.remove(tr)
            debug_print('No data for %s.%s after trim' %
                        (tr.stats.station, tr.stats.channel), 1, debug)
    if parallel:
        if not num_cores:
            num_cores = cpu_count()
        if num_cores > len(st):
            num_cores = len(st)
        pool = Pool(processes=num_cores)
        results = [pool.apply_async(process, (tr,), {
            'lowcut': lowcut, 'highcut': highcut, 'filt_order': filt_order,
            'samp_rate': samp_rate, 'debug': debug, 'starttime': starttime,
            'clip': clip, 'seisan_chan_names': seisan_chan_names,
            'fill_gaps': fill_gaps, 'length': length})
                   for tr in st]
        pool.close()
        try:
            stream_list = [p.get() for p in results]
        except KeyboardInterrupt as e:  # pragma: no cover
            pool.terminate()
            raise e
        pool.join()
        st = Stream(stream_list)
    else:
        for i, tr in enumerate(st):
            st[i] = process(
                tr=tr, lowcut=lowcut, highcut=highcut, filt_order=filt_order,
                samp_rate=samp_rate, debug=debug, starttime=starttime,
                clip=clip, seisan_chan_names=seisan_chan_names,
                fill_gaps=fill_gaps, length=length)
    if tracein:
        st.merge()
        return st[0]
    return st