Source code for absfuyu.dxt.intext

"""
Absfuyu: Data Extension
-----------------------
int extension

Version: 5.1.0
Date updated: 10/03/2025 (dd/mm/yyyy)
"""

# Module Package
# ---------------------------------------------------------------------------
__all__ = ["IntExt", "Pow"]


# Library
# ---------------------------------------------------------------------------
import math
from collections import Counter
from typing import Any, Self

from absfuyu.core import ShowAllMethodsMixin, versionadded, versionchanged
from absfuyu.dxt.dxt_support import DictBoolTrue


# Class
# ---------------------------------------------------------------------------
[docs] class Pow: """Number power by a number""" def __init__(self, number: int | float, power_by: int) -> None: self.number = number self.power_by = power_by def __str__(self) -> str: if self.power_by == 1: return str(self.number) else: return f"{self.number}^{self.power_by}" # return f"{self.__class__.__name__}({self.number}, {self.power_by})" def __repr__(self) -> str: return self.__str__()
[docs] def to_list(self) -> list[int]: """ Convert into list :rtype: list[int | float] """ return [self.number] * self.power_by # type: ignore
[docs] def calculate(self) -> float: """ Calculate the ``self.number`` to the power of ``self.power_by`` :rtype: float """ # return self.number**self.power_by return math.pow(self.number, self.power_by)
[docs] class IntExt(ShowAllMethodsMixin, int): """ ``int`` extension """ # convert stuff
[docs] def to_binary(self) -> str: """ Convert to binary number Returns ------- str Binary number Example: -------- >>> test = IntNumber(10) >>> test.to_binary() '1010' """ return format(self, "b")
[docs] def to_celcius_degree(self) -> float: """ Convert into Celcius degree as if ``self`` is Fahrenheit degree Returns ------- float Celcius degree Example: -------- >>> test = IntNumber(10) >>> test.to_celcius_degree() -12.222222222222221 """ c_degree = (self - 32) / 1.8 return c_degree
[docs] def to_fahrenheit_degree(self) -> float: """ Convert into Fahrenheit degree as if ``self`` is Celcius degree Returns ------- float Fahrenheit degree Example: -------- >>> test = IntNumber(10) >>> test.to_fahrenheit_degree() 50.0 """ f_degree = (self * 1.8) + 32 return f_degree
[docs] def reverse(self) -> Self: """ Reverse a number. Reverse ``abs(number)`` if ``number < 0`` Returns ------- IntNumber Reversed number Example: -------- >>> test = IntNumber(102) >>> test.reverse() 201 """ number = int(self) if number <= 1: number *= -1 return self.__class__(str(number)[::-1])
# is_stuff
[docs] def is_even(self) -> bool: """ An even number is a number which divisible by 2 Returns ------- bool | ``True`` if an even number | ``False`` if not an even number """ return self % 2 == 0
[docs] def is_prime(self) -> bool: """ Check if the integer is a prime number or not A prime number is a natural number greater than ``1`` that is not a product of two smaller natural numbers. A natural number greater than ``1`` that is not prime is called a composite number. Returns ------- bool | ``True`` if a prime number | ``False`` if not a prime number """ number = self if number <= 1: return False for i in range(2, int(math.sqrt(number)) + 1): # divisor range if number % i == 0: return False return True
[docs] def is_twisted_prime(self) -> bool: """ A number is said to be twisted prime if it is a prime number and reverse of the number is also a prime number Returns ------- bool | ``True`` if a twisted prime number | ``False`` if not a twisted prime number """ prime = self.is_prime() rev = self.reverse().is_prime() return prime and rev
[docs] def is_perfect(self) -> bool: """ Check if integer is perfect number Perfect number: a positive integer that is equal to the sum of its proper divisors. The smallest perfect number is ``6``, which is the sum of ``1``, ``2``, and ``3``. Other perfect numbers are ``28``, ``496``, and ``8,128``. Returns ------- bool | ``True`` if a perfect number | ``False`` if not a perfect number """ # --- """ # List of known perfect number # Source: https://en.wikipedia.org/wiki/List_of_Mersenne_primes_and_perfect_numbers perfect_number_index = [ 2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941, 11_213, 19_937, 21_701, 23_209, 44_497, 86_243, 110_503, 132_049, 216_091, 756_839, 859_433, # 1_257_787, 1_398_269, 2_976_221, 3_021_377, 6_972_593, # 13_466_917, 20_996_011, 24_036_583, 25_964_951, # 30_402_457, 32_582_657, 37_156_667, 42_643_801, # 43_112_609, 57_885_161, ## 74_207_281, 77_232_917, 82_589_933 ] perfect_number = [] for x in perfect_number_index: # a perfect number have a form of (2**(n-1))*((2**n)-1) perfect_number.append((2**(x-1))*((2**x)-1)) """ number = int(self) perfect_number = [ 6, 28, 496, 8128, 33_550_336, 8_589_869_056, 137_438_691_328, 2_305_843_008_139_952_128, ] if number in perfect_number: return True elif number < perfect_number[-1]: return False else: # Faster way to check perfect_number_index: list[int] = [ 61, 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941, 11_213, 19_937, 21_701, 23_209, 44_497, 86_243, 110_503, 132_049, 216_091, 756_839, 859_433, 1_257_787, # 1_398_269, # 2_976_221, # 3_021_377, # 6_972_593, # 13_466_917, # 20_996_011, # 24_036_583, # 25_964_951, # 30_402_457, # 32_582_657, # 37_156_667, # 42_643_801, # 43_112_609, # 57_885_161, ## 74_207_281, ## 77_232_917, ## 82_589_933 ] for x in perfect_number_index: # a perfect number have a form of (2**(n-1))*((2**n)-1) perfect_number = (2 ** (x - 1)) * ((2**x) - 1) if number < perfect_number: # type: ignore return False elif number == perfect_number: # type: ignore return True # Manual way when above method not working # sum s = 1 # add all divisors i = 2 while i * i <= number: if number % i == 0: s += +i + number / i # type: ignore i += 1 # s == number -> perfect return True if s == number and number != 1 else False
[docs] def is_narcissistic(self) -> bool: """ Check if a narcissistic number In number theory, a narcissistic number (also known as a pluperfect digital invariant (PPDI), an Armstrong number (after Michael F. Armstrong) or a plus perfect number) in a given number base ``b`` is a number that is the sum of its own digits each raised to the power of the number of digits. Returns ------- bool | ``True`` if a narcissistic number | ``False`` if not a narcissistic number """ try: check = sum([int(x) ** len(str(self)) for x in str(self)]) res = int(self) == check return res # type: ignore except Exception: return False
[docs] def is_palindromic(self) -> bool: """ A palindromic number (also known as a numeral palindrome or a numeric palindrome) is a number (such as ``16461``) that remains the same when its digits are reversed. Returns ------- bool | ``True`` if a palindromic number | ``False`` if not a palindromic number """ return self == self.reverse()
[docs] def is_palindromic_prime(self) -> bool: """ A palindormic prime is a number which is both palindromic and prime Returns ------- bool | ``True`` if a palindormic prime number | ``False`` if not a palindormic prime number """ return self.is_palindromic() and self.is_prime()
# calculation stuff
[docs] def lcm(self, with_number: int) -> Self: """ Least common multiple of ``self`` and ``with_number`` Parameters ---------- with_number : int The number that want to find LCM with Returns ------- IntNumber Least common multiple Example: -------- >>> test = IntNumber(102) >>> test.lcm(5) 510 """ return self.__class__(math.lcm(self, with_number))
[docs] @versionchanged("3.3.0", reason="Updated functionality") def gcd(self, with_number: int) -> Self: """ Greatest common divisor of ``self`` and ``with_number`` Parameters ---------- with_number : int The number that want to find GCD with Returns ------- IntNumber Greatest common divisor Example: -------- >>> test = IntNumber(1024) >>> test.gcd(8) 8 """ return self.__class__(math.gcd(self, with_number))
[docs] def add_to_one_digit(self, master_number: bool = False) -> Self: """ Convert ``self`` into 1-digit number by adding all of the digits together Parameters ---------- master_number : bool | Break when sum = ``22`` or ``11`` (numerology) | (Default: ``False``) Returns ------- IntNumber IntNumber Example: -------- >>> test = IntNumber(119) >>> test.add_to_one_digit() 2 >>> test = IntNumber(119) >>> test.add_to_one_digit(master_number=True) 11 """ number = int(self) if number < 0: number *= -1 while len(str(number)) != 1: number = sum(map(int, str(number))) if master_number: if number == 22 or number == 11: break # Master number return self.__class__(number)
[docs] @versionchanged("5.0.0", reason="Removed ``short_form`` parameter") def divisible_list(self) -> list[int]: """ A list of divisible number Returns ------- list[int] A list of divisible number Example: -------- >>> test = IntNumber(1024) >>> test.divisible_list() [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] """ if self <= 1: return [1] divi_list = [x for x in range(1, int(self / 2) + 1) if self % x == 0] + [self] return divi_list
[docs] def prime_factor(self, short_form: bool = True) -> list[int] | list[Pow]: """ Prime factor Parameters ---------- short_form : bool | Show prime list in short form | Normal example: ``[2, 2, 2, 3, 3]`` | Short form example: ``[2^3, 3^2]`` | (Default: ``True``) Returns ------- list[int] | list[Pow] | List of prime number that when multiplied together == ``self`` | list[int]: Long form | list[Pow]: Short form Example: -------- >>> test = IntNumber(1024) >>> test.prime_factor() [2^10] >>> test = IntNumber(1024) >>> test.prime_factor(short_form=False) [2, 2, 2, 2, 2, 2, 2, 2, 2, 2] """ # Generate list factors = [] divisor = 2 number = int(self) if number <= 1: return [number] while divisor <= number: if number % divisor == 0: factors.append(divisor) number //= divisor # number = number // divisor else: divisor += 1 # Output if short_form: temp = dict(Counter(factors)) return [Pow(k, v) for k, v in temp.items()] return factors
# analyze
[docs] def analyze(self, short_form: bool = True) -> dict[str, dict[str, Any]]: """ Analyze the number with almost all ``IntNumber`` method Parameters ---------- short_form : bool | Enable short form for some items | (Default: ``True``) Returns ------- dict[str, dict[str, Any]] Detailed analysis Example: -------- >>> test = IntNumber(1024) >>> test.analyze() { 'summary': {'number': 1024, 'length': 4, 'even': True, 'prime factor': [2^10], 'divisible': [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]}, 'convert': {'binary': '10000000000', 'octa': '2000', 'hex': '400', 'reverse': 4201, 'add to one': 7}, 'characteristic': {'prime': False, 'twisted prime': False, 'perfect': False, 'narcissistic': False, 'palindromic': False, 'palindromic prime': False} } """ output = { "summary": { "number": self, "length": len(str(self)), "even": self.is_even(), "prime factor": self.prime_factor(short_form=short_form), "divisible": self.divisible_list(), }, "convert": { "binary": bin(self)[2:], "octa": oct(self)[2:], "hex": hex(self)[2:], # "hash": hash(self), "reverse": self.reverse(), "add to one": self.add_to_one_digit(), }, } characteristic = { "prime": self.is_prime(), "twisted prime": self.is_twisted_prime(), "perfect": self.is_perfect(), "narcissistic": self.is_narcissistic(), "palindromic": self.is_palindromic(), "palindromic prime": self.is_palindromic_prime(), } if short_form: characteristic = DictBoolTrue(characteristic) output["characteristic"] = characteristic return output # type: ignore
[docs] @versionadded("5.1.0") def split(self, parts: int) -> list[int]: """ Evenly split the number into ``parts`` parts Parameters ---------- parts : int Split by how many parts Returns ------- list[int] List of evenly splitted numbers Example: -------- >>> IntExt(10).split(4) [2, 2, 3, 3] """ p = max(1, parts) if p == 1: return [int(self)] quotient, remainder = divmod(self, p) return [quotient + (i >= (p - remainder)) for i in range(p)]