Skip to content

Commit c30838f

Browse files
committed
I guess I had uncommited code
1 parent de5ea21 commit c30838f

2 files changed

Lines changed: 222 additions & 17 deletions

File tree

Sources/Math/Properties/BasicProperties.swift

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public extension Math {
2020
var getParity: Parity {
2121
return self % 2 == 0 ? .even : .odd
2222
}
23-
23+
2424
/// Returns the sign of this value.
2525
///
2626
/// - Returns: `.positive` if greater than zero, `.negative` if less than zero, or `.zero` if equal to zero.
@@ -33,32 +33,58 @@ public extension Math {
3333
return .positive
3434
}
3535
}
36-
36+
3737
/// Returns the absolute value of this number.
3838
var absoluteValue: Math {
3939
return self < 0 ? Math(0) - self : self
4040
}
41-
41+
42+
/// Returns the value truncated down to the nearest integer if it has a decimal part.
43+
var asCutOffDecimal: Math {
44+
return Math(integerLiteral: Int(Double(self)))
45+
}
46+
47+
/// Returns the value floored to a given multiple.
48+
func floor(by multiple: Int) -> Math {
49+
let scaled = self / Math(integerLiteral: multiple)
50+
let floored = scaled.asCutOffDecimal
51+
return floored * Math(integerLiteral: multiple)
52+
}
53+
54+
/// Returns the value ceiled to a given multiple.
55+
func ceiling(by multiple: Int) -> Math {
56+
let scaled = self / Math(integerLiteral: multiple)
57+
let ceiled = Math(floatLiteral: ceil(Double(scaled)))
58+
return ceiled * Math(integerLiteral: multiple)
59+
}
60+
61+
/// Returns the value rounded to a given number of decimal places.
62+
func rounded(toDecimalPlaces places: Int) -> Math {
63+
guard places >= 0 else { return self }
64+
let multiplier = pow(10.0, Double(places))
65+
return Math(floatLiteral: (Double(self) * multiplier).rounded() / multiplier)
66+
}
67+
4268
/// Returns `true` if this number is zero.
4369
var isZero: Bool {
4470
return self == 0
4571
}
46-
72+
4773
/// Returns `true` if this number is positive (greater than zero).
4874
var isPositive: Bool {
4975
return self > 0
5076
}
51-
77+
5278
/// Returns `true` if this number is negative (less than zero).
5379
var isNegative: Bool {
5480
return self < 0
5581
}
56-
82+
5783
/// Returns `true` if this number is even.
5884
var isEven: Bool {
5985
return self % 2 == 0
6086
}
61-
87+
6288
/// Returns `true` if this number is odd.
6389
var isOdd: Bool {
6490
return self % 2 != 0

Sources/Math/Properties/SpecialNumbers.swift

Lines changed: 189 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)