BigFraction.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /*
  2. * Copyright (c) 2022, Lucas Chollet <lucas.chollet@free.fr>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "BigFraction.h"
  7. #include <AK/ByteString.h>
  8. #include <AK/Math.h>
  9. #include <AK/StringBuilder.h>
  10. #include <LibCrypto/NumberTheory/ModularFunctions.h>
  11. namespace Crypto {
  12. BigFraction::BigFraction(SignedBigInteger numerator, UnsignedBigInteger denominator)
  13. : m_numerator(move(numerator))
  14. , m_denominator(move(denominator))
  15. {
  16. VERIFY(m_denominator != 0);
  17. reduce();
  18. }
  19. BigFraction::BigFraction(SignedBigInteger value)
  20. : BigFraction(move(value), 1)
  21. {
  22. }
  23. ErrorOr<BigFraction> BigFraction::from_string(StringView sv)
  24. {
  25. auto maybe_dot_index = sv.find('.');
  26. auto integer_part_view = sv.substring_view(0, maybe_dot_index.value_or(sv.length()));
  27. auto fraction_part_view = maybe_dot_index.has_value() ? sv.substring_view(1 + *maybe_dot_index) : "0"sv;
  28. auto integer_part = TRY(SignedBigInteger::from_base(10, integer_part_view));
  29. auto fractional_part = TRY(SignedBigInteger::from_base(10, fraction_part_view));
  30. auto fraction_length = UnsignedBigInteger(static_cast<u64>(fraction_part_view.length()));
  31. if (!sv.is_empty() && sv[0] == '-')
  32. fractional_part.negate();
  33. return BigFraction(move(integer_part)) + BigFraction(move(fractional_part), NumberTheory::Power("10"_bigint, move(fraction_length)));
  34. }
  35. BigFraction BigFraction::operator+(BigFraction const& rhs) const
  36. {
  37. if (rhs.m_numerator == "0"_bigint)
  38. return *this;
  39. auto result = *this;
  40. result.m_numerator.set_to(m_numerator.multiplied_by(rhs.m_denominator).plus(rhs.m_numerator.multiplied_by(m_denominator)));
  41. result.m_denominator.set_to(m_denominator.multiplied_by(rhs.m_denominator));
  42. result.reduce();
  43. return result;
  44. }
  45. BigFraction BigFraction::operator-(BigFraction const& rhs) const
  46. {
  47. return *this + (-rhs);
  48. }
  49. BigFraction BigFraction::operator*(BigFraction const& rhs) const
  50. {
  51. auto result = *this;
  52. result.m_numerator.set_to(result.m_numerator.multiplied_by(rhs.m_numerator));
  53. result.m_denominator.set_to(result.m_denominator.multiplied_by(rhs.m_denominator));
  54. result.reduce();
  55. return result;
  56. }
  57. BigFraction BigFraction::operator-() const
  58. {
  59. return { m_numerator.negated_value(), m_denominator };
  60. }
  61. BigFraction BigFraction::invert() const
  62. {
  63. return BigFraction { 1 } / *this;
  64. }
  65. BigFraction BigFraction::operator/(BigFraction const& rhs) const
  66. {
  67. VERIFY(rhs.m_numerator != "0"_bigint);
  68. auto result = *this;
  69. result.m_numerator.set_to(m_numerator.multiplied_by(rhs.m_denominator));
  70. result.m_denominator.set_to(m_denominator.multiplied_by(rhs.m_numerator.unsigned_value()));
  71. if (rhs.m_numerator.is_negative())
  72. result.m_numerator.negate();
  73. result.reduce();
  74. return result;
  75. }
  76. bool BigFraction::operator<(BigFraction const& rhs) const
  77. {
  78. return (*this - rhs).m_numerator.is_negative();
  79. }
  80. bool BigFraction::operator==(BigFraction const& rhs) const
  81. {
  82. return m_numerator == rhs.m_numerator && m_denominator == rhs.m_denominator;
  83. }
  84. BigFraction::BigFraction(double d)
  85. {
  86. bool negative = false;
  87. if (d < 0) {
  88. negative = true;
  89. d = -d;
  90. }
  91. i8 current_pow = 0;
  92. while (AK::pow(10.0, (double)current_pow) <= d)
  93. current_pow += 1;
  94. current_pow -= 1;
  95. unsigned decimal_places = 0;
  96. while (d >= NumericLimits<double>::epsilon() || current_pow >= 0) {
  97. m_numerator.set_to(m_numerator.multiplied_by(SignedBigInteger { 10 }));
  98. i8 digit = (u64)(d * AK::pow(0.1, (double)current_pow)) % 10;
  99. m_numerator.set_to(m_numerator.plus(UnsignedBigInteger { digit }));
  100. d -= digit * AK::pow(10.0, (double)current_pow);
  101. if (current_pow < 0) {
  102. ++decimal_places;
  103. m_denominator.set_to(NumberTheory::Power("10"_bigint, UnsignedBigInteger { decimal_places }));
  104. }
  105. current_pow -= 1;
  106. }
  107. m_numerator.set_to(negative ? (m_numerator.negated_value()) : m_numerator);
  108. }
  109. double BigFraction::to_double() const
  110. {
  111. // FIXME: very naive implementation
  112. return m_numerator.to_double() / m_denominator.to_double();
  113. }
  114. void BigFraction::set_to_0()
  115. {
  116. m_numerator.set_to_0();
  117. m_denominator.set_to(1);
  118. }
  119. BigFraction BigFraction::rounded(unsigned rounding_threshold) const
  120. {
  121. auto const get_last_digit = [](auto const& integer) {
  122. return integer.divided_by("10"_bigint).remainder;
  123. };
  124. auto res = m_numerator.divided_by(m_denominator);
  125. BigFraction result { move(res.quotient) };
  126. auto const needed_power = NumberTheory::Power("10"_bigint, UnsignedBigInteger { rounding_threshold });
  127. // We get one more digit to do proper rounding
  128. auto const fractional_value = res.remainder.multiplied_by(needed_power.multiplied_by("10"_bigint)).divided_by(m_denominator).quotient;
  129. result.m_numerator.set_to(result.m_numerator.multiplied_by(needed_power));
  130. result.m_numerator.set_to(result.m_numerator.plus(fractional_value.divided_by("10"_bigint).quotient));
  131. if (get_last_digit(fractional_value) > "4"_bigint)
  132. result.m_numerator.set_to(result.m_numerator.plus("1"_bigint));
  133. result.m_denominator.set_to(result.m_denominator.multiplied_by(needed_power));
  134. return result;
  135. }
  136. void BigFraction::reduce()
  137. {
  138. auto const gcd = NumberTheory::GCD(m_numerator.unsigned_value(), m_denominator);
  139. if (gcd == 1)
  140. return;
  141. auto const numerator_divide = m_numerator.divided_by(gcd);
  142. VERIFY(numerator_divide.remainder == "0"_bigint);
  143. m_numerator = numerator_divide.quotient;
  144. auto const denominator_divide = m_denominator.divided_by(gcd);
  145. VERIFY(denominator_divide.remainder == "0"_bigint);
  146. m_denominator = denominator_divide.quotient;
  147. }
  148. ByteString BigFraction::to_byte_string(unsigned rounding_threshold) const
  149. {
  150. StringBuilder builder;
  151. if (m_numerator.is_negative() && m_numerator != "0"_bigint)
  152. builder.append('-');
  153. auto const number_of_digits = [](auto integer) {
  154. unsigned size = 1;
  155. for (auto division_result = integer.divided_by(UnsignedBigInteger { 10 });
  156. division_result.remainder == UnsignedBigInteger { 0 } && division_result.quotient != UnsignedBigInteger { 0 };
  157. division_result = division_result.quotient.divided_by(UnsignedBigInteger { 10 })) {
  158. ++size;
  159. }
  160. return size;
  161. };
  162. auto const rounded_fraction = rounded(rounding_threshold);
  163. // We take the unsigned value as we already manage the '-'
  164. auto const full_value = rounded_fraction.m_numerator.unsigned_value().to_base_deprecated(10);
  165. int split = full_value.length() - (number_of_digits(rounded_fraction.m_denominator) - 1);
  166. if (split < 0)
  167. split = 0;
  168. auto const remove_trailing_zeros = [](StringView value) -> StringView {
  169. auto n = value.length();
  170. VERIFY(n > 0);
  171. while (value.characters_without_null_termination()[n - 1] == '0')
  172. --n;
  173. return { value.characters_without_null_termination(), n };
  174. };
  175. auto const raw_fractional_value = full_value.substring(split, full_value.length() - split);
  176. auto const integer_value = split == 0 ? "0"sv : full_value.substring_view(0, split);
  177. auto const fractional_value = rounding_threshold == 0 ? "0"sv : remove_trailing_zeros(raw_fractional_value);
  178. builder.append(integer_value);
  179. bool const has_decimal_part = fractional_value.length() > 0 && fractional_value != "0";
  180. if (has_decimal_part) {
  181. builder.append('.');
  182. auto number_pre_zeros = number_of_digits(rounded_fraction.m_denominator) - full_value.length() - 1;
  183. if (number_pre_zeros > rounding_threshold || fractional_value == "0")
  184. number_pre_zeros = 0;
  185. builder.append_repeated('0', number_pre_zeros);
  186. if (fractional_value != "0")
  187. builder.append(fractional_value);
  188. }
  189. return builder.to_byte_string();
  190. }
  191. BigFraction BigFraction::sqrt() const
  192. {
  193. // FIXME: very naive implementation
  194. return BigFraction { AK::sqrt(to_double()) };
  195. }
  196. }