* feat: add configurable SMTP HELO hostname Allow the SMTP HELO/EHLO hostname to be configured separately from the SMTP server hostname. This is useful when the SMTP server requires clients to identify themselves with a fully qualified hostname different from the server address. * chore: remove vendored dependency changes * Bump go-pkgz/notify to v1.4.0 and document SMTP_HELO_HOST The HELOHost field lands in go-pkgz/notify v1.4.0, so the branch needs the bump to compile; v1.3.0 in master has no such field. The example module is tidied alongside, as any change to backend/go.mod requires. Documents the parameter in the parameters table and, separately, in the email setup page: what it does, that leaving it unset keeps the previous `localhost` greeting, and the case it exists for, a relay refusing the greeting under Postfix `reject_non_fqdn_helo_hostname`. Also records the current limit: verification emails for email authentication go through go-pkgz/auth's own sender, which has no equivalent setting, so the greeting there is unchanged. * Bump go-pkgz/auth to v2.2.0 and apply SMTP_HELO_HOST to verification email The verification email sender had no way to set the greeting, so a relay that refuses the HELO would accept notifications and still reject sign-in emails. EmailParams gains HELOHost in go-pkgz/auth v2.2.0, so the same SMTP_HELO_HOST now drives both paths. The example module is tidied alongside, as any change to backend/go.mod requires. --------- Co-authored-by: oli <someone@somewhere.tld> Co-authored-by: Dmitry Verkhoturov <paskal.07@gmail.com>
68 lines
2.0 KiB
Go
68 lines
2.0 KiB
Go
package stats
|
|
|
|
import (
|
|
"math"
|
|
"sort"
|
|
)
|
|
|
|
// Interp calculates the one-dimensional piecewise-linear interpolant to a
|
|
// function with given discrete data points (xp, fp), evaluated at each x.
|
|
// Values of x below xp[0] return fp[0] and values above xp[len(xp)-1] return
|
|
// fp[len(xp)-1], so no extrapolation is performed. Unlike numpy's interp,
|
|
// which silently returns nonsense for unsorted coordinates, xp must be
|
|
// strictly increasing or ErrBounds is returned. An empty x or xp returns
|
|
// ErrEmptyInput and xp and fp of different lengths return ErrSize.
|
|
// A NaN in xp returns ErrBounds and a NaN in x gives a NaN in the output.
|
|
func Interp(x, xp, fp Float64Data) ([]float64, error) {
|
|
|
|
if x.Len() == 0 || xp.Len() == 0 {
|
|
return nil, ErrEmptyInput
|
|
}
|
|
|
|
if xp.Len() != fp.Len() {
|
|
return nil, ErrSize
|
|
}
|
|
|
|
// NaN loses every comparison, so the ordering check can't catch it
|
|
for i := 0; i < xp.Len(); i++ {
|
|
if math.IsNaN(xp[i]) || (i > 0 && xp[i] <= xp[i-1]) {
|
|
return nil, ErrBounds
|
|
}
|
|
}
|
|
|
|
output := make([]float64, x.Len())
|
|
|
|
for i, xv := range x {
|
|
switch {
|
|
case math.IsNaN(xv):
|
|
output[i] = math.NaN()
|
|
case xv <= xp[0]:
|
|
output[i] = fp[0]
|
|
case xv >= xp[xp.Len()-1]:
|
|
output[i] = fp[fp.Len()-1]
|
|
default:
|
|
// The first index with xp[j] >= xv, which the clamping
|
|
// above guarantees is within [1, len(xp)-1]
|
|
j := sort.SearchFloat64s(xp, xv)
|
|
if xv == xp[j] {
|
|
// An exact knot hit returns fp[j] exactly, with no
|
|
// interpolation arithmetic that could lose precision
|
|
output[i] = fp[j]
|
|
continue
|
|
}
|
|
t := (xv - xp[j-1]) / (xp[j] - xp[j-1])
|
|
if math.IsInf(xp[j]-xp[j-1], 1) {
|
|
// The knot spacing overflows float64, so halve each
|
|
// term before dividing; halving is exact for the huge
|
|
// values that make an overflowing difference possible
|
|
t = (xv/2 - xp[j-1]/2) / (xp[j]/2 - xp[j-1]/2)
|
|
}
|
|
// The symmetric form stays finite for any finite fp where
|
|
// fp[j]-fp[j-1] would overflow, since 0 < t < 1 here
|
|
output[i] = fp[j-1]*(1-t) + fp[j]*t
|
|
}
|
|
}
|
|
|
|
return output, nil
|
|
}
|