Implemented Number::{ceil, floor, round, pow, inverse}
Except that pow only accepts integer exponents, since nth root is not that quick to implement and simple blind implementation may have performance issues.
This commit is contained in:
parent
ab6ed8f45c
commit
eb36ad2c93
@ -667,6 +667,13 @@ struct Number
|
|||||||
Number operator/(Number const& rhs) const;
|
Number operator/(Number const& rhs) const;
|
||||||
Number& operator/=(Number const& rhs);
|
Number& operator/=(Number const& rhs);
|
||||||
|
|
||||||
|
Number floor() const; ///< Return number rounded down to nearest integer
|
||||||
|
Number ceil() const; ///< Return number rounded up to nearest integer
|
||||||
|
Number round() const; ///< Return number rounded to nearest integer
|
||||||
|
|
||||||
|
Number inverse() const; ///< Return number raised to power -1
|
||||||
|
Number pow(Number n) const; ///< Return number raised to power `n`.
|
||||||
|
|
||||||
/// Parses source contained by token into a Number instance
|
/// Parses source contained by token into a Number instance
|
||||||
static Result<Number> from(Token token);
|
static Result<Number> from(Token token);
|
||||||
};
|
};
|
||||||
|
@ -132,22 +132,24 @@ static Result<Value> comparison_operator(Interpreter&, std::vector<Value> args)
|
|||||||
return Value::from(Binary_Predicate{}(args.front(), args.back()));
|
return Value::from(Binary_Predicate{}(args.front(), args.back()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using Operator_Entry = std::tuple<char const*, Intrinsic>;
|
||||||
|
|
||||||
/// Operators definition table
|
/// Operators definition table
|
||||||
static constexpr auto Operators = std::array {
|
static constexpr auto Operators = std::array {
|
||||||
std::tuple { "+", plus_minus_operator<std::plus<>> },
|
Operator_Entry { "+", plus_minus_operator<std::plus<>> },
|
||||||
std::tuple { "-", plus_minus_operator<std::minus<>> },
|
Operator_Entry { "-", plus_minus_operator<std::minus<>> },
|
||||||
std::tuple { "*", binary_operator<std::multiplies<>, '*'> },
|
Operator_Entry { "*", binary_operator<std::multiplies<>, '*'> },
|
||||||
std::tuple { "/", binary_operator<std::divides<>, '/'> },
|
Operator_Entry { "/", binary_operator<std::divides<>, '/'> },
|
||||||
|
|
||||||
std::tuple { "<", comparison_operator<std::less<>> },
|
Operator_Entry { "<", comparison_operator<std::less<>> },
|
||||||
std::tuple { ">", comparison_operator<std::greater<>> },
|
Operator_Entry { ">", comparison_operator<std::greater<>> },
|
||||||
std::tuple { "<=", comparison_operator<std::less_equal<>> },
|
Operator_Entry { "<=", comparison_operator<std::less_equal<>> },
|
||||||
std::tuple { ">=", comparison_operator<std::greater_equal<>> },
|
Operator_Entry { ">=", comparison_operator<std::greater_equal<>> },
|
||||||
|
|
||||||
std::tuple { "==", equality_operator<std::equal_to<>> },
|
Operator_Entry { "==", equality_operator<std::equal_to<>> },
|
||||||
std::tuple { "!=", equality_operator<std::not_equal_to<>> },
|
Operator_Entry { "!=", equality_operator<std::not_equal_to<>> },
|
||||||
|
|
||||||
std::tuple { ".",
|
Operator_Entry { ".",
|
||||||
+[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
|
+[](Interpreter &i, std::vector<Value> args) -> Result<Value> {
|
||||||
assert(args.size() == 2, "Operator . requires two arguments"); // TODO(assert)
|
assert(args.size() == 2, "Operator . requires two arguments"); // TODO(assert)
|
||||||
assert(args.back().type == Value::Type::Number, "Only numbers can be used for indexing"); // TODO(assert)
|
assert(args.back().type == Value::Type::Number, "Only numbers can be used for indexing"); // TODO(assert)
|
||||||
@ -155,7 +157,7 @@ static constexpr auto Operators = std::array {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
std::tuple { "&",
|
Operator_Entry { "&",
|
||||||
+[](Interpreter&, std::vector<Value> args) -> Result<Value> {
|
+[](Interpreter&, std::vector<Value> args) -> Result<Value> {
|
||||||
using Chord_Chord = Shape<Value::Type::Music, Value::Type::Music>;
|
using Chord_Chord = Shape<Value::Type::Music, Value::Type::Music>;
|
||||||
|
|
||||||
|
@ -190,3 +190,93 @@ parse_fractional:
|
|||||||
|
|
||||||
return result.simplify();
|
return result.simplify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Number Number::floor() const
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
if (den <= -1 || den >= 1) {
|
||||||
|
if (auto const r = result.num % result.den; r != 0) {
|
||||||
|
result.num += ((num < 0) xor (den < 0) ? 1 : -1) * r;
|
||||||
|
result.num /= result.den;
|
||||||
|
result.den = 1;
|
||||||
|
} else {
|
||||||
|
result.simplify_inplace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Number Number::ceil() const
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
if (den <= -1 || den >= 1) {
|
||||||
|
if (auto const r = result.num % result.den; r != 0) {
|
||||||
|
result.num += ((num < 0) xor (den < 0) ? -1 : 1) * r;
|
||||||
|
result.num /= result.den;
|
||||||
|
result.den = 1;
|
||||||
|
} else {
|
||||||
|
result.simplify_inplace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Number Number::round() const
|
||||||
|
{
|
||||||
|
auto result = *this;
|
||||||
|
if (den <= -1 || den >= 1) {
|
||||||
|
if (auto const r = result.num % result.den; r != 0) {
|
||||||
|
auto const dir = r * 2 >= result.den ? -1 : 1;
|
||||||
|
result.num += ((num < 0) xor (den < 0) ? dir : -dir) * r;
|
||||||
|
result.num /= result.den;
|
||||||
|
result.den = 1;
|
||||||
|
} else {
|
||||||
|
result.simplify_inplace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Number Number::inverse() const
|
||||||
|
{
|
||||||
|
assert(num != 0, "Cannot take inverse of fraction with 0 in denominator");
|
||||||
|
return { den, num };
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
// Raise Number to integer power helper
|
||||||
|
static inline Number pow(Number const& x, decltype(Number::num) n)
|
||||||
|
{
|
||||||
|
// We simply raise numerator and denominator to required power
|
||||||
|
// and if n is negative we take inverse.
|
||||||
|
if (n == 0) return Number(1);
|
||||||
|
auto result = Number { 1, 1 };
|
||||||
|
|
||||||
|
auto flip = false;
|
||||||
|
if (n < 0) { flip = true; n = -n; }
|
||||||
|
|
||||||
|
for (auto i = n; i != 0; --i) result.num *= x.num;
|
||||||
|
for (auto i = n; i != 0; --i) result.den *= x.den;
|
||||||
|
return flip ? result.inverse() : result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Number Number::pow(Number n) const
|
||||||
|
{
|
||||||
|
n.simplify_inplace();
|
||||||
|
|
||||||
|
// Simple case, we raise this to integer power.
|
||||||
|
if (n.den == 1) {
|
||||||
|
return impl::pow(*this, n.num);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hard case, we raise this to fractional power.
|
||||||
|
// Essentialy finding n.den root of (x to n.num power).
|
||||||
|
// We need to protect ourselfs against even roots of negative numbers.
|
||||||
|
// TODO Implement this case properly.
|
||||||
|
//
|
||||||
|
// TODO(assert) <- taking power is not always doable so this operation
|
||||||
|
// should produce error not crash with assertion failure in the future.
|
||||||
|
unimplemented();
|
||||||
|
}
|
||||||
|
@ -52,4 +52,47 @@ suite number_test = [] {
|
|||||||
test_number_from(".75", Number(3, 4));
|
test_number_from(".75", Number(3, 4));
|
||||||
test_number_from("120.", Number(120, 1));
|
test_number_from("120.", Number(120, 1));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
"Rounding"_test = [] {
|
||||||
|
should("Support floor operation") = [] {
|
||||||
|
expect(eq(Number(0), Number(1, 2).floor()));
|
||||||
|
expect(eq(Number(1), Number(3, 2).floor()));
|
||||||
|
expect(eq(Number(-1), Number(-1, 2).floor()));
|
||||||
|
expect(eq(Number(0), Number(0).floor()));
|
||||||
|
expect(eq(Number(1), Number(1).floor()));
|
||||||
|
expect(eq(Number(-1), Number(-1).floor()));
|
||||||
|
};
|
||||||
|
|
||||||
|
should("Support ceil operation") = [] {
|
||||||
|
expect(eq(Number(1), Number(1, 2).ceil()));
|
||||||
|
expect(eq(Number(2), Number(3, 2).ceil()));
|
||||||
|
expect(eq(Number(0), Number(-1, 2).ceil()));
|
||||||
|
expect(eq(Number(-1), Number(-3, 2).ceil()));
|
||||||
|
expect(eq(Number(0), Number(0).ceil()));
|
||||||
|
expect(eq(Number(1), Number(1).ceil()));
|
||||||
|
expect(eq(Number(-1), Number(-1).ceil()));
|
||||||
|
};
|
||||||
|
|
||||||
|
should("Support round operation") = [] {
|
||||||
|
expect(eq(Number(1), Number(3, 4).round()));
|
||||||
|
expect(eq(Number(0), Number(1, 4).round()));
|
||||||
|
expect(eq(Number(1), Number(5, 4).round()));
|
||||||
|
expect(eq(Number(2), Number(7, 4).round()));
|
||||||
|
|
||||||
|
expect(eq(Number(-1), Number(-3, 4).round()));
|
||||||
|
expect(eq(Number(0), Number(-1, 4).round()));
|
||||||
|
expect(eq(Number(-1), Number(-5, 4).round()));
|
||||||
|
expect(eq(Number(-2), Number(-7, 4).round()));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"Number exponantiation"_test = [] {
|
||||||
|
should("Support integer powers") = [] {
|
||||||
|
expect(eq(Number(1, 4), Number(1, 2).pow(Number(2))));
|
||||||
|
expect(eq(Number(4, 1), Number(1, 2).pow(Number(-2))));
|
||||||
|
|
||||||
|
expect(eq(Number(4, 1), Number(2).pow(Number(2))));
|
||||||
|
expect(eq(Number(1, 4), Number(2).pow(Number(-2))));
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user