<p dir="auto">We ship two sets of wrappers for the Dierckx FITPACK library: the f2py wrappers,<code class="notranslate">dfitpack</code>, and handwritten <code class="notranslate">_fitpack</code> wrappers. The origin of duplication is long lost to history, likely traces all the way back to when <code class="notranslate">multipack</code> was a thing and numpy and scipy were not (for a historic archive of multipack, see <a href="https://github.com/matthew-brett/multipack">https://github.com/matthew-brett/multipack</a>).<br>
I'd actually be interested in hearing the story behind this.</p>
<p dir="auto">Both these sets of wrappers wrap the same fortran routines, but have somewhat differing defaults and conventions. Also over time some bug fixes were applied in one set of wrappers but not the other etc. <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="13650375" data-permission-text="Title is private" data-url="https://github.com/scipy/scipy/issues/1408" data-hovercard-type="issue" data-hovercard-url="/scipy/scipy/issues/1408/hovercard" href="https://github.com/scipy/scipy/issues/1408">#1408</a> suggests to get rid of <code class="notranslate">_fitpack</code> in favor of <code class="notranslate">dfitpack</code>. I'm not sure it's really possibly to <em>get rid</em> of it, but at least some deduplication would be a nice housekeeping.  This issue is mainly to keep track of this housekeeping, without much urgency and without timing guarantees.</p>
<p dir="auto">To a first approximation, <code class="notranslate">dfitpack</code> wrappers are used by the OOP interface, <code class="notranslate">*UnivariateSpline</code> and relatives;<br>
<code class="notranslate">_fitpack</code> is used by <code class="notranslate">splXXX</code> routines. But do see below for details.</p>
<p dir="auto">First, where is what. <code class="notranslate">dfitpack</code> wrappers are in generated by <a href="https://github.com/scipy/scipy/blob/main/scipy/interpolate/src/fitpack.pyf">https://github.com/scipy/scipy/blob/main/scipy/interpolate/src/fitpack.pyf</a>, python-level code which ends up in the top level namespace is in <code class="notranslate">_fitpack2.py</code>.<br>
<code class="notranslate">_fitpack</code> wrappers are in <a href="https://github.com/scipy/scipy/blob/main/scipy/interpolate/src/_fitpackmodule.c">https://github.com/scipy/scipy/blob/main/scipy/interpolate/src/_fitpackmodule.c</a>. The route to the top-level namespace is a bit convoluted: <code class="notranslate">splev</code> and related names are imported from <code class="notranslate">_fitpack_py.py</code>; this module is a thin layer which does essentially</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="   if isinstance(tck, BSpline):
      # dispatch to BSpline methods
   else:
      # dispatch to _fitpack_impl"><pre class="notranslate"><code class="notranslate">   if isinstance(tck, BSpline):
      # dispatch to BSpline methods
   else:
      # dispatch to _fitpack_impl
</code></pre></div>
<p dir="auto">and <code class="notranslate">_fitpack_impl.py</code> is the python layer on top of the <code class="notranslate">_fitpack</code> extension.</p>
<p dir="auto">Now, which wrappers are used where. Grepping <code class="notranslate">_fitpack2.py</code> shows these routines:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="fitpack2
========

from dfitpack
-------------

  routine                      used by

- dfitpack.fpcurf0 -- `UnivariateSpline.__init__`, `InterpolatedUnivariateSpline`
- dfitpack.fpcurf1 -- UnivariateSpline._reset_nest (`__init__`, `_set_smoothing_factor`)
- dfitpack.fpcurfm1 -- `LSQUnivariateSpline.__init__`
- dfitpack.splint -- `UnivariateSpline.integral`
- dfitpack.spalde -- `UnivariateSpline.derivatives`
- dfitpack.sproot -- `UnivariateSpline.roots`
- dfitpack.fpchec -- `LSQUnivariateSpline.__init__`

- dfitpack.parder -- `_BivariateSplineBase.__call__`
- dfitpack.bispev -- `_BivariateSplineBase.__call__`
- dfitpack.pardeu -- `_BivariateSplineBase.__call__`
- dfitpack.bispeu -- `_BivariateSplineBase.__call__`
- dfitpack.pardtc -- `_BivariateSplineBase.partial_derivative__`
- dfitpack.dblint -- `_BivariateSpline.integral`

- dfitpack.surfit_smth -- `SmoothBivariateSpline.__init__`
- dfitpack.surfit_lsq -- `LSQBivariateSpline.__init__`
- dfitpack.regrid_smth -- `RectBivariateSpline.__init__`
- dfitpack.spherefit_smth -- `SmoothSphereBivariateSpline.__init__`
- dfitpack.spherefit_lsq -- `LSQSphereBivariateSpline.__init__`
- dfitpack.regrid_smth_spher -- `RectSphereBivariateSpline.__init__`

from _fitpack_py
----------------

- _fitpack.splev -- `UnivariateSpline.__call__` -- via _fitpack_py.splev -- _fitpack_impl.splev

- splder -- `UnivariateSpline.derivative` --- _fitpack_impl.splder : this is pure python, *not*  fitpack.splder!
- splantider -- `UnivariateSpline.antiferivative` --- _fitpack_impl.splantider : this is pure python, too"><pre class="notranslate"><code class="notranslate">fitpack2
========

from dfitpack
-------------

  routine                      used by

- dfitpack.fpcurf0 -- `UnivariateSpline.__init__`, `InterpolatedUnivariateSpline`
- dfitpack.fpcurf1 -- UnivariateSpline._reset_nest (`__init__`, `_set_smoothing_factor`)
- dfitpack.fpcurfm1 -- `LSQUnivariateSpline.__init__`
- dfitpack.splint -- `UnivariateSpline.integral`
- dfitpack.spalde -- `UnivariateSpline.derivatives`
- dfitpack.sproot -- `UnivariateSpline.roots`
- dfitpack.fpchec -- `LSQUnivariateSpline.__init__`

- dfitpack.parder -- `_BivariateSplineBase.__call__`
- dfitpack.bispev -- `_BivariateSplineBase.__call__`
- dfitpack.pardeu -- `_BivariateSplineBase.__call__`
- dfitpack.bispeu -- `_BivariateSplineBase.__call__`
- dfitpack.pardtc -- `_BivariateSplineBase.partial_derivative__`
- dfitpack.dblint -- `_BivariateSpline.integral`

- dfitpack.surfit_smth -- `SmoothBivariateSpline.__init__`
- dfitpack.surfit_lsq -- `LSQBivariateSpline.__init__`
- dfitpack.regrid_smth -- `RectBivariateSpline.__init__`
- dfitpack.spherefit_smth -- `SmoothSphereBivariateSpline.__init__`
- dfitpack.spherefit_lsq -- `LSQSphereBivariateSpline.__init__`
- dfitpack.regrid_smth_spher -- `RectSphereBivariateSpline.__init__`

from _fitpack_py
----------------

- _fitpack.splev -- `UnivariateSpline.__call__` -- via _fitpack_py.splev -- _fitpack_impl.splev

- splder -- `UnivariateSpline.derivative` --- _fitpack_impl.splder : this is pure python, *not*  fitpack.splder!
- splantider -- `UnivariateSpline.antiferivative` --- _fitpack_impl.splantider : this is pure python, too
</code></pre></div>
<p dir="auto">Note that the UnivariateSpline evaluation goes through <code class="notranslate">_fitpack</code> not <code class="notranslate">dfitpack</code>!</p>
<p dir="auto">Grepping <code class="notranslate">_fitpack_impl.py</code> shows these:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="_fitpack_impl
=============

from dftipack
-------------

- dfitpack.curfit -- `splrep`    # do note that curfit.f calls fpcurf, which is wrapped separately for UnivariateSpline
- dfitpack.percur -- `splrep`
- dfitpack.dblint -- `dblint`

from _fitpack
-------------

- _fitpack.parcur -- `splPrep`
- _fitpack._spl_ -- `splev`
- _fitpack._splint -- `splint`
- _fitpack._sproot -- `sproot`
- _fitpack._spalde -- `spalde`
- _fitpack._surfit -- `bisplrep`
- _fitpack._bispev -- `bisplev`
- _fitpack._insert -- `insert`"><pre class="notranslate"><code class="notranslate">_fitpack_impl
=============

from dftipack
-------------

- dfitpack.curfit -- `splrep`    # do note that curfit.f calls fpcurf, which is wrapped separately for UnivariateSpline
- dfitpack.percur -- `splrep`
- dfitpack.dblint -- `dblint`

from _fitpack
-------------

- _fitpack.parcur -- `splPrep`
- _fitpack._spl_ -- `splev`
- _fitpack._splint -- `splint`
- _fitpack._sproot -- `sproot`
- _fitpack._spalde -- `spalde`
- _fitpack._surfit -- `bisplrep`
- _fitpack._bispev -- `bisplev`
- _fitpack._insert -- `insert`
</code></pre></div>
<p dir="auto">So, what can be deduplicated. First candidates are <code class="notranslate">splint</code>, <code class="notranslate">sproot</code> and <code class="notranslate">spalde</code>. <code class="notranslate">_fitpack._spl_</code> is used in both OOP and functional interfaces (which means that <code class="notranslate">dfitpack.splev</code> and <code class="notranslate">dfitpack.splder</code> are not used and not tested --- the swich between splder and splev can be done on the python side I think). Spline construction <code class="notranslate">_fitpack.parcur</code> can probably be replaced by <code class="notranslate">dftipack.parcur</code> but the latter is untested. <code class="notranslate">_fitpack.insert</code> is a curious beast which contains a non-trivial dance with a temporary storage, not sure how to replcate that with f2py.</p>
<p dir="auto">Things to watch out for when/if deduplicating:</p>
<ul dir="auto">
<li>f2py interface insists that <code class="notranslate">len(c) == len(t)</code> : this is not always guaranteed I think, <code class="notranslate">_fitpack</code> allows <code class="notranslate">len(c) == len(t) - k - 1</code> and ignores  trailing elements in <code class="notranslate">c</code>. This restriction should probably be relaxed to <code class="notranslate">len(c) &gt;= len(t) - k - 1</code>.</li>
<li>does f2py allow/undestand non-contiguous arrays, does it copy if not contiguous or it needs to be checked on the python side?</li>
<li>need to carefully check for default values (e.g. the default for <code class="notranslate">s</code> is different IIRC).</li>
</ul>
<p dir="auto">Is it on purpose that these lines are duplicates? If so, a comment to explain why would be welcome.</p>
<ul dir="auto">
<li><a href="https://github.com/scipy/scipy/blob/v0.14.0/scipy/stats/stats.py#L3873">https://github.com/scipy/scipy/blob/v0.14.0/scipy/stats/stats.py#L3873</a></li>
<li><a href="https://github.com/scipy/scipy/blob/v0.14.0/scipy/stats/stats.py#L3875">https://github.com/scipy/scipy/blob/v0.14.0/scipy/stats/stats.py#L3875</a></li>
</ul>