Recently in a job interview I was asked to give the result of 100th element of a 3rd-class Fibonacci sequence(Fib(n)=Fib(n-1)+Fib(n-2)+Fib(n-3). I finished by Mathematical Induction and constructing a class to present numbers larger than long long. Then I was asked to implement it via template meta-programming. The problem is that the result will exceed the range of long long and I don't know how to fix this. Here is my code using template meta-programming.
template<long long num>
struct fib
{
    enum { result = fib<num - 1>::result + fib<num - 2>::result + fib<num - 3>::result};
};
template<>
struct fib<0>
{
    enum { result = 1 };
};
template<>
struct fib<1>
{
    enum { result = 1 };
};
template<>
struct fib<2>
{
    enum { result = 2 };
};
template<>
struct fib<3>
{
    enum { result = 4 };
};
int main()
{
    cout << fib<100>::result << endl;
    return 0;
}
                A possible implementation is to use a custom structure to store the numbers instead of a built-in type. You could for instance store numbers like this:
template <int... Digits>
struct number<Digits... > { };
Note: For the sake of simplicity when adding, I store the digits in reverse order, so the number 275 is stored as number<5, 7, 2>.
Fibonacci only requires addition, so you simply have to define addition, e.g., a template add (see the end of the answer for the actual implementation).
You can then define the fib template quite easily:
template <int N>
struct fib_impl {
    using type = add_t<
        typename fib_impl<N-1>::type, 
        typename fib_impl<N-2>::type,
        typename fib_impl<N-3>::type>;
};
template <>
struct fib_impl<0> { using type = number<0>; };
template <>
struct fib_impl<1> { using type = number<0>; };
template <>
struct fib_impl<2> { using type = number<1>; };
template <int N>
using fib = typename fib_impl<N>::type;
And with an appropriate output operator (see below), you can print the 100th Tribonacci number:
int main() {
    std::cout << fib<100>{} << "\n";
}
Which outputs:
53324762928098149064722658
While the 100th is not present in the OEIS, you can check that the 37th one is correct:
static_assert(std::is_same_v<fib<37>, number<2, 5, 8, 6, 3, 4, 2, 3, 1, 1>>);
operator<<:std::ostream& operator<<(std::ostream &out, number<>) {
    return out;
}
template <int Digit, int... Digits>
std::ostream& operator<<(std::ostream &out, number<Digit, Digits... >) {
    // Do not forget that number<> is in reverse order:
    return out << number<Digits... >{} << Digit;
}
add template:cat utility to concatenate numbers:// Small concatenation utility:
template <class N1, class N2>
struct cat;
template <int... N1, int... N2>
struct cat<number<N1... >, number<N2... >> {
    using type = number<N1... , N2...>;
};
template <class N1, class N2>
using cat_t = typename cat<N1, N2>::type;
template <class AccNumber, int Carry, class Number1, class Number2>
struct add_impl;
template <class AccNumber, int Carry>
struct add_impl<AccNumber, Carry, number<>, number<>> {
    using type = std::conditional_t<Carry == 0, AccNumber, cat_t<AccNumber, number<1>>>;
};
template <class AccNumber, int Carry,
          int Digit2, int... Digits2>
struct add_impl<AccNumber, Carry, number<>, number<Digit2, Digits2...>> {
    using type = typename add_impl<
        cat_t<AccNumber, number<(Digit2 + Carry) % 10>>,
        (Digit2 + Carry) / 10,
        number<Digits2... >, number<>>::type;
};
template <class AccNumber, int Carry,
          int Digit1, int... Digits1>
struct add_impl<AccNumber, Carry, number<Digit1, Digits1... >, number<>> {
    using type = typename add_impl<
        cat_t<AccNumber, number<(Digit1 + Carry) % 10>>,
        (Digit1 + Carry) / 10,
        number<Digits1... >, number<>>::type;
};
template <class AccNumber, int Carry,
          int Digit1, int... Digits1, int Digit2, int... Digits2>
struct add_impl<AccNumber, Carry, number<Digit1, Digits1... >, number<Digit2, Digits2...>> {
    using type = typename add_impl<
                    cat_t<AccNumber, number<(Digit1 + Digit2 + Carry) % 10>>,
                    (Digit1 + Digit2 + Carry) / 10,
                    number<Digits1... >, number<Digits2... >>::type;
};
template <class... Numbers>
struct add;
template <class Number>
struct add<Number> {
    using type = Number;
};
template <class Number, class... Numbers>
struct add<Number, Numbers... > {
    using type = typename add_impl<
        number<>, 0, Number, typename add<Numbers... >::type>::type;
};
template <class... Numbers>
using add_t = typename add<Numbers... >::type;
                        I am not aware of ready-to-use arbirtrary precicion facitlities for templates. However, a toy number-type that can hold numbers bigger than long long is easy to write:
template <long long H,long long L> 
struct my_number {
    static const long long high = H;
    static const long long low = L;
    static const long long mod = 10000000000;
    static void print() {
        std::cout << high << setw(10) << setfill('0') << low;
    }
};
It stores the last 10 digits of the result in low and the leading digits in high. Two my_numbers can be summed via
template <typename A,typename B>
struct sum {
    static const long long low = (A::low + B::low) % A::mod;
    static const long long high = A::high + B::high + (A::low + B::low) / A::mod;
    using number = my_number<high,low>;
};
and for 3 numbers:
template <typename A,typename B,typename C>
struct sum3 { using number = typename sum<A,sum<B,C>>::number; };
As already mentioned, this is just a toy example. Anyhow, once you have a number type that can represent big enough numbers you just have to adjust you fib with minor modifications:
template<long long num> struct fib {
    using result_t = typename sum3< typename fib<num-1>::result_t, 
                                    typename fib<num-2>::result_t,
                                    typename fib<num-3>::result_t
                                   >::number;
};
template<> struct fib<0> { using result_t = my_number<0,1>; };
template<> struct fib<1> { using result_t = my_number<0,1>; };
template<> struct fib<2> { using result_t = my_number<0,2>; };
template<> struct fib<3> { using result_t = my_number<0,4>; };
int main() {
    fib<100>::result_t::print();
}
I couldn't find a reliable source for the correct value of fib<100>, so unfortunately I couldn't test against that.
Full example is here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With