use crate::Rational;
use crate::rational::arithmetic::traits::{
DenominatorsInClosedInterval, SimplestRationalInInterval,
};
use alloc::collections::BinaryHeap;
use alloc::vec::Vec;
use core::cmp::Ordering::*;
use core::cmp::{Ordering, Reverse};
use malachite_base::num::arithmetic::traits::{
Ceiling, CoprimeWith, Floor, Reciprocal, UnsignedAbs,
};
use malachite_base::num::basic::traits::{One, Zero};
use malachite_base::num::factorization::traits::Primes;
use malachite_nz::integer::Integer;
use malachite_nz::natural::Natural;
use malachite_nz::platform::Limb;
crate_test_fn! {smallest_guaranteed_denominator(interval_diameter: &Rational) -> Natural {
if *interval_diameter >= 1u32 {
return Natural::ONE;
}
let mut primorial = Natural::ONE;
let mut pow = Natural::ONE;
let mut best = Natural::ONE;
for p in Limb::primes() {
let bound = Rational::from(&pow) / interval_diameter;
if bound <= primorial {
break;
}
best = bound.floor().unsigned_abs() + Natural::ONE;
primorial *= Natural::from(p);
pow <<= 1u32;
}
best
}}
fn denominator_present(a: &Rational, b: &Rational, d: &Natural) -> bool {
let mut n = (a * Rational::from(d)).ceiling();
let hi = (b * Rational::from(d)).floor();
while n <= hi {
if n.unsigned_abs_ref().coprime_with(d) {
return true;
}
n += Integer::ONE;
}
false
}
fn smallest_likely_denominator(interval_diameter: &Rational) -> Natural {
interval_diameter.reciprocal().ceiling().unsigned_abs()
}
#[derive(Clone, Debug)]
struct Gap {
candidate: Rational,
lo: Rational,
hi: Rational,
lo_is_endpoint: bool,
hi_is_endpoint: bool,
}
impl Gap {
fn new(lo: Rational, hi: Rational, lo_is_endpoint: bool, hi_is_endpoint: bool) -> Self {
let candidate = if lo_is_endpoint || hi_is_endpoint {
Rational::simplest_rational_in_open_interval(&lo, &hi)
} else {
let n = Integer::from_sign_and_abs_ref(lo.sign, &lo.numerator)
+ Integer::from_sign_and_abs_ref(hi.sign, &hi.numerator);
Rational {
sign: n >= 0u32,
numerator: n.unsigned_abs(),
denominator: &lo.denominator + &hi.denominator,
}
};
Self {
candidate,
lo,
hi,
lo_is_endpoint,
hi_is_endpoint,
}
}
}
impl PartialEq for Gap {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.candidate == other.candidate
}
}
impl Eq for Gap {}
impl PartialOrd for Gap {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Gap {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.candidate
.denominator
.cmp(&other.candidate.denominator)
.then_with(|| self.candidate.cmp(&other.candidate))
}
}
#[derive(Clone, Debug)]
pub struct DenominatorsInClosedRationalInterval {
a: Rational,
b: Rational,
low_threshold: Natural,
high_threshold: Natural,
current: Natural,
gaps: BinaryHeap<Reverse<Gap>>,
endpoint_denominators: Vec<Natural>,
}
impl DenominatorsInClosedRationalInterval {
fn split_gaps_with_denominator(&mut self, d: &Natural) {
while let Some(Reverse(g)) = self.gaps.peek() {
if g.candidate.denominator_ref() != d {
break;
}
let Reverse(g) = self.gaps.pop().unwrap();
self.gaps.push(Reverse(Gap::new(
g.lo,
g.candidate.clone(),
g.lo_is_endpoint,
false,
)));
self.gaps.push(Reverse(Gap::new(
g.candidate,
g.hi,
false,
g.hi_is_endpoint,
)));
}
}
}
impl Iterator for DenominatorsInClosedRationalInterval {
type Item = Natural;
fn next(&mut self) -> Option<Natural> {
if self.current >= self.high_threshold {
self.gaps.clear();
self.endpoint_denominators.clear();
self.current += Natural::ONE;
Some(self.current.clone())
} else if self.current >= self.low_threshold {
self.gaps.clear();
self.endpoint_denominators.clear();
loop {
self.current += Natural::ONE;
if denominator_present(&self.a, &self.b, &self.current) {
return Some(self.current.clone());
}
}
} else {
if self.gaps.is_empty() {
assert_eq!(self.current, 0u32);
let ad = self.a.denominator_ref();
let bd = self.b.denominator_ref();
self.endpoint_denominators = match ad.cmp(bd) {
Equal => alloc::vec![ad.clone()],
Less => alloc::vec![ad.clone(), bd.clone()],
Greater => alloc::vec![bd.clone(), ad.clone()],
};
self.gaps.push(Reverse(Gap::new(
self.a.clone(),
self.b.clone(),
true,
true,
)));
}
let heap_denominator = self
.gaps
.peek()
.unwrap()
.0
.candidate
.denominator_ref()
.clone();
if self
.endpoint_denominators
.first()
.is_some_and(|pd| *pd <= heap_denominator)
{
let pd = self.endpoint_denominators.remove(0);
if pd == heap_denominator {
self.split_gaps_with_denominator(&heap_denominator);
}
self.current = pd.clone();
Some(pd)
} else {
self.split_gaps_with_denominator(&heap_denominator);
self.current = heap_denominator.clone();
Some(heap_denominator)
}
}
}
}
impl DenominatorsInClosedInterval for Rational {
type Denominators = DenominatorsInClosedRationalInterval;
fn denominators_in_closed_interval(
a: Rational,
b: Rational,
) -> DenominatorsInClosedRationalInterval {
assert!(a < b);
let diameter = &b - &a;
let (mut low_threshold, high_threshold) = if diameter >= 1u32 {
(Natural::ZERO, Natural::ZERO)
} else {
(
smallest_likely_denominator(&diameter),
smallest_guaranteed_denominator(&diameter),
)
};
if low_threshold < 100u32 {
low_threshold = Natural::ZERO;
}
DenominatorsInClosedRationalInterval {
a,
b,
low_threshold,
high_threshold,
current: Natural::ZERO,
gaps: BinaryHeap::new(),
endpoint_denominators: Vec::new(),
}
}
}