@@ -273,23 +273,83 @@ public extension Math {
273273 var isLucky : Bool {
274274 let n = Int ( self . description) ?? 0
275275 guard n > 0 else { return false }
276+ if n == 1 { return true }
277+ if n % 2 == 0 { return false }
276278
277- var numbers = Array ( 1 ... ( n * 10 ) )
278- var i = 1
279+ // Start with position in odd numbers sequence
280+ var pos = ( n + 1 ) / 2
279281
280- while i < numbers. count {
281- let step = numbers [ i]
282- if step > numbers. count { break }
282+ // Apply sieve steps
283+ var step = 3
284+ var removed = 1
285+
286+ while step <= pos {
287+ // Check if current position would be removed at this step
288+ if pos % step == 0 {
289+ return false
290+ }
283291
284- numbers = numbers. enumerated ( ) . filter { ( index, _) in
285- ( index + 1 ) % step != 0
286- } . map { $0. element }
292+ // Adjust position for already removed numbers
293+ let removedAtThisStep = ( pos - removed) / step
294+ removed += removedAtThisStep
295+ pos -= removedAtThisStep
287296
288- i += 1
297+ // Move to next step (next lucky number position)
298+ // Calculate next lucky position after sieving
299+ var nextStep = step + 1
300+ var tempPos = ( nextStep * 2 - 1 + 1 ) / 2
301+
302+ // Find next position that survives all previous sieves
303+ while true {
304+ var survives = true
305+ var checkStep = 3
306+ var checkRemoved = 1
307+
308+ while checkStep < nextStep && survives {
309+ if tempPos % checkStep == 0 {
310+ survives = false
311+ break
312+ }
313+ let removedAtCheck = ( tempPos - checkRemoved) / checkStep
314+ checkRemoved += removedAtCheck
315+ tempPos -= removedAtCheck
316+
317+ checkStep += 2
318+ while checkStep < nextStep {
319+ let testPos = ( checkStep * 2 - 1 + 1 ) / 2
320+ var testSurvives = true
321+ var innerCheckStep = 3
322+ var innerRemoved = 1
323+
324+ while innerCheckStep < checkStep && testSurvives {
325+ if testPos % innerCheckStep == 0 {
326+ testSurvives = false
327+ break
328+ }
329+ let innerRemovedAtCheck = ( testPos - innerRemoved) / innerCheckStep
330+ innerRemoved += innerRemovedAtCheck
331+ innerCheckStep = innerCheckStep == 3 ? 7 : innerCheckStep + 2
332+ }
333+
334+ if testSurvives {
335+ break
336+ }
337+ checkStep += 2
338+ }
339+ }
340+
341+ if survives {
342+ step = nextStep * 2 - 1
343+ break
344+ }
345+ nextStep += 1
346+ tempPos = ( nextStep * 2 - 1 + 1 ) / 2
347+ }
289348 }
290349
291- return numbers . contains ( n )
350+ return true
292351 }
352+
293353
294354 /// Returns `true` if this number is a palindromic number.
295355 ///
@@ -543,4 +603,123 @@ public extension Math {
543603 }
544604 return sum == product
545605 }
606+
607+ /// Returns `true` if this number is a perfect power: n = a^b with integers a > 1, b > 1.
608+ var isPerfectPower : Bool {
609+ guard let n = self . asInt, n > 1 else { return false }
610+ let maxBase = Int ( Double ( n) . squareRoot ( ) )
611+ if maxBase < 2 { return false }
612+ for a in 2 ... maxBase {
613+ var value = a * a
614+ while value <= n {
615+ if value == n { return true }
616+ if value > n / a { break }
617+ value *= a
618+ }
619+ }
620+ return false
621+ }
622+
623+ /// Returns `true` if this number is a prime power with exponent ≥ 2: n = p^k, k ≥ 2.
624+ var isPrimePower : Bool {
625+ guard let n = self . asInt, n > 1 else { return false }
626+ // Factorize n and ensure all prime factors are the same and exponent ≥ 2
627+ var num = n
628+ var p = 2
629+ var primeFactor : Int ? = nil
630+ var exponent = 0
631+ while p * p <= num {
632+ while num % p == 0 {
633+ if let pf = primeFactor, pf != p { return false }
634+ primeFactor = p
635+ exponent += 1
636+ num /= p
637+ }
638+ p += ( p == 2 ? 1 : 2 )
639+ }
640+ if num > 1 {
641+ if let pf = primeFactor, pf != num { return false }
642+ primeFactor = num
643+ exponent += 1
644+ }
645+ return primeFactor != nil && exponent >= 2
646+ }
647+
648+ /// Returns `true` if this number is a factorion (Krishnamurthy number):
649+ /// the sum of the factorials of its digits equals the number.
650+ /// Examples: 1, 2, 145, 40585
651+ var isFactorion : Bool {
652+ guard let n = self . asInt, n >= 0 else { return false }
653+ var m = n
654+ var sum = 0
655+ while m > 0 {
656+ let d = m % 10
657+ sum += Math . factorialDigitLookup [ d]
658+ m /= 10
659+ }
660+ if n == 0 { return Math . factorialDigitLookup [ 0 ] == 0 } // 0! = 1, so 0 is not a factorion
661+ return sum == n
662+ }
663+
664+ /// Alias for `isNarcissistic`.
665+ var isArmstrong : Bool { self . isNarcissistic }
666+
667+ /// Returns `true` if this number is a palindrome in binary representation.
668+ var isBinaryPalindromic : Bool {
669+ guard let n = self . asInt, n >= 0 else { return false }
670+ let s = String ( n, radix: 2 )
671+ return s == String ( s. reversed ( ) )
672+ }
673+
674+ /// Returns `true` if this number is a palindrome in hexadecimal representation.
675+ var isHexPalindromic : Bool {
676+ guard let n = self . asInt, n >= 0 else { return false }
677+ let s = String ( n, radix: 16 )
678+ return s == String ( s. reversed ( ) )
679+ }
680+
681+ /// Returns `true` if this number is tetrahedral: n = k(k+1)(k+2)/6 for some k ≥ 1.
682+ var isTetrahedral : Bool {
683+ guard let n = self . asInt, n > 0 else { return false }
684+ var k = 1
685+ while true {
686+ let value = k * ( k + 1 ) * ( k + 2 ) / 6
687+ if value == n { return true }
688+ if value > n { return false }
689+ k += 1
690+ }
691+ }
692+
693+ /// Returns `true` if this number is a centered square number: n = 1 + 4*T(k).
694+ var isCenteredSquare : Bool {
695+ guard let n = self . asInt, n > 0 else { return false }
696+ let diff = n - 1
697+ guard diff % 4 == 0 else { return false }
698+ let t = Math ( integerLiteral: diff / 4 )
699+ return t. isTriangular
700+ }
701+
702+ /// Returns `true` if this number is a power of three.
703+ var isPowerOfThree : Bool {
704+ guard let n0 = self . asInt, n0 > 0 else { return false }
705+ var n = n0
706+ while n % 3 == 0 { n /= 3 }
707+ return n == 1
708+ }
709+
710+ /// Returns `true` if this number is a power of five.
711+ var isPowerOfFive : Bool {
712+ guard let n0 = self . asInt, n0 > 0 else { return false }
713+ var n = n0
714+ while n % 5 == 0 { n /= 5 }
715+ return n == 1
716+ }
717+
718+ // MARK: - Local helpers
719+ /// Precomputed factorials for digits 0...9
720+ private static let factorialDigitLookup : [ Int ] = {
721+ var f = [ Int] ( repeating: 1 , count: 10 )
722+ for i in 2 ..< 10 { f [ i] = f [ i - 1 ] * i }
723+ return f
724+ } ( )
546725}
0 commit comments