Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 9feee5e06482
Choose a base ref
...
head repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fcc4f37b3667
Choose a head ref
  • 9 commits
  • 10 files changed
  • 1 contributor

Commits on Feb 17, 2018

  1. Dimensions.

    pleroy committed Feb 17, 2018
    Copy the full SHA
    1f3e501 View commit details
  2. Dimension generators.

    pleroy committed Feb 17, 2018
    Copy the full SHA
    d811d02 View commit details
  3. Split out the generators.

    pleroy committed Feb 17, 2018
    Copy the full SHA
    b49b974 View commit details
  4. Cleanup.

    pleroy committed Feb 17, 2018
    Copy the full SHA
    209c1be View commit details
  5. More cleanups.

    pleroy committed Feb 17, 2018
    Copy the full SHA
    9132ab6 View commit details
  6. Copy the full SHA
    781a547 View commit details
  7. Copy the full SHA
    301ff4b View commit details
  8. Copy the full SHA
    d41f0ce View commit details
  9. Merge pull request #1724 from pleroy/Quantities

    Split quantities into three parts to make the code more manageable
    pleroy authored Feb 17, 2018
    Copy the full SHA
    fcc4f37 View commit details
50 changes: 50 additions & 0 deletions quantities/dimensions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

#pragma once

#include <cstdint>

namespace principia {
namespace quantities {
namespace internal_dimensions {

// Dimensionality of physical quantities. Note that we strongly type angles.
template<std::int64_t LengthExponent,
std::int64_t MassExponent,
std::int64_t TimeExponent,
std::int64_t CurrentExponent,
std::int64_t TemperatureExponent,
std::int64_t AmountExponent,
std::int64_t LuminousIntensityExponent,
std::int64_t AngleExponent>
struct Dimensions;

// A double by any other name...
using NoDimensions = Dimensions<0, 0, 0, 0, 0, 0, 0, 0>;

// Instantiating this struct asserts at compile time that the template parameter
// can be serialized.
template<typename Dimensions>
struct DimensionsAreSerializable;

// These structs have a |Type| member that is a |Dimensions| suitable for
// the result of the operation applied to argument(s) having the |Dimensions|
// given as template parameter(s).

template<typename Dimensions, int n>
struct DimensionsExponentiationGenerator;

// Only legal if |n| divides the dimensions.
template<typename Dimensions, int n>
struct DimensionsNthRootGenerator;

template<typename LDimensions, typename RDimensions>
struct DimensionsProductGenerator;

template<typename LDimensions, typename RDimensions>
struct DimensionsQuotientGenerator;

} // namespace internal_dimensions
} // namespace quantities
} // namespace principia

#include "quantities/dimensions_body.hpp"
165 changes: 165 additions & 0 deletions quantities/dimensions_body.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@

#pragma once

#include "quantities/dimensions.hpp"

#include "base/not_constructible.hpp"

namespace principia {
namespace quantities {
namespace internal_dimensions {

using base::not_constructible;

class ExponentSerializer : not_constructible {
public:
// Returns true if the exponent is in the range that we can serialize.
static constexpr bool IsSerializable(std::int64_t exponent);

// Returns the serialized representation of the exponent. |position| is the
// 0-based position of the dimension in the representation.
static constexpr std::int64_t Representation(
std::int64_t exponent,
std::int64_t position);

private:
static constexpr std::int64_t min_exponent = -24;
static constexpr std::int64_t max_exponent = 7;
static constexpr std::int64_t exponent_mask = 0x1F;
static constexpr std::int64_t exponent_bits = 5;
};

constexpr bool ExponentSerializer::IsSerializable(
std::int64_t const exponent) {
return exponent >= min_exponent && exponent <= max_exponent;
}

constexpr std::int64_t ExponentSerializer::Representation(
std::int64_t const exponent,
std::int64_t const position) {
// For exponents in [-16, 7] this returns the representations
// 0x10, 0x11, ... 0x00, ... 0x07. For exponents in [-24, -17] this returns
// the representations 0x08, 0x09, ... 0x0F. The latter used to be reserved
// for exponents in the range [8, 15] but we believe that we never used them,
// and with polynomials in the monomial basis we need large negative
// exponents.
return (exponent & exponent_mask) << (position * exponent_bits);
}

template<std::int64_t LengthExponent,
std::int64_t MassExponent,
std::int64_t TimeExponent,
std::int64_t CurrentExponent,
std::int64_t TemperatureExponent,
std::int64_t AmountExponent,
std::int64_t LuminousIntensityExponent,
std::int64_t AngleExponent>
struct Dimensions : not_constructible {
enum {
Length = LengthExponent,
Mass = MassExponent,
Time = TimeExponent,
Current = CurrentExponent,
Temperature = TemperatureExponent,
Amount = AmountExponent,
LuminousIntensity = LuminousIntensityExponent,
Angle = AngleExponent,
};

static std::int64_t constexpr representation =
ExponentSerializer::Representation(LengthExponent, 0) |
ExponentSerializer::Representation(MassExponent, 1) |
ExponentSerializer::Representation(TimeExponent, 2) |
ExponentSerializer::Representation(CurrentExponent, 3) |
ExponentSerializer::Representation(TemperatureExponent, 4) |
ExponentSerializer::Representation(AmountExponent, 5) |
ExponentSerializer::Representation(LuminousIntensityExponent, 6) |
ExponentSerializer::Representation(AngleExponent, 7);
};

template<typename Dimensions>
struct DimensionsAreSerializable {
static_assert(ExponentSerializer::IsSerializable(Dimensions::Length),
"Invalid length exponent");
static_assert(ExponentSerializer::IsSerializable(Dimensions::Mass),
"Invalid mass exponent");
static_assert(ExponentSerializer::IsSerializable(Dimensions::Time),
"Invalid time exponent");
static_assert(ExponentSerializer::IsSerializable(Dimensions::Current),
"Invalid current exponent");
static_assert(ExponentSerializer::IsSerializable(Dimensions::Temperature),
"Invalid temperature exponent");
static_assert(ExponentSerializer::IsSerializable(Dimensions::Amount),
"Invalid amount exponent");
static_assert(ExponentSerializer::IsSerializable(
Dimensions::LuminousIntensity),
"Invalid luminous intensity exponent");
static_assert(ExponentSerializer::IsSerializable(Dimensions::Angle),
"Invalid angle exponent");
};

template<typename Dimensions, int n>
struct DimensionsExponentiationGenerator : not_constructible {
using Type =
internal_dimensions::Dimensions<Dimensions::Length * n,
Dimensions::Mass * n,
Dimensions::Time * n,
Dimensions::Current * n,
Dimensions::Temperature * n,
Dimensions::Amount * n,
Dimensions::LuminousIntensity * n,
Dimensions::Angle * n>;
};

template<typename Dimensions, int n>
struct DimensionsNthRootGenerator : not_constructible {
static_assert((Dimensions::Length % n) == 0 &&
(Dimensions::Mass % n) == 0 &&
(Dimensions::Time % n) == 0 &&
(Dimensions::Current % n) == 0 &&
(Dimensions::Temperature % n) == 0 &&
(Dimensions::Amount % n) == 0 &&
(Dimensions::LuminousIntensity % n) == 0 &&
(Dimensions::Angle % n) == 0,
"Dimensions not suitable for Nth root");

using Type =
internal_dimensions::Dimensions<Dimensions::Length / n,
Dimensions::Mass / n,
Dimensions::Time / n,
Dimensions::Current / n,
Dimensions::Temperature / n,
Dimensions::Amount / n,
Dimensions::LuminousIntensity / n,
Dimensions::Angle / n>;
};

template<typename LDimensions, typename RDimensions>
struct DimensionsProductGenerator : not_constructible {
using Type = Dimensions<LDimensions::Length + RDimensions::Length,
LDimensions::Mass + RDimensions::Mass,
LDimensions::Time + RDimensions::Time,
LDimensions::Current + RDimensions::Current,
LDimensions::Temperature + RDimensions::Temperature,
LDimensions::Amount + RDimensions::Amount,
LDimensions::LuminousIntensity +
RDimensions::LuminousIntensity,
LDimensions::Angle + RDimensions::Angle>;
};

template<typename LDimensions, typename RDimensions>
struct DimensionsQuotientGenerator : not_constructible {
using Type = Dimensions<LDimensions::Length - RDimensions::Length,
LDimensions::Mass - RDimensions::Mass,
LDimensions::Time - RDimensions::Time,
LDimensions::Current - RDimensions::Current,
LDimensions::Temperature - RDimensions::Temperature,
LDimensions::Amount - RDimensions::Amount,
LDimensions::LuminousIntensity -
RDimensions::LuminousIntensity,
LDimensions::Angle - RDimensions::Angle>;
};

} // namespace internal_dimensions
} // namespace quantities
} // namespace principia
30 changes: 30 additions & 0 deletions quantities/generators.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

#pragma once

namespace principia {
namespace quantities {
namespace internal_generators {

// These structs have a |Type| member that is a |Quantity| suitable for
// the result of the operation applied to argument(s) of the |Quantity| types
// given as template parameter(s).

template<typename Q, int n>
struct ExponentiationGenerator;

// Only legal if |n| divides the dimensions of |Q|.
template<typename Q, int n, typename = void>
struct NthRootGenerator;

template<typename Left, typename Right>
struct ProductGenerator;

template<typename Left, typename Right>
struct QuotientGenerator;

} // namespace internal_generators
} // namespace quantities
} // namespace principia

// Because of circular dependencies, this file doesn't include
// generators_body.hpp. This will be done by quantities_body.hpp.
117 changes: 117 additions & 0 deletions quantities/generators_body.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@

#pragma once

#include "quantities/generators.hpp"

#include "base/not_constructible.hpp"
#include "quantities/dimensions.hpp"
#include "quantities/quantities.hpp"

namespace principia {
namespace quantities {
namespace internal_generators {

using base::not_constructible;
using internal_dimensions::Dimensions;
using internal_dimensions::DimensionsExponentiationGenerator;
using internal_dimensions::DimensionsNthRootGenerator;
using internal_dimensions::DimensionsProductGenerator;
using internal_dimensions::DimensionsQuotientGenerator;
using internal_dimensions::NoDimensions;

template<typename Q>
struct Collapse : not_constructible {
using Type = Q;
};

template<>
struct Collapse<Quantity<NoDimensions>> : not_constructible {
using Type = double;
};

template<typename Q, int n>
struct ExponentiationGenerator : not_constructible {
using Type =
typename Collapse<
Quantity<typename DimensionsExponentiationGenerator<
typename Q::Dimensions, n>::Type>>::Type;
};

template<int n>
struct ExponentiationGenerator<double, n> : not_constructible {
using Type = double;
};

template<typename Q, int n, typename>
struct NthRootGenerator : not_constructible {
using Type =
typename Collapse<
Quantity<typename DimensionsNthRootGenerator<
typename Q::Dimensions, n>::Type>>::Type;
};

// NOTE(phl): We use |is_arithmetic| here, not |double|, to make it possible to
// write something like |Sqrt(2)|. We could use |is_arithmetic| in more places
// but it would make the template magic even harder to follow, so let's not do
// that until we have a good reason.
template<typename Q, int n>
struct NthRootGenerator<Q, n, std::enable_if_t<std::is_arithmetic<Q>::value>>
: not_constructible {
using Type = double;
};

template<typename Left, typename Right>
struct ProductGenerator : not_constructible {
using Type =
typename Collapse<
Quantity<typename DimensionsProductGenerator<
typename Left::Dimensions,
typename Right::Dimensions>::Type>>::Type;
};

template<typename Left>
struct ProductGenerator<Left, double> : not_constructible {
using Type = Left;
};

template<typename Right>
struct ProductGenerator<double, Right> : not_constructible {
using Type = Right;
};

template<>
struct ProductGenerator<double, double> : not_constructible {
using Type = double;
};

template<typename Left, typename Right>
struct QuotientGenerator : not_constructible {
using Type =
typename Collapse<
Quantity<typename DimensionsQuotientGenerator<
typename Left::Dimensions,
typename Right::Dimensions>::Type>>::Type;
};

template<typename Left>
struct QuotientGenerator<Left, double> : not_constructible {
using Type = Left;
};

template<typename Right>
struct QuotientGenerator<double, Right> : not_constructible {
using Type =
typename Collapse<
Quantity<typename DimensionsQuotientGenerator<
NoDimensions,
typename Right::Dimensions>::Type>>::Type;
};

template<>
struct QuotientGenerator<double, double> : not_constructible {
using Type = double;
};

} // namespace internal_generators
} // namespace quantities
} // namespace principia
16 changes: 16 additions & 0 deletions quantities/named_quantities.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

#pragma once

#include "quantities/generators.hpp"
#include "quantities/quantities.hpp"

namespace principia {
@@ -16,6 +17,21 @@ using Product = decltype(std::declval<Left>() * std::declval<Right>());
template<typename Left, typename Right>
using Quotient = decltype(std::declval<Left>() / std::declval<Right>());

template<typename T, int exponent>
using Exponentiation =
typename internal_generators::ExponentiationGenerator<T, exponent>::Type;
template<typename Q>
using Square = Exponentiation<Q, 2>;
template<typename Q>
using Cube = Exponentiation<Q, 3>;

template<typename Q, int n>
using NthRoot = typename internal_generators::NthRootGenerator<Q, n>::Type;
template<typename Q>
using SquareRoot = NthRoot<Q, 2>;
template<typename Q>
using CubeRoot = NthRoot<Q, 3>;

// The result type of the derivative of a |Value|-valued function with respect
// to its |Argument|-valued argument.
template<typename Value, typename Argument>
Loading