Problem 699: Triffle Numbers

View on Project Euler

Project Euler Problem 699 Solution

EulerSolve provides an optimized solution for Project Euler Problem 699, Triffle Numbers, with C++, Python, Java, and a step-by-step mathematical explanation.

Problem Summary Let \(\sigma(n)\) be the sum of the positive divisors of \(n\). A positive integer \(n\) is called triffle when the reduced fraction \(\sigma(n)/n\) has denominator \(3^k\) for some \(k\ge 1\). In other words, after all common factors of \(\sigma(n)\) and \(n\) are canceled, the denominator is a positive power of \(3\). If \(\mathcal{T}\) denotes the set of triffle numbers, the goal is to compute $$T(N)=\sum_{\substack{n\le N \\ n\in \mathcal{T}}} n.$$ The C++, Python, and Java implementations do not test every \(n\le N\). They separate the exact power of \(3\) dividing \(n\), convert the triffle condition into divisibility statements involving \(\sigma(m)\), and then search only the multiplicative branches that can still satisfy those statements. Mathematical Approach Every triffle number is divisible by \(3\), so write it uniquely as $$n=3^a m,\qquad a\ge 1,\qquad 3\nmid m.$$ This isolates the only prime that is allowed to remain in the reduced denominator. Step 1: Separate the Power of Three Because \(\sigma\) is multiplicative on coprime factors, $$\sigma(n)=\sigma(3^a)\sigma(m).$$ The divisor sum of the pure \(3\)-part is $$\sigma(3^a)=1+3+\cdots+3^a=\frac{3^{a+1}-1}{2}.$$ Define $$c_a=\frac{3^{a+1}-1}{2}.$$ Then $$\frac{\sigma(n)}{n}=\frac{c_a\sigma(m)}{3^a m}.$$ Since \(3^{a+1}-1\equiv -1 \pmod 3\), the factor \(c_a\) is never divisible by \(3\)....

Detailed mathematical approach

Problem Summary

Let \(\sigma(n)\) be the sum of the positive divisors of \(n\). A positive integer \(n\) is called triffle when the reduced fraction \(\sigma(n)/n\) has denominator \(3^k\) for some \(k\ge 1\). In other words, after all common factors of \(\sigma(n)\) and \(n\) are canceled, the denominator is a positive power of \(3\).

If \(\mathcal{T}\) denotes the set of triffle numbers, the goal is to compute

$$T(N)=\sum_{\substack{n\le N \\ n\in \mathcal{T}}} n.$$

The C++, Python, and Java implementations do not test every \(n\le N\). They separate the exact power of \(3\) dividing \(n\), convert the triffle condition into divisibility statements involving \(\sigma(m)\), and then search only the multiplicative branches that can still satisfy those statements.

Mathematical Approach

Every triffle number is divisible by \(3\), so write it uniquely as

$$n=3^a m,\qquad a\ge 1,\qquad 3\nmid m.$$

This isolates the only prime that is allowed to remain in the reduced denominator.

Step 1: Separate the Power of Three

Because \(\sigma\) is multiplicative on coprime factors,

$$\sigma(n)=\sigma(3^a)\sigma(m).$$

The divisor sum of the pure \(3\)-part is

$$\sigma(3^a)=1+3+\cdots+3^a=\frac{3^{a+1}-1}{2}.$$

Define

$$c_a=\frac{3^{a+1}-1}{2}.$$

Then

$$\frac{\sigma(n)}{n}=\frac{c_a\sigma(m)}{3^a m}.$$

Since \(3^{a+1}-1\equiv -1 \pmod 3\), the factor \(c_a\) is never divisible by \(3\).

Step 2: Cancel Every Denominator Prime Other Than \(3\)

All primes outside \(3\) that could remain in the denominator must come from \(m\), because \(m\) is coprime to \(3\). Therefore the reduced denominator is a power of \(3\) exactly when every prime factor of \(m\) cancels against the numerator \(c_a\sigma(m)\).

This gives the key divisibility condition

$$m \mid c_a\sigma(m).$$

Once this is true, all non-\(3\) denominator factors are gone.

Step 3: Leave a Positive Power of \(3\) Behind

After the non-\(3\) part vanishes, the remaining denominator can only come from \(3^a\). Because \(3\nmid c_a\) and \(3\nmid m\), the only cancellation with \(3^a\) comes from \(\sigma(m)\). Hence, when \(m \mid c_a\sigma(m)\), the reduced denominator is

$$3^{a-v_3(\sigma(m))}.$$

To be triffle, this denominator must still be greater than \(1\), so

$$a-v_3(\sigma(m))\ge 1,$$

which is equivalent to

$$v_3(\sigma(m))\le a-1.$$

Thus \(n=3^a m\) is triffle if and only if

$$m \mid c_a\sigma(m),\qquad v_3(\sigma(m))\le a-1.$$

Step 4: Build \(m\) Prime Power by Prime Power

Write

$$m=\prod_{j=1}^r p_j^{e_j},\qquad p_j\neq 3.$$

Then

$$\sigma(m)=\prod_{j=1}^r \sigma(p_j^{e_j}),\qquad \sigma(p^e)=\frac{p^{e+1}-1}{p-1}.$$

So

$$\frac{c_a\sigma(m)}{m}=c_a\prod_{j=1}^r \frac{\sigma(p_j^{e_j})}{p_j^{e_j}}.$$

This identity is ideal for depth-first search. Start from \(m=1\), add a new prime power \(p^e\), update the reduced fraction above, and increase \(v_3(\sigma(m))\) by \(v_3(\sigma(p^e))\). Any branch that already exceeds the layer bound or violates the \(3\)-adic limit can be pruned immediately.

Step 5: Sum Independent \(3\)-Layers

For each \(a\ge 1\) with \(3^a\le N\), define

$$\mathcal{M}_a(N)=\left\{m\le \frac{N}{3^a}: 3\nmid m,\ m\mid c_a\sigma(m),\ v_3(\sigma(m))\le a-1\right\}.$$

Then

$$\boxed{T(N)=\sum_{\substack{a\ge 1\\3^a\le N}} 3^a\sum_{m\in\mathcal{M}_a(N)} m.}$$

Each value of \(a\) defines an independent search layer, so the layer sums can be computed separately and combined at the end.

Worked Example: Why \(84\) Is Triffle

Take \(n=84=3^1\cdot 28\). Here \(a=1\), \(m=28\), and

$$c_1=\frac{3^2-1}{2}=4,\qquad \sigma(28)=56.$$

Now

$$\frac{c_1\sigma(m)}{m}=\frac{4\cdot 56}{28}=8,$$

so every denominator prime other than \(3\) disappears. Also

$$v_3(\sigma(28))=v_3(56)=0\le 1-1.$$

Therefore the reduced denominator is \(3^{1-0}=3\), and indeed

$$\frac{\sigma(84)}{84}=\frac{4\cdot 56}{84}=\frac{8}{3}.$$

So \(84\) is triffle. The small checkpoint

$$T(100)=3+9+12+27+54+81+84=270$$

matches the value verified by the implementations.

How the Code Works

The C++, Python, and Java implementations iterate over all \(a\) with \(3^a\le N\). For each layer they compute the bound \(\lfloor N/3^a\rfloor\) and the geometric-series factor \(c_a\), then start a depth-first search from \(m=1\).

The search state stores the current \(m\), the reduced fraction for \(c_a\sigma(m)/m\), the current value of \(v_3(\sigma(m))\), and the current divisor sum \(\sigma(m)\). When a new prime power \(p^e\) with \(p\neq 3\) is appended, the implementations multiply by \(\sigma(p^e)/p^e\), cancel common factors immediately, and recurse only if the new \(m\) stays within the layer bound and the \(3\)-adic valuation still satisfies \(v_3(\sigma(m))\le a-1\).

To keep branching small, the next prime candidates are taken from the prime factors of the current reduced numerator, with \(2\) always included as a cheap fallback. A state is accepted when the reduced fraction has denominator \(1\), because that means every non-\(3\) denominator factor has already been removed. The accepted \(m\)-values are summed, then multiplied by \(3^a\).

For 64-bit factorization, the implementations use deterministic Miller-Rabin primality testing and Pollard-Rho splitting. Different \(a\)-layers are independent, so they can also be processed in parallel. Small checkpoints such as \(T(100)=270\) and \(T(10^6)=26089287\) are used as correctness guards.

Complexity Analysis

There is no simple closed-form bound because the runtime depends on how strongly the divisibility tests and valuation bounds prune the search tree. A practical summary is

$$\text{time} \approx \text{visited DFS states} \times \text{average 64-bit factorization cost}.$$

Memory is dominated by the recursion path, the set of already visited \(m\)-values inside each layer, and the factorization cache. In practice the method works because most branches die early and the different \(3\)-layers are independent.

Footnotes and References

  1. Project Euler problem page: https://projecteuler.net/problem=699
  2. Divisor function: Wikipedia - Divisor function
  3. \(p\)-adic valuation: Wikipedia - p-adic valuation
  4. Pollard-Rho algorithm: Wikipedia - Pollard-Rho algorithm
  5. Miller-Rabin primality test: Wikipedia - Miller-Rabin primality test

Problem 699 source code

C++

#include <algorithm>
#include <chrono>
#include <cstdint>
#include <functional>
#include <future>
#include <iostream>
#include <string>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

using std::uint64_t;
using u128 = __uint128_t;

namespace {

constexpr uint64_t kDefaultN = 100000000000000ULL;

uint64_t gcd_u64(uint64_t a, uint64_t b) {
    while (b) {
        uint64_t t = a % b;
        a = b;
        b = t;
    }
    return a;
}

uint64_t mul_mod_u64(uint64_t a, uint64_t b, uint64_t mod) {
    return static_cast<uint64_t>((static_cast<u128>(a) * b) % mod);
}

uint64_t pow_mod_u64(uint64_t a, uint64_t d, uint64_t mod) {
    uint64_t r = 1;
    while (d) {
        if (d & 1) r = mul_mod_u64(r, a, mod);
        a = mul_mod_u64(a, a, mod);
        d >>= 1;
    }
    return r;
}

bool is_prime_u64(uint64_t n) {
    if (n < 2) return false;
    static uint64_t small_primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
    for (uint64_t p : small_primes) {
        if (n == p) return true;
        if (n % p == 0) return false;
    }

    uint64_t d = n - 1, s = 0;
    while ((d & 1) == 0) {
        d >>= 1;
        ++s;
    }

    auto witness = [&](uint64_t a) -> bool {
        if (a % n == 0) return false;
        uint64_t x = pow_mod_u64(a, d, n);
        if (x == 1 || x == n - 1) return false;
        for (uint64_t i = 1; i < s; ++i) {
            x = mul_mod_u64(x, x, n);
            if (x == n - 1) return false;
        }
        return true;
    };

    static uint64_t bases[] = {2ULL, 325ULL, 9375ULL, 28178ULL, 450775ULL, 9780504ULL, 1795265022ULL};
    for (uint64_t a : bases) {
        if (witness(a)) return false;
    }
    return true;
}

uint64_t splitmix64(uint64_t& x) {
    uint64_t z = (x += 0x9e3779b97f4a7c15ULL);
    z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
    z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
    return z ^ (z >> 31);
}

thread_local uint64_t rng_state =
    static_cast<uint64_t>(std::chrono::high_resolution_clock::now().time_since_epoch().count()) ^
    0x9e3779b97f4a7c15ULL;

uint64_t rand_u64(uint64_t lo, uint64_t hi) {
    uint64_t r = splitmix64(rng_state);
    return lo + (hi > lo ? (r % (hi - lo + 1)) : 0);
}

uint64_t pollard_rho(uint64_t n) {
    if ((n & 1ULL) == 0) return 2;
    if (n % 3ULL == 0) return 3;

    while (true) {
        uint64_t c = rand_u64(1, n - 1);
        uint64_t x = rand_u64(0, n - 1);
        uint64_t y = x;
        uint64_t d = 1;

        auto f = [&](uint64_t v) { return (mul_mod_u64(v, v, n) + c) % n; };

        while (d == 1) {
            x = f(x);
            y = f(f(y));
            uint64_t diff = (x > y) ? (x - y) : (y - x);
            d = gcd_u64(diff, n);
        }
        if (d != n) return d;
    }
}

void factor_u64(uint64_t n, std::vector<uint64_t>& fac) {
    if (n == 1) return;
    if (is_prime_u64(n)) {
        fac.push_back(n);
        return;
    }
    uint64_t d = pollard_rho(n);
    factor_u64(d, fac);
    factor_u64(n / d, fac);
}

struct FactorCache {
    std::unordered_map<uint64_t, std::vector<uint64_t>> mp;

    std::vector<uint64_t> get(uint64_t n) {
        auto it = mp.find(n);
        if (it != mp.end()) return it->second;

        std::vector<uint64_t> f;
        if (n > 1) {
            std::vector<uint64_t> tmp;
            factor_u64(n, tmp);
            std::sort(tmp.begin(), tmp.end());
            tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
            f = std::move(tmp);
        }
        mp.emplace(n, f);
        return f;
    }
};

int v3_u64(uint64_t x) {
    int c = 0;
    while (x % 3ULL == 0) {
        x /= 3ULL;
        ++c;
    }
    return c;
}

uint64_t sigma_prime_power(uint64_t p, int e, uint64_t p_pow) {
    u128 next_pow = static_cast<u128>(p_pow) * p;
    u128 sig = (next_pow - 1) / (p - 1);
    return static_cast<uint64_t>(sig);
}

std::string to_string_u128(u128 x) {
    if (x == 0) return "0";
    std::string s;
    while (x > 0) {
        int digit = static_cast<int>(x % 10);
        s.push_back(static_cast<char>('0' + digit));
        x /= 10;
    }
    std::reverse(s.begin(), s.end());
    return s;
}

struct SolverA {
    uint64_t A;
    uint64_t limit;
    int maxv3;
    bool validate;

    uint64_t sum_m = 0;

    std::unordered_set<uint64_t> visited_m;
    FactorCache cache;
    std::vector<uint64_t> used;

    SolverA(uint64_t A_, uint64_t limit_, int maxv3_, bool validate_)
        : A(A_), limit(limit_), maxv3(maxv3_), validate(validate_) {
        visited_m.reserve(1 << 16);
        cache.mp.reserve(1 << 16);
        used.reserve(32);
    }

    bool is_used(uint64_t p) const {
        for (uint64_t x : used) {
            if (x == p) return true;
        }
        return false;
    }

    void dfs(uint64_t m, uint64_t num, uint64_t den, int v3sig, uint64_t sigma_m) {
        if (m > limit) return;
        if (!visited_m.insert(m).second) return;

        if (den == 1 && v3sig <= maxv3) {
            sum_m += m;

            if (validate) {
                uint64_t g = gcd_u64(m, sigma_m);
                uint64_t r = m / g;
                if (A % r != 0) {
                    std::cerr << "Validation failed: A%r!=0, A=" << A << " m=" << m << "\n";
                    std::abort();
                }
                if (v3_u64(sigma_m) != v3sig) {
                    std::cerr << "Validation failed: v3 mismatch, m=" << m << "\n";
                    std::abort();
                }
                u128 prod = static_cast<u128>(A) * sigma_m;
                if (static_cast<uint64_t>(prod % m) != 0ULL) {
                    std::cerr << "Validation failed: (A*sigma_m)%m!=0, m=" << m << "\n";
                    std::abort();
                }
            }
        }

        std::vector<uint64_t> cand = cache.get(num);
        cand.push_back(2);
        std::sort(cand.begin(), cand.end());
        cand.erase(std::unique(cand.begin(), cand.end()), cand.end());

        for (uint64_t p : cand) {
            if (p == 3) continue;
            if (is_used(p)) continue;

            uint64_t p_pow = p;
            for (int e = 1;; ++e) {
                if (static_cast<u128>(m) * p_pow > limit) break;

                uint64_t sig = sigma_prime_power(p, e, p_pow);
                int v3f = v3_u64(sig);
                if (v3sig + v3f <= maxv3) {
                    uint64_t denom_factor = p_pow;
                    uint64_t sig_factor = sig;

                    uint64_t num1 = num;
                    uint64_t den1 = den;

                    uint64_t g1 = gcd_u64(num1, denom_factor);
                    num1 /= g1;
                    denom_factor /= g1;

                    uint64_t g2 = gcd_u64(sig_factor, den1);
                    sig_factor /= g2;
                    den1 /= g2;

                    u128 new_num = static_cast<u128>(num1) * sig_factor;
                    u128 new_den = static_cast<u128>(den1) * denom_factor;

                    uint64_t new_m = static_cast<uint64_t>(static_cast<u128>(m) * p_pow);
                    uint64_t new_sigma_m = static_cast<uint64_t>(static_cast<u128>(sigma_m) * sig);

                    used.push_back(p);
                    dfs(new_m, static_cast<uint64_t>(new_num), static_cast<uint64_t>(new_den),
                        v3sig + v3f, new_sigma_m);
                    used.pop_back();
                }

                if (p_pow > limit / p) break;
                p_pow *= p;
            }
        }
    }

    void run() {
        dfs(1, A, 1, 0, 1);
    }
};

u128 computeT(uint64_t N, bool validate, bool multithread) {
    std::vector<std::pair<int, uint64_t>> tasks;
    u128 pow3 = 1;
    for (int a = 1;; ++a) {
        pow3 *= 3;
        if (pow3 > N) break;
        tasks.push_back({a, static_cast<uint64_t>(pow3)});
    }

    auto solve_one = [&](int a, uint64_t p3) -> u128 {
        uint64_t limit = N / p3;
        u128 t = static_cast<u128>(p3) * 3 - 1;
        uint64_t A = static_cast<uint64_t>(t / 2);
        SolverA solver(A, limit, a - 1, validate);
        solver.run();
        return static_cast<u128>(p3) * solver.sum_m;
    };

    u128 total = 0;
    if (!multithread || tasks.size() <= 1) {
        for (const auto& task : tasks) total += solve_one(task.first, task.second);
        return total;
    }

    std::vector<std::future<u128>> fut;
    fut.reserve(tasks.size());
    for (const auto& task : tasks) {
        fut.push_back(std::async(std::launch::async, solve_one, task.first, task.second));
    }
    for (auto& f : fut) total += f.get();
    return total;
}

}  // namespace

int main(int argc, char** argv) {
    uint64_t N = kDefaultN;
    bool validate = true;
    bool multithread = true;

    for (int i = 1; i < argc; ++i) {
        std::string s = argv[i];
        if (s.rfind("--N=", 0) == 0) {
            N = std::stoull(s.substr(4));
        } else if (s.rfind("--validate=", 0) == 0) {
            validate = (std::stoi(s.substr(11)) != 0);
        } else if (s.rfind("--mt=", 0) == 0) {
            multithread = (std::stoi(s.substr(5)) != 0);
        }
    }

    if (validate) {
        u128 t100 = computeT(100ULL, false, false);
        if (static_cast<uint64_t>(t100) != 270ULL) {
            std::cerr << "Checkpoint failed: T(100) expected 270, got "
                      << static_cast<uint64_t>(t100) << "\n";
            return 1;
        }
        u128 t1e6 = computeT(1000000ULL, false, false);
        if (static_cast<uint64_t>(t1e6) != 26089287ULL) {
            std::cerr << "Checkpoint failed: T(1e6) expected 26089287, got "
                      << static_cast<uint64_t>(t1e6) << "\n";
            return 1;
        }
    }

    u128 ans = computeT(N, validate, multithread);
    std::cout << to_string_u128(ans) << "\n";
    return 0;
}

Python

import sys
import multiprocessing
import multiprocessing.pool
import math

def gcd(a, b):
    while b:
        a, b = b, a % b
    return a

def is_prime(n):
    if n < 2: return False
    small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
    for p in small_primes:
        if n == p: return True
        if n % p == 0: return False
        
    d = n - 1
    s = 0
    while (d & 1) == 0:
        d >>= 1
        s += 1
        
    def witness(a):
        if a % n == 0: return False
        x = pow(a, d, n)
        if x == 1 or x == n - 1: return False
        for _ in range(1, s):
            x = (x * x) % n
            if x == n - 1: return False
        return True
        
    bases = [2, 325, 9375, 28178, 450775, 9780504, 1795265022]
    for a in bases:
        if witness(a): return False
    return True

import random

def pollard_rho(n):
    if n & 1 == 0: return 2
    if n % 3 == 0: return 3
    
    while True:
        c = random.randint(1, n - 1)
        x = random.randint(0, n - 1)
        y = x
        d = 1
        
        def f(v):
            return (v * v + c) % n
            
        while d == 1:
            x = f(x)
            y = f(f(y))
            d = gcd(abs(x - y), n)
            
        if d != n: return d

def factor(n, fac):
    if n == 1: return
    if is_prime(n):
        fac.append(n)
        return
    d = pollard_rho(n)
    factor(d, fac)
    factor(n // d, fac)

class FactorCache:
    def __init__(self):
        self.mp = {}
        
    def get(self, n):
        if n in self.mp: return self.mp[n]
        f = []
        if n > 1:
            tmp = []
            factor(n, tmp)
            f = sorted(list(set(tmp)))
        self.mp[n] = f
        return f

def v3(x):
    c = 0
    while x % 3 == 0:
        x //= 3
        c += 1
    return c

def sigma_prime_power(p, e, p_pow):
    next_pow = p_pow * p
    return (next_pow - 1) // (p - 1)

class SolverA:
    def __init__(self, A, limit, maxv3, validate=False):
        self.A = A
        self.limit = limit
        self.maxv3 = maxv3
        self.validate = validate
        self.sum_m = 0
        self.visited_m = set()
        self.cache = FactorCache()
        self.used = []
        
    def dfs(self, m, num, den, v3sig, sigma_m):
        if m > self.limit: return
        if m in self.visited_m: return
        self.visited_m.add(m)
        
        if den == 1 and v3sig <= self.maxv3:
            self.sum_m += m
            
        cand = self.cache.get(num)[:]
        cand.append(2)
        cand = sorted(list(set(cand)))
        
        for p in cand:
            if p == 3: continue
            if p in self.used: continue
            
            p_pow = p
            e = 1
            while True:
                if m * p_pow > self.limit: break
                
                sig = sigma_prime_power(p, e, p_pow)
                v3f = v3(sig)
                if v3sig + v3f <= self.maxv3:
                    denom_factor = p_pow
                    sig_factor = sig
                    
                    num1 = num
                    den1 = den
                    
                    g1 = gcd(num1, denom_factor)
                    num1 //= g1
                    denom_factor //= g1
                    
                    g2 = gcd(sig_factor, den1)
                    sig_factor //= g2
                    den1 //= g2
                    
                    new_num = num1 * sig_factor
                    new_den = den1 * denom_factor
                    
                    new_m = m * p_pow
                    new_sigma_m = sigma_m * sig
                    
                    self.used.append(p)
                    self.dfs(new_m, new_num, new_den, v3sig + v3f, new_sigma_m)
                    self.used.pop()
                    
                if p_pow > self.limit // p: break
                p_pow *= p
                e += 1

def solve_one(args):
    a, p3, N = args
    limit = N // p3
    t = p3 * 3 - 1
    A = t // 2
    solver = SolverA(A, limit, a - 1, False)
    solver.dfs(1, A, 1, 0, 1)
    return p3 * solver.sum_m

def compute_T(N):
    tasks = []
    pow3 = 1
    a = 1
    while True:
        pow3 *= 3
        if pow3 > N: break
        tasks.append((a, pow3, N))
        a += 1
        
    threads = multiprocessing.cpu_count() or 1
    total = 0
    if threads <= 1 or len(tasks) <= 1:
        for t in tasks:
            total += solve_one(t)
    else:
        with multiprocessing.Pool(threads) as pool:
            results = pool.map(solve_one, tasks)
        total = sum(results)
    return total

def solve():
    N = 100000000000000
    ans = compute_T(N)
    return str(ans)

if __name__ == '__main__':
    print(solve())

Java

import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.*;

public class Euler699 {
    static long gcd(long a, long b) {
        while (b != 0) {
            long t = a % b;
            a = b;
            b = t;
        }
        return a;
    }

    static long mulMod(long a, long b, long mod) {
        return BigInteger.valueOf(a).multiply(BigInteger.valueOf(b)).mod(BigInteger.valueOf(mod)).longValue();
    }

    static long powMod(long a, long d, long mod) {
        long r = 1;
        while (d > 0) {
            if ((d & 1) != 0)
                r = mulMod(r, a, mod);
            a = mulMod(a, a, mod);
            d >>= 1;
        }
        return r;
    }

    static boolean isPrime(long n) {
        if (n < 2)
            return false;
        long[] smallPrimes = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37 };
        for (long p : smallPrimes) {
            if (n == p)
                return true;
            if (n % p == 0)
                return false;
        }

        long d = n - 1;
        int s = 0;
        while ((d & 1) == 0) {
            d >>= 1;
            s++;
        }

        long[] bases = { 2L, 325L, 9375L, 28178L, 450775L, 9780504L, 1795265022L };
        for (long a : bases) {
            if (a % n == 0)
                continue;
            long x = powMod(a, d, n);
            if (x == 1 || x == n - 1)
                continue;
            boolean composite = true;
            for (int i = 1; i < s; i++) {
                x = mulMod(x, x, n);
                if (x == n - 1) {
                    composite = false;
                    break;
                }
            }
            if (composite)
                return false;
        }
        return true;
    }

    static long pollardRho(long n) {
        if ((n & 1) == 0)
            return 2;
        if (n % 3 == 0)
            return 3;

        ThreadLocalRandom rand = ThreadLocalRandom.current();
        while (true) {
            long c = rand.nextLong(1, n);
            long x = rand.nextLong(0, n);
            long y = x;
            long d = 1;

            while (d == 1) {
                x = (mulMod(x, x, n) + c) % n;
                long ty = (mulMod(y, y, n) + c) % n;
                y = (mulMod(ty, ty, n) + c) % n;

                long diff = Math.abs(x - y);
                d = gcd(diff, n);
            }
            if (d != n)
                return d;
        }
    }

    static void factor(long n, List<Long> fac) {
        if (n == 1)
            return;
        if (isPrime(n)) {
            fac.add(n);
            return;
        }
        long d = pollardRho(n);
        factor(d, fac);
        factor(n / d, fac);
    }

    static class FactorCache {
        Map<Long, List<Long>> mp = new HashMap<>();

        List<Long> get(long n) {
            if (mp.containsKey(n))
                return mp.get(n);
            List<Long> f = new ArrayList<>();
            if (n > 1) {
                List<Long> tmp = new ArrayList<>();
                factor(n, tmp);
                Collections.sort(tmp);
                for (long p : tmp) {
                    if (f.isEmpty() || f.get(f.size() - 1) != p) {
                        f.add(p);
                    }
                }
            }
            mp.put(n, f);
            return f;
        }
    }

    static int v3(long x) {
        int c = 0;
        while (x % 3 == 0) {
            x /= 3;
            c++;
        }
        return c;
    }

    static long sigmaPrimePower(long p, int e, long pPow) {
        BigInteger nextPow = BigInteger.valueOf(pPow).multiply(BigInteger.valueOf(p));
        return nextPow.subtract(BigInteger.ONE).divide(BigInteger.valueOf(p - 1)).longValue();
    }

    static class SolverA {
        long A;
        long limit;
        int maxv3;
        long sumM = 0;
        HashSet<Long> visitedM = new HashSet<>();
        FactorCache cache = new FactorCache();
        List<Long> used = new ArrayList<>();

        SolverA(long a, long limit, int maxv3) {
            this.A = a;
            this.limit = limit;
            this.maxv3 = maxv3;
        }

        void dfs(long m, long num, long den, int v3sig, long sigmaM) {
            if (m > limit)
                return;
            if (!visitedM.add(m))
                return;

            if (den == 1 && v3sig <= maxv3) {
                sumM += m;
            }

            List<Long> cand = new ArrayList<>(cache.get(num));
            cand.add(2L);
            Collections.sort(cand);
            List<Long> uniqCand = new ArrayList<>();
            for (long p : cand) {
                if (uniqCand.isEmpty() || uniqCand.get(uniqCand.size() - 1) != p) {
                    uniqCand.add(p);
                }
            }

            for (long p : uniqCand) {
                if (p == 3)
                    continue;
                if (used.contains(p))
                    continue;

                long pPow = p;
                for (int e = 1;; e++) {
                    if (BigInteger.valueOf(m).multiply(BigInteger.valueOf(pPow))
                            .compareTo(BigInteger.valueOf(limit)) > 0)
                        break;

                    long sig = sigmaPrimePower(p, e, pPow);
                    int v3f = v3(sig);

                    if (v3sig + v3f <= maxv3) {
                        long denomFactor = pPow;
                        long sigFactor = sig;

                        long num1 = num;
                        long den1 = den;

                        long g1 = gcd(num1, denomFactor);
                        num1 /= g1;
                        denomFactor /= g1;

                        long g2 = gcd(sigFactor, den1);
                        sigFactor /= g2;
                        den1 /= g2;

                        long newNum = num1 * sigFactor;
                        long newDen = den1 * denomFactor;

                        long newM = m * pPow;
                        long newSigmaM = sigmaM * sig;

                        used.add(p);
                        dfs(newM, newNum, newDen, v3sig + v3f, newSigmaM);
                        used.remove(used.size() - 1);
                    }

                    if (pPow > limit / p)
                        break;
                    pPow *= p;
                }
            }
        }
    }

    public static String solve() {
        long N = 100000000000000L;
        List<long[]> tasks = new ArrayList<>();
        long pow3 = 1;
        int a = 1;
        while (true) {
            pow3 *= 3;
            if (pow3 > N)
                break;
            tasks.add(new long[] { a, pow3 });
            a++;
        }

        int threads = Runtime.getRuntime().availableProcessors();
        if (threads < 1)
            threads = 1;
        ExecutorService pool = Executors.newFixedThreadPool(threads);
        List<Future<Long>> futures = new ArrayList<>();

        for (long[] task : tasks) {
            futures.add(pool.submit(() -> {
                int t_a = (int) task[0];
                long t_p3 = task[1];
                long limit = N / t_p3;
                long t_val = t_p3 * 3 - 1;
                long A = t_val / 2;
                SolverA solver = new SolverA(A, limit, t_a - 1);
                solver.dfs(1L, A, 1L, 0, 1L);
                return t_p3 * solver.sumM;
            }));
        }

        long total = 0;
        for (Future<Long> f : futures) {
            try {
                total += f.get();
            } catch (Exception e) {
            }
        }
        pool.shutdown();
        return Long.toString(total);
    }

    public static void main(String[] args) {
        System.out.println(solve());
    }
}