Continuing the code for combinatorics library, adding the definition of ⁿPᵣ. This is an extract from a larger codebase.
- Is there any way to limit the code of
gcd()
to this file? Should I convert the namespaceCombinatorics
to a class? - Any other advice you would want to give me?
Code
// MathLibrary.cpp : Defines the functions for the static library.
//
#include "pch.h"
#include "framework.h"
#include <vector>
#include <iostream>
#include <limits>
#include <cstddef>
namespace Combinatorics
{
const std::vector<unsigned long long> representableFactors = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,
479001600,6227020800,87178291200,1307674368000,20922789888000,355687428096000,6402373705728000,121645100408832000,2432902008176640000};
unsigned long long factorial(unsigned int n)
{
if (n >= representableFactors.size())
{
throw std::invalid_argument("Combinatorics:factorial - The argument is too large");
}
return representableFactors[n];
}
inline long long gcd(long long a, long long b)
{
if (a % b == 0)
{
return b;
}
return gcd(b, (a % b));
}
long long combinations(int n, int r)
{
if (n < 0 || r < 0)
{
throw std::invalid_argument("Combinatorics::combinations - N and R cannot be less than 0");
}
if (n < r)
{
throw std::invalid_argument("Combinatorics::combinations - The value of r cannot be greater than n");
}
if (n - r < r)
{
r = n - r;
}
long long int denominatorProduct = 1;
long long int numeratorProduct = 1;
for (long long int denomCount = r, numCount = n ; denomCount >= 1; denomCount--, numCount--)
{
//TODO : Convert to limits
if((LLONG_MAX / n) < numeratorProduct || (LLONG_MAX / n) < denominatorProduct)
{
throw std::invalid_argument("Combinatorics::combinations - Overflow detected aborting");
}
denominatorProduct *= denomCount;
numeratorProduct *= numCount;
long long gcdCommonFactor = gcd(denominatorProduct, numeratorProduct);
denominatorProduct /= gcdCommonFactor;
numeratorProduct /= gcdCommonFactor;
}
return (numeratorProduct / denominatorProduct);
}
//Code to be reviewed.
long long permutations(int n, int r)
{
if (n < 0 || r < 0)
{
throw std::invalid_argument("Combinatorics::permutations - N and R cannot be less than 0");
}
if (n < r)
{
throw std::invalid_argument("Combinatorics::permutations - The value of r cannot be greater than n");
}
long long permutations = 1;
for (long long int numeratorCount = n; numeratorCount >= (n - r + 1); numeratorCount--)
{
//TODO: Convert to limits
if ((LLONG_MAX / n) < permutations)
{
throw std::invalid_argument("Combinatorics::permutations - Overflow detected aborting");
}
permutations *= numeratorCount;
}
return permutations;
}
}
Test code
#include "pch.h"
#include <iostream>
#include "../MathLibrary/MathLibrary.cpp"
TEST(Combinatorial_Factorial, small_ints)
{
EXPECT_EQ(Combinatorics::factorial(0), 1);
EXPECT_EQ(Combinatorics::factorial(1), 1);
EXPECT_EQ(Combinatorics::factorial(5), 120);
EXPECT_EQ(Combinatorics::factorial(20), 2432902008176640000);
}
TEST(Combinatorial_Factorial, too_big)
{
EXPECT_THROW(Combinatorics::factorial(500), std::invalid_argument);
}
TEST(Combinatorial_Combinations, small_ints)
{
EXPECT_EQ(Combinatorics::combinations(5,5), 1);
EXPECT_EQ(Combinatorics::combinations(5, 0), 1);
EXPECT_EQ(Combinatorics::combinations(5, 1), 5);
EXPECT_EQ(Combinatorics::combinations(20,10),184756);
EXPECT_EQ(Combinatorics::combinations(40, 35),658008);
}
TEST(Combinatorial_Combinations, negative_n)
{
EXPECT_THROW(Combinatorics::combinations(-5, 5), std::invalid_argument);
}
TEST(Combinatorial_Combinations, r_greater_than_n)
{
EXPECT_THROW(Combinatorics::combinations(4, 5), std::invalid_argument);
}
TEST(Combinatorial_Combinations, overflow)
{
EXPECT_THROW(Combinatorics::combinations(100, 50), std::invalid_argument);
}
TEST(Combinatorial_Permutations, small_ints)
{
EXPECT_EQ(Combinatorics::permutations(5, 5), 120);
EXPECT_EQ(Combinatorics::permutations(5, 0), 1);
EXPECT_EQ(Combinatorics::permutations(5, 2), 20);
EXPECT_EQ(Combinatorics::permutations(10, 2), 90);
EXPECT_EQ(Combinatorics::permutations(40, 3), 59280);
EXPECT_EQ(Combinatorics::permutations(40, 7), 93963542400);
EXPECT_EQ(Combinatorics::permutations(50, 4), 5527200);
}
TEST(Combinatorial_Permutations, r_negative)
{
EXPECT_THROW(Combinatorics::permutations(5, -5), std::invalid_argument);
}
TEST(Combinatorial_Permutations, n_negative)
{
EXPECT_THROW(Combinatorics::permutations(-5, 5), std::invalid_argument);
}
TEST(Combinatorial_Permutations,r_greater)
{
EXPECT_THROW(Combinatorics::permutations(5, 6), std::invalid_argument);
}
TEST(Combinatorial_Permutations,overflow)
{
EXPECT_THROW(Combinatorics::permutations(50,46), std::invalid_argument);
}
static
storage class. \$\endgroup\$