380 lines
8.8 KiB
Python
380 lines
8.8 KiB
Python
import random
|
|
from .autonode import node_wrapper, get_node_names_mappings, validate, anytype
|
|
classes = []
|
|
node = node_wrapper(classes)
|
|
import math
|
|
|
|
@node
|
|
class MinNode:
|
|
"""
|
|
Returns the minimum of two values
|
|
"""
|
|
RETURN_TYPES = (anytype,)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": (anytype,),
|
|
"input2": (anytype,),
|
|
}
|
|
}
|
|
FUNCTION = "min"
|
|
CATEGORY = "Math"
|
|
custom_name = "Min"
|
|
def min(self, input1, input2):
|
|
return (min(input1, input2),)
|
|
|
|
@node
|
|
class MaxNode:
|
|
"""
|
|
Returns the maximum of two values
|
|
"""
|
|
RETURN_TYPES = (anytype,)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": (anytype,),
|
|
"input2": (anytype,),
|
|
}
|
|
}
|
|
FUNCTION = "max"
|
|
CATEGORY = "Math"
|
|
custom_name = "Max"
|
|
def max(self, input1, input2):
|
|
return (max(input1, input2),)
|
|
|
|
@node
|
|
class RoundNode:
|
|
"""
|
|
Rounds a value to the nearest integer
|
|
"""
|
|
RETURN_TYPES = ("INT",)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": (anytype,),
|
|
}
|
|
}
|
|
FUNCTION = "round"
|
|
CATEGORY = "Math"
|
|
custom_name = "Round"
|
|
def round(self, input1):
|
|
return (round(input1),)
|
|
|
|
@node
|
|
class AbsNode:
|
|
"""
|
|
Returns the absolute value of a number
|
|
"""
|
|
RETURN_TYPES = (anytype,)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": (anytype,),
|
|
}
|
|
}
|
|
FUNCTION = "abs"
|
|
CATEGORY = "Math"
|
|
custom_name = "Abs"
|
|
def abs(self, input1):
|
|
return (abs(input1),)
|
|
|
|
@node
|
|
class FloorNode:
|
|
"""
|
|
Returns the floor of a number
|
|
"""
|
|
RETURN_TYPES = ("INT",)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": (anytype,),
|
|
}
|
|
}
|
|
FUNCTION = "floor"
|
|
CATEGORY = "Math"
|
|
custom_name = "Floor"
|
|
def floor(self, input1):
|
|
return (math.floor(input1),)
|
|
|
|
@node
|
|
class CeilNode:
|
|
"""
|
|
Returns the ceiling of a number
|
|
"""
|
|
RETURN_TYPES = ("INT",)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": (anytype,),
|
|
}
|
|
}
|
|
FUNCTION = "ceil"
|
|
CATEGORY = "Math"
|
|
custom_name = "Ceil"
|
|
def ceil(self, input1):
|
|
return (math.ceil(input1),)
|
|
|
|
@node
|
|
class PowerNode:
|
|
"""
|
|
Returns the power of a number
|
|
"""
|
|
RETURN_TYPES = (anytype,)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": (anytype,),
|
|
"power": (anytype,),
|
|
}
|
|
}
|
|
FUNCTION = "power"
|
|
CATEGORY = "Math"
|
|
custom_name = "Power"
|
|
def power(self, input1, power):
|
|
abs_input = abs(input1)
|
|
# fast paths for values that won't overflow digit-wise
|
|
if abs_input == 0:
|
|
if power < 0:
|
|
raise ZeroDivisionError("0 cannot be raised to a negative power")
|
|
return (math.pow(input1, power),)
|
|
if abs_input == 1:
|
|
return (math.pow(input1, power),)
|
|
|
|
# validate power with log10 scale, prevent huge magnitudes
|
|
log10_abs = math.log10(abs_input)
|
|
if (log10_abs * power) > 100:
|
|
raise OverflowError("Power is too large, exceeds 100 digits")
|
|
return (math.pow(input1, power),)
|
|
|
|
@node
|
|
class SigmoidNode:
|
|
"""
|
|
Returns the sigmoid of a number
|
|
"""
|
|
RETURN_TYPES = ("FLOAT",)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": ("FLOAT",),
|
|
}
|
|
}
|
|
FUNCTION = "sigmoid"
|
|
CATEGORY = "Math"
|
|
custom_name = "Sigmoid"
|
|
def sigmoid(self, input1):
|
|
return (1 / (1 + math.exp(-input1)),)
|
|
def is_prime_small(n: int) -> bool:
|
|
"""
|
|
Deterministic check for primality for smaller n.
|
|
Skips multiples of 2 and 3, then checks i, i+2, i+4 up to sqrt(n).
|
|
"""
|
|
if n < 2:
|
|
return False
|
|
if n in (2, 3):
|
|
return True
|
|
if n % 2 == 0 or n % 3 == 0:
|
|
return n == 2 or n == 3
|
|
|
|
# 6k ± 1 optimization
|
|
limit = int(math.isqrt(n)) # integer sqrt
|
|
i = 5
|
|
while i <= limit:
|
|
if n % i == 0 or n % (i + 2) == 0:
|
|
return False
|
|
i += 6
|
|
return True
|
|
|
|
def miller_rabin_test(d: int, n: int) -> bool:
|
|
""" One round of the Miller-Rabin test with a random base 'a'. """
|
|
a = random.randrange(2, n - 1)
|
|
x = pow(a, d, n) # a^d % n
|
|
if x == 1 or x == n - 1:
|
|
return True
|
|
|
|
# Keep squaring x while d does not reach n-1
|
|
while d != n - 1:
|
|
x = (x * x) % n
|
|
d <<= 1 # d *= 2
|
|
if x == 1:
|
|
return False
|
|
if x == n - 1:
|
|
return True
|
|
return False
|
|
|
|
def is_prime_miller_rabin(n: int, k: int = 5) -> bool:
|
|
"""
|
|
Miller-Rabin primality test with k rounds (probabilistic).
|
|
Good enough for big integers in practice.
|
|
"""
|
|
# Handle small or trivial cases
|
|
if n < 2:
|
|
return False
|
|
# check small primes quickly
|
|
for small_prime in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]:
|
|
if n == small_prime:
|
|
return True
|
|
if n % small_prime == 0 and n != small_prime:
|
|
return False
|
|
|
|
# Write n - 1 as d * 2^r
|
|
d = n - 1
|
|
while d % 2 == 0:
|
|
d //= 2
|
|
|
|
# Witness loop
|
|
for _ in range(k):
|
|
if not miller_rabin_test(d, n):
|
|
return False
|
|
return True
|
|
|
|
@node
|
|
class IsPrimeNode:
|
|
"""
|
|
Checks if an integer is prime.
|
|
|
|
- If the integer |value| < threshold, uses a deterministic small-check (trial division).
|
|
- Otherwise, uses a Miller-Rabin pseudoprime test for a faster check (probabilistic).
|
|
|
|
Returns a BOOLEAN (True if prime, False if composite).
|
|
"""
|
|
FUNCTION = "is_prime"
|
|
RETURN_TYPES = ("BOOLEAN",)
|
|
CATEGORY = "Math"
|
|
custom_name = "Is Prime?"
|
|
|
|
@staticmethod
|
|
def is_prime(value: int, threshold: int = 10_000_000, miller_rabin_rounds: int = 5):
|
|
# handle negative or zero
|
|
if value < 2:
|
|
return (False,)
|
|
|
|
if value < threshold:
|
|
# use small prime check
|
|
return (is_prime_small(value),)
|
|
else:
|
|
# use Miller-Rabin
|
|
return (is_prime_miller_rabin(value, k=miller_rabin_rounds),)
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"value": ("INT", {"default": 1, "min": -9999999999, "max": 9999999999, "step": 1}),
|
|
},
|
|
"optional": {
|
|
"threshold": ("INT", {"default": 10_000_000, "min": 1, "max": 9999999999, "step": 1}),
|
|
"miller_rabin_rounds": ("INT", {"default": 5, "min": 1, "max": 50, "step": 1}),
|
|
}
|
|
}
|
|
@node
|
|
class RAMPNode:
|
|
"""
|
|
Returns the ramp of a number
|
|
"""
|
|
RETURN_TYPES = ("FLOAT",)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": ("FLOAT",),
|
|
}
|
|
}
|
|
FUNCTION = "ramp"
|
|
CATEGORY = "Math"
|
|
custom_name = "RAMP"
|
|
def ramp(self, input1):
|
|
return (max(0, input1),)
|
|
|
|
@node
|
|
class ModuloNode:
|
|
"""
|
|
Returns the modulo of a number
|
|
"""
|
|
RETURN_TYPES = ("INT",)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": ("INT",),
|
|
"modulo": ("INT",),
|
|
}
|
|
}
|
|
FUNCTION = "modulo"
|
|
CATEGORY = "Math"
|
|
custom_name = "Modulo"
|
|
def modulo(self, input1, modulo):
|
|
return (input1 % modulo,)
|
|
|
|
@node
|
|
class LogNode:
|
|
"""
|
|
Returns the log of a number
|
|
"""
|
|
RETURN_TYPES = ("FLOAT",)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": ("FLOAT",),
|
|
"base": ("FLOAT",),
|
|
}
|
|
}
|
|
FUNCTION = "log"
|
|
CATEGORY = "Math"
|
|
custom_name = "Log"
|
|
def log(self, input1, base):
|
|
return (math.log(input1, base),)
|
|
|
|
@node
|
|
class MultiplyNode:
|
|
"""
|
|
Returns the product of two numbers
|
|
"""
|
|
RETURN_TYPES = (anytype,)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": (anytype,),
|
|
"input2": (anytype,),
|
|
}
|
|
}
|
|
FUNCTION = "multiply"
|
|
CATEGORY = "Math"
|
|
custom_name = "Multiply"
|
|
def multiply(self, input1, input2):
|
|
return (input1 * input2,)
|
|
|
|
@node
|
|
class DivideNode:
|
|
"""
|
|
Returns the quotient of two numbers
|
|
"""
|
|
RETURN_TYPES = ("FLOAT",)
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"input1": (anytype,),
|
|
"input2": (anytype,),
|
|
}
|
|
}
|
|
FUNCTION = "divide"
|
|
CATEGORY = "Math"
|
|
custom_name = "Divide"
|
|
def divide(self, input1, input2):
|
|
if input2 == 0:
|
|
raise ZeroDivisionError("Cannot divide by zero")
|
|
return (input1 / input2,)
|
|
|
|
validate(classes)
|
|
CLASS_MAPPINGS, CLASS_NAMES = get_node_names_mappings(classes)
|