net.algart.math.functions.Func Maven / Gradle / Ivy
Show all versions of algart Show documentation
/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2024 Daniel Alievsky, AlgART Laboratory (http://algart.net)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.algart.math.functions;
import net.algart.math.IPoint;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.DoublePredicate;
/**
* Abstract mathematical function
* f(x0, x1, ..., xn-1),
* or f(x), where x is a point of the n-dimensional space.
*
* Implementations of this interface are usually immutable and
* always thread-safe: get
methods of this interface may be freely used
* while simultaneous accessing to the same instance from several threads.
* All implementations of this interface from this package are immutable.
*
* @author Daniel Alievsky
*/
public interface Func {
/**
* Identity function, just returning its first argument:
* f(x0, x1, ..., xn-1) =
* x0.
* The {@link #get} method of this object requires at least 1 argument
* and throws IndexOutOfBoundsException
if the number of arguments is 0.
*
* This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func IDENTITY = new IdentityFunc();
/**
* Updatable version of {@link #IDENTITY} function.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func.Updatable UPDATABLE_IDENTITY = new IdentityFunc.Updatable();
/**
* Maximum from several numbers:
* f(x0, x1, ..., xn-1) =
* max(x0, x1, ..., xn-1).
* The {@link #get} method of this object may process any number of arguments.
* If the number of arguments is 0, it returns Double.NEGATIVE_INFINITY
.
*
*
Unlike standard Math.max
method, this function supposes that
* max(x,y) = x>y ? x : y
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func MAX = new MaxFunc();
/**
* Minimum from several numbers:
* f(x0, x1, ..., xn-1) =
* min(x0, x1, ..., xn-1).
* The {@link #get} method of this object may process any number of arguments.
* If the number of arguments is 0, it returns Double.POSITIVE_INFINITY
.
*
*
Unlike standard Math.min
method, this function supposes that
* min(x,y) = x<y ? x : y
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func MIN = new MinFunc();
/**
* Absolute value function:
* f(x0) =
* |x0|.
* More precisely, the result of this function is
* StrictMath.abs(x[0])
.
* The {@link #get} method of this object requires at least 1 argument
* and throws IndexOutOfBoundsException
if the number of arguments is 0.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func ABS = new AbsFunc();
/**
* Absolute value of the difference of 2 numbers:
* f(x0, x1) =
* |x0-x1|.
* The {@link #get} method of this object requires at least 2 arguments
* and throws IndexOutOfBoundsException
if the number of arguments is 0 or 1.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func ABS_DIFF = new AbsDiffFunc();
/**
* Positive difference of 2 numbers:
* f(x0, x1) =
* max(x0-x1,0).
* The {@link #get} method of this object requires at least 2 arguments
* and throws IndexOutOfBoundsException
if the number of arguments is 0 or 1.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func POSITIVE_DIFF = new PositiveDiffFunc();
/**
* An instance of {@link LinearFunc} class, describing the linear function
* x0 + x1:
* {@link LinearFunc#getInstance(double, double...) LinearFunc.getInstance(0.0, 1.0, 1.0)}.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
LinearFunc X_PLUS_Y = LinearFunc.getInstance(0.0, 1.0, 1.0);
/**
* An instance of {@link LinearFunc} class, describing the linear function
* x0 - x1:
* {@link LinearFunc#getInstance(double, double...) LinearFunc.getInstance(0.0, 1.0, -1.0)}.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
LinearFunc X_MINUS_Y = LinearFunc.getInstance(0.0, 1.0, -1.0);
/**
* An instance of {@link LinearFunc} class, describing the linear function
* x1 - x0:
* {@link LinearFunc#getInstance(double, double...) LinearFunc.getInstance(0.0, -1.0, 1.0)}.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
LinearFunc Y_MINUS_X = LinearFunc.getInstance(0.0, -1.0, 1.0);
/**
* An instance of {@link LinearFunc} class, describing the linear function
* (x0 + x1)/2:
* {@link LinearFunc#getInstance(double, double...) LinearFunc.getInstance(0.0, 0.5, 0.5)}.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
LinearFunc HALF_X_PLUS_Y = LinearFunc.getInstance(0.0, 0.5, 0.5);
/**
* An instance of {@link LinearFunc} class, describing the linear function
* (x0 - x1)/2:
* {@link LinearFunc#getInstance(double, double...) LinearFunc.getInstance(0.0, 0.5, -0.5)}.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
LinearFunc HALF_X_MINUS_Y = LinearFunc.getInstance(0.0, 0.5, -0.5);
/**
* An instance of {@link LinearFunc} class, describing the linear function
* (x1 - x0)/2:
* {@link LinearFunc#getInstance(double, double...) LinearFunc.getInstance(0.0, -0.5, 0.5)}.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
LinearFunc HALF_Y_MINUS_X = LinearFunc.getInstance(0.0, -0.5, 0.5);
/**
* An instance of {@link LinearFunc} class, describing the linear function
* 1.0 - x0:
* {@link LinearFunc#getInstance(double, double...) LinearFunc.getInstance(1.0, -1.0)}.
* This instance describes logical NOT operation in a case when the arguments are bits (0 and 1 values).
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
LinearFunc REVERSE = LinearFunc.getInstance(1.0, -1.0);
/**
* Select function:
* f(x0, x1, ..., xn-1) =
* xi+1, where i is x0 cast to integer type:
* i=(int)x[0]
.
* The {@link #get} method of this object requires at least 2 arguments
* and throws IndexOutOfBoundsException
if the number of arguments is 0 or 1.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func SELECT = new SelectFunc();
/**
* Select function:
* f(x0, x1, x2, x3) =
* x0 > x1 ? x2 : x3.
* The {@link #get} method of this object requires at least 4 arguments
* and throws IndexOutOfBoundsException
if the number of arguments is 0, 1, 2 or 3.
*
*
Note: call of this function is almost equivalent to calling {@link #SELECT_IF_GREATER_OR_EQUAL} function
* with another order of the arguments:
* f(x1, x0, x3, x2),
* that is
* x1 >= x0 ? x3 : x2.
* The only difference is connected with processing Double.NaN
values of x0 and
* x1: this function will choose x3, but the corresponding call of
* {@link #SELECT_IF_GREATER_OR_EQUAL} will choose x2, because in Java any comparison with
* Double.NaN
returns false
.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func SELECT_IF_GREATER = new SelectIfGreaterFunc();
/**
* Select function:
* f(x0, x1, x2, x3) =
* x0 >= x1 ? x2 : x3.
* The {@link #get} method of this object requires at least 4 arguments
* and throws IndexOutOfBoundsException
if the number of arguments is 0, 1, 2 or 3.
*
*
Note: call of this function is almost equivalent to calling {@link #SELECT_IF_GREATER} function
* with another order of the arguments:
* f(x1, x0, x3, x2),
* that is
* x1 > x0 ? x3 : x2.
* The only difference is connected with processing Double.NaN
values of x0 and
* x1: this function will choose x3, but the corresponding call of
* {@link #SELECT_IF_GREATER} will choose x2, because in Java any comparison with
* Double.NaN
returns false
.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func SELECT_IF_GREATER_OR_EQUAL = new SelectIfGreaterOrEqualFunc();
/**
* Selecting from 8 "integer" directions on 2D plane:
* f(x0, x1) = integer code from 1 to 8.
* The result of this function is the index of one of 8 sectors, where the 2-dimensional vector
* (x0,x1) lies.
* Namely, let φ=Math.atan2(x1,x0), more precisely,
* an equivalent angle in 0°..360° range.
* The result of this function is:
*
* - 0, if 337.5° < φ ≤ 22.5° and also in the special case
* x0=x1=0.0;
* - 1, if 22.5° < φ ≤ 67.5°;
* - 2, if 67.5° < φ ≤ 112.5°;
* - 3, if 112.5° < φ ≤ 157.5°;
* - 4, if 157.5° < φ ≤ 202.5°;
* - 5, if 202.5° < φ ≤ 247.5°;
* - 6, if 247.5° < φ ≤ 292.5°;
* - 7, if 292.5° < φ ≤ 337.5°.
*
*
* (A strange formula "337.5° < φ ≤ 22.5°" just means that the direction lies
* between 337.5°=-22.5° and +22.5° — in other words, "almost rightward" along X axis.)
*
*
This function is useful while processing 2-dimensional matrices, for example, in algorithms
* like Canny edge detector.
*
*
The {@link #get} method of this object requires at least 2 arguments
* and throws IndexOutOfBoundsException
if the number of arguments is 0 or 1.
*
*
This instance is immutable and thread-safe: there are no ways to modify its settings.
*/
Func SELECT_FROM_8_DIRECTIONS_2D = new SelectFrom8Directions2DFunc();
/**
* Vectors, corresponding to 8 directions recognized by {@link #SELECT_FROM_8_DIRECTIONS_2D} function.
* More precisely, if is an unmodifiable list consisting of the following elements:
*
* - {@link IPoint#valueOf(long...) IPoint.valueOf(1,0)},
* - {@link IPoint#valueOf(long...) IPoint.valueOf(1,1)},
* - {@link IPoint#valueOf(long...) IPoint.valueOf(0,1)},
* - {@link IPoint#valueOf(long...) IPoint.valueOf(-1,1)},
* - {@link IPoint#valueOf(long...) IPoint.valueOf(-1,0)},
* - {@link IPoint#valueOf(long...) IPoint.valueOf(-1,-1)},
* - {@link IPoint#valueOf(long...) IPoint.valueOf(0,-1)},
* - {@link IPoint#valueOf(long...) IPoint.valueOf(1,-1)}.
*
*
* So, if the direction returned by {@link #SELECT_FROM_8_DIRECTIONS_2D} is n, then the corresponding
* "rounding" direction is described by
* {@link #SHIFTS_ALONG_8_DIRECTIONS_2D}.get(
n)
.
*
*
This instance is immutable and thread-safe: there are no ways to modify it.
*/
List SHIFTS_ALONG_8_DIRECTIONS_2D = Collections.unmodifiableList(
java.util.Arrays.asList(
IPoint.valueOf(1, 0),
IPoint.valueOf(1, 1),
IPoint.valueOf(0, 1),
IPoint.valueOf(-1, 1),
IPoint.valueOf(-1, 0),
IPoint.valueOf(-1, -1),
IPoint.valueOf(0, -1),
IPoint.valueOf(1, -1)));
/**
* "Updatable" mathematical function: an extension of {@link Func} interface
* allowing assigning values to the function result, that leads to
* corresponding correction of arguments.
* Usually can be implemented for one-argument functions,
* where the {@link #set set(x[], newResult)} method assigns
* to x[0]
the result of the inverse function, called for newResult
.
*
*
* Implementations of this interface are usually immutable and
* always thread-safe: get
and set
methods of this interface may be freely used
* while simultaneous accessing the same instance from several threads.
*/
interface Updatable extends Func {
/**
* Correct some of x
arguments so that
* {@link #get get(x)} will be, as possible, equal to newResult
.
* For example, if this is one-argument function f(x),
* and its inverse function is g(y) (g(f(x))=x),
* then this method should assign x[0]=g(newResult)
.
*
* This method does not guarantee the precise equality
* {@link #get get(x)}==newResult
. (Usually, it is impossible due to
* limited precision of floating-point calculations.) But this method should try
* to provide this equality (after its call) with, as possible, maximal possible precision.
*
* @param x the function arguments.
* @param newResult the desired function result.
* @throws IndexOutOfBoundsException may be thrown if the length of x
array is less
* than the required number of this function arguments.
*/
void set(double[] x, double newResult);
}
/**
* Returns the result of this function for the given arguments:
* f(x0, x1, ..., xx.length
-1).
*
*
This method must not change the values of x
elements!
*
* @param x the function arguments.
* @return the function result.
* @throws IndexOutOfBoundsException may be thrown if the number of passed arguments is less
* than the required number of this function arguments.
*/
double get(double... x);
/**
* Equivalent to {@link #get(double...) get}(new double[0])
.
* Provides better performance because it does not require Java array creation.
*
* @return the function result.
* @throws IndexOutOfBoundsException may be thrown this function requires at least 1 argument.
*/
double get();
/**
* Equivalent to {@link #get(double...) get}(new double[] {x0})
.
* Provides better performance because it does not require Java array creation.
*
* @param x0 the function argument.
* @return the function result.
* @throws IndexOutOfBoundsException may be thrown this function requires at least 2 arguments.
*/
double get(double x0);
/**
* Equivalent to {@link #get(double...) get}(new double[] {x0, x1})
.
* Provides better performance because it does not require Java array creation.
*
* @param x0 the first function argument.
* @param x1 the second function argument.
* @return the function result.
* @throws IndexOutOfBoundsException may be thrown this function requires at least 3 arguments.
*/
double get(double x0, double x1);
/**
* Equivalent to {@link #get(double...) get}(new double[] {x0, x1, x2})
.
* Provides better performance because it does not require Java array creation.
*
* @param x0 the first function argument.
* @param x1 the second function argument.
* @param x2 the third function argument.
* @return the function result.
* @throws IndexOutOfBoundsException may be thrown this function requires at least 4 arguments.
*/
double get(double x0, double x1, double x2);
/**
* Equivalent to {@link #get(double...) get}(new double[] {x0, x1, x2, x3})
.
* Provides better performance because it does not require Java array creation.
*
* @param x0 the first function argument.
* @param x1 the second function argument.
* @param x2 the third function argument.
* @param x3 the fourth function argument.
* @return the function result.
* @throws IndexOutOfBoundsException may be thrown this function requires at least 5 arguments.
*/
double get(double x0, double x1, double x2, double x3);
/**
* Returns the function {@link Func1} that is based on the given predicate and returns
* 1.0 for all values for which this predicate returns true
,
* and 0.0 for all values for which this predicate returns false
.
* Equivalent to
*
* (Func1) x0 -> predicate.test(x0) ? 1.0 : 0.0;
*
*
* @param predicate some predicate for double values.
* @return equivalent AlgART function.
* @throws NullPointerException if the argument is {@code null}.
*/
static Func1 of(DoublePredicate predicate) {
Objects.requireNonNull(predicate, "Null predicate");
return x0 -> predicate.test(x0) ? 1.0 : 0.0;
}
}