gcd.c 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include <linux/kernel.h>
  3. #include <linux/gcd.h>
  4. #include <linux/export.h>
  5. /*
  6. * This implements the binary GCD algorithm. (Often attributed to Stein,
  7. * but as Knuth has noted, appears in a first-century Chinese math text.)
  8. *
  9. * This is faster than the division-based algorithm even on x86, which
  10. * has decent hardware division.
  11. */
  12. DEFINE_STATIC_KEY_TRUE(efficient_ffs_key);
  13. #if !defined(CONFIG_CPU_NO_EFFICIENT_FFS)
  14. /* If __ffs is available, the even/odd algorithm benchmarks slower. */
  15. static unsigned long binary_gcd(unsigned long a, unsigned long b)
  16. {
  17. unsigned long r = a | b;
  18. b >>= __ffs(b);
  19. if (b == 1)
  20. return r & -r;
  21. for (;;) {
  22. a >>= __ffs(a);
  23. if (a == 1)
  24. return r & -r;
  25. if (a == b)
  26. return a << __ffs(r);
  27. if (a < b)
  28. swap(a, b);
  29. a -= b;
  30. }
  31. }
  32. #endif
  33. /* If normalization is done by loops, the even/odd algorithm is a win. */
  34. /**
  35. * gcd - calculate and return the greatest common divisor of 2 unsigned longs
  36. * @a: first value
  37. * @b: second value
  38. */
  39. unsigned long gcd(unsigned long a, unsigned long b)
  40. {
  41. unsigned long r = a | b;
  42. if (!a || !b)
  43. return r;
  44. #if !defined(CONFIG_CPU_NO_EFFICIENT_FFS)
  45. if (static_branch_likely(&efficient_ffs_key))
  46. return binary_gcd(a, b);
  47. #endif
  48. /* Isolate lsbit of r */
  49. r &= -r;
  50. while (!(b & r))
  51. b >>= 1;
  52. if (b == r)
  53. return r;
  54. for (;;) {
  55. while (!(a & r))
  56. a >>= 1;
  57. if (a == r)
  58. return r;
  59. if (a == b)
  60. return a;
  61. if (a < b)
  62. swap(a, b);
  63. a -= b;
  64. a >>= 1;
  65. if (a & r)
  66. a += b;
  67. a >>= 1;
  68. }
  69. }
  70. EXPORT_SYMBOL_GPL(gcd);