Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an integrator to the library #894

Open
drewr95 opened this issue May 24, 2024 · 15 comments
Open

Add an integrator to the library #894

drewr95 opened this issue May 24, 2024 · 15 comments

Comments

@drewr95
Copy link
Contributor

drewr95 commented May 24, 2024

I didn't see one when searching your library, if I missed it my apologies.

I have implemented my own that I think would do well here :). It is very similar to cyclic_value except it's clamped to the min and max range. It also has a compile-time MIN and MAX similar to cyclic value to save RAM.

Let me know if you'd like me to add it.

@jwellbelove
Copy link
Contributor

Could you show an example of how it would be used?

@drewr95
Copy link
Contributor Author

drewr95 commented May 24, 2024

Could you show an example of how it would be used?

Sure thing. I'll just paste the main incrementing and decrementing unittests. One for compile-time known min and maxes and run-time (I have 100% unit test coverage):

/**
 * @brief Tests preincrementing the integrator.
 */
TYPED_TEST(CompileTimeIntegratorTest, PreIncrement)
{
    constexpr TypeParam min{2};
    constexpr TypeParam max{10};
    common::Integrator<TypeParam, min, max> integrator{min};
    for (auto i = min; i < max; ++i)
    {
        EXPECT_EQ(i + 1, ++integrator);
    }
    EXPECT_EQ(max, integrator);

    // Try to increment past max.
    EXPECT_EQ(max, ++integrator);
}

/**
 * @brief Tests postincrementing the integer.
 */
TYPED_TEST(CompileTimeIntegratorTest, PostIncrement)
{
    constexpr TypeParam min{2};
    constexpr TypeParam max{10};
    common::Integrator<TypeParam, min, max> integrator{min};
    for (auto i = min; i < max; ++i)
    {
        EXPECT_EQ(i, integrator++);
    }
    EXPECT_EQ(max, integrator);

    // Try increment past max.
    integrator++;
    EXPECT_EQ(max, integrator);
}

/**
 * @brief Tests predecrementing the integrator.
 */
TYPED_TEST(CompileTimeIntegratorTest, PreDecrement)
{
    constexpr TypeParam min{2};
    constexpr TypeParam max{10};
    common::Integrator<TypeParam, min, max> integrator{max};
    for (auto i = max; i > min; --i)
    {
        EXPECT_EQ(i - 1, --integrator);
    }
    EXPECT_EQ(min, integrator);

    // Try to decrement past min.
    EXPECT_EQ(min, --integrator);
}

/**
 * @brief Tests the special handling when the integrator min is 0.  This is a
 * special case because the value can't be subtracted normally when the type is
 * unsigned.  This is because of unsigned integeger wrapping back to the max
 * value.
 */
TYPED_TEST(CompileTimeIntegratorTest, PreDecrementMin0)
{
    constexpr TypeParam min{0};
    constexpr TypeParam max{1};
    common::Integrator<TypeParam, min, max> integrator{max};
    EXPECT_EQ(min, --integrator);

    // Try to subtract past min.
    EXPECT_EQ(min, --integrator);
}

/**
 * @brief Tests postdecrementing the integrator.
 */
TYPED_TEST(CompileTimeIntegratorTest, PostDecrement)
{
    constexpr TypeParam min{2};
    constexpr TypeParam max{10};
    common::Integrator<TypeParam, min, max> integrator{max};
    for (auto i = max; i > min; --i)
    {
        EXPECT_EQ(i, integrator--);
    }
    EXPECT_EQ(min, integrator);

    // Try to decrement past min.
    integrator--;
    EXPECT_EQ(min, integrator);
}

/**
 * @brief Tests preincrementing the integrator.
 */
TYPED_TEST(RunTimeIntegratorTest, PreIncrement)
{
    constexpr TypeParam min{2};
    constexpr TypeParam max{10};
    common::Integrator<TypeParam> integrator{min, max};
    for (auto i = min; i < max; ++i)
    {
        EXPECT_EQ(i + 1, ++integrator);
    }
    EXPECT_EQ(max, integrator);

    // Try to increment past max.
    EXPECT_EQ(max, ++integrator);
}

I only show one example of the run time because it follows similarly.

@jwellbelove
Copy link
Contributor

Yeah, that looks good. Do a PR if you like.

@jwellbelove
Copy link
Contributor

Does the integrator also include other operators as well as ++ and --?
+

+=
-=

@drewr95
Copy link
Contributor Author

drewr95 commented May 25, 2024

Yeah, that looks good. Do a PR if you like.

Okay sounds good!

Does the integrator also include other operators as well as ++ and --?

+= -=

It does have ++ and -- as I show in the test. Testing both pre and post decrement. I did not implement += and -=. I don't think integrators do that though I don't see why I couldn't add that.

@drewr95
Copy link
Contributor Author

drewr95 commented May 25, 2024

Fo the += and the -=, does the value get clamped to the max and min respectively? say value is 1 and the range is [0, 2], and I do integrator += 5; Does the integrator get clamped to 2 or is the value ignored? I assumed clamped but wanted to clarify

@jwellbelove
Copy link
Contributor

I assumed that the value would always be clamped.

@jwellbelove
Copy link
Contributor

I been giving the idea some thought over the past few days and I think that the 'integrator' idea is just a specific case of a more general functionality.
I'm seeing it more as a specialisation of a numeric type. Sort of a 'clamped' numeric, with implicit casts to and from the underlying type.

template <typename T, T Minimum = etl::intergral_limits<T>::min, T maximum = etl::intergral_limits<T>::max>
class numeric
{
  //...
};

or possibly an accumulator type, with min/max bounds.

@drewr95
Copy link
Contributor Author

drewr95 commented May 27, 2024

I been giving the idea some thought over the past few days and I think that the 'integrator' idea is just a specific case of a more general functionality. I'm seeing it more as a specialisation of a numeric type. Sort of a 'clamped' numeric, with implicit casts to and from the underlying type.

template <typename T, T Minimum = etl::intergral_limits<T>::min, T maximum = etl::intergral_limits<T>::max>
class numeric
{
  //...
};

or possibly an accumulator type, with min/max bounds.

Yeah that could work. Though if we do that, maybe you just want to extend type_def? You already did most of the work with that class, and I feel something like numeric is exactly that. Just with specified limits.

Also, if this supports floating point types, I don't think you can have a compile time specialization because of the template check would fail due to floating point equality comparisons.

@jwellbelove
Copy link
Contributor

Yes, you're right.
Maybe the original idea of an accumulator type similar to the implementation of etl::cyclic_value is the better option.

The ETL has got so many features now that I sometimes forget what's already been implemented!

@drewr95
Copy link
Contributor Author

drewr95 commented May 27, 2024

Yes, you're right. Maybe the original idea of an accumulator type similar to the implementation of etl::cyclic_value is the better option.

Yeah I think I like the accumulator type as well :]

The ETL has got so many features now that I sometimes forget what's already been implemented!

You have made embedded development exponentially less of a pain with this library, it is amazing.

@drewr95
Copy link
Contributor Author

drewr95 commented May 27, 2024

Seems like an accumulator is slightly different in controls.

I don't think it would take long to implement (basically just implementing operator+ and operator- as well). Would you like me to implement both? If so I will create a separate issue for accumulator.

Also looks like Boost has accumulators might want to look at those as well

@jwellbelove
Copy link
Contributor

jwellbelove commented May 27, 2024

What do you see as the fundamental difference in functionality of accumulator and integrator?

@drewr95
Copy link
Contributor Author

drewr95 commented May 27, 2024

What do you see as the fundamental difference in functionality of accumulator and integrator?

Honestly I'm not even sure myself lol. I'll go ahead and implement it, if it's wrong it can always be fixed.

@jaskij
Copy link
Contributor

jaskij commented Jul 3, 2024

What do you see as the fundamental difference in functionality of accumulator and integrator?

To me, accumulator is just a map-reduce operation, similar to std::transform_reduce or the like. An integrator would be a proper numerical integration algorithm. You most likely should be able to implement such an integrator using the accumulator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

No branches or pull requests

3 participants