1use crate::algorithms::convolution::KaratsubaHint;
2use crate::algorithms::eea::{signed_gcd, signed_lcm};
3use crate::algorithms::matmul::StrassenHint;
4use crate::algorithms::poly_gcd::PolyTFracGCDRing;
5use crate::algorithms::poly_gcd::gcd::poly_gcd_local;
6use crate::algorithms::poly_gcd::squarefree_part::poly_power_decomposition_local;
7use crate::divisibility::{DivisibilityRing, DivisibilityRingStore, Domain};
8use crate::field::*;
9use crate::homomorphism::*;
10use crate::computation::DontObserve;
11use crate::integer::*;
12use crate::ordered::{OrderedRing, OrderedRingStore};
13use crate::rings::poly::dense_poly::DensePolyRing;
14use crate::rings::poly::*;
15use crate::impl_interpolation_base_ring_char_zero;
16use crate::pid::{EuclideanRing, PrincipalIdealRing};
17use crate::ring::*;
18
19use std::fmt::Debug;
20
21#[derive(Debug)]
60pub struct RationalFieldBase<I: RingStore>
61 where I::Type: IntegerRing
62{
63 integers: I
64}
65
66impl<I> Clone for RationalFieldBase<I>
67 where I: RingStore + Clone,
68 I::Type: IntegerRing
69{
70 fn clone(&self) -> Self {
71 Self {
72 integers: self.integers.clone()
73 }
74 }
75}
76
77impl<I> Copy for RationalFieldBase<I>
78 where I: RingStore + Copy,
79 I::Type: IntegerRing,
80 El<I>: Copy
81{}
82
83pub type RationalField<I> = RingValue<RationalFieldBase<I>>;
87
88pub struct RationalFieldEl<I>(El<I>, El<I>)
89 where I: RingStore,
90 I::Type: IntegerRing;
91
92impl<I> Debug for RationalFieldEl<I>
93 where I: RingStore,
94 I::Type: IntegerRing,
95 El<I>: Debug
96{
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 write!(f, "RationalFieldEl({:?}, {:?})", &self.0, &self.1)
99 }
100}
101
102impl<I> Clone for RationalFieldEl<I>
103 where I: RingStore,
104 I::Type: IntegerRing,
105 El<I>: Clone
106{
107 fn clone(&self) -> Self {
108 RationalFieldEl(self.0.clone(), self.1.clone())
109 }
110}
111
112impl<I> Copy for RationalFieldEl<I>
113 where I: RingStore,
114 I::Type: IntegerRing,
115 El<I>: Copy
116{}
117
118impl<I> PartialEq for RationalFieldBase<I>
119 where I: RingStore,
120 I::Type: IntegerRing
121{
122 fn eq(&self, other: &Self) -> bool {
123 self.integers.get_ring() == other.integers.get_ring()
124 }
125}
126
127impl<I> RationalFieldBase<I>
128 where I: RingStore,
129 I::Type: IntegerRing
130{
131 pub fn num<'a>(&'a self, el: &'a <Self as RingBase>::Element) -> &'a El<I> {
148 debug_assert!(self.base_ring().is_one(&signed_gcd(self.base_ring().clone_el(&el.1), self.base_ring().clone_el(&el.0), self.base_ring())));
149 &el.0
150 }
151
152 pub fn den<'a>(&'a self, el: &'a <Self as RingBase>::Element) -> &'a El<I> {
169 debug_assert!(self.base_ring().is_one(&signed_gcd(self.base_ring().clone_el(&el.1), self.base_ring().clone_el(&el.0), self.base_ring())));
170 &el.1
171 }
172}
173
174impl<I> RationalField<I>
175 where I: RingStore,
176 I::Type: IntegerRing
177{
178 pub const fn new(integers: I) -> Self {
179 RingValue::from(RationalFieldBase { integers })
180 }
181
182 pub fn num<'a>(&'a self, el: &'a El<Self>) -> &'a El<I> {
186 self.get_ring().num(el)
187 }
188
189 pub fn den<'a>(&'a self, el: &'a El<Self>) -> &'a El<I> {
193 self.get_ring().den(el)
194 }
195}
196
197impl<I> RationalFieldBase<I>
198 where I: RingStore,
199 I::Type: IntegerRing
200{
201 fn reduce(&self, value: (&mut El<I>, &mut El<I>)) {
202 let gcd = signed_gcd(self.integers.clone_el(&*value.1), self.integers.clone_el(&*value.0), &self.integers);
204 *value.0 = self.integers.checked_div(&*value.0, &gcd).unwrap();
205 *value.1 = self.integers.checked_div(&*value.1, &gcd).unwrap();
206 }
207
208 fn mul_assign_raw(&self, lhs: &mut <Self as RingBase>::Element, rhs: (&El<I>, &El<I>)) {
209 self.integers.mul_assign_ref(&mut lhs.0, rhs.0);
210 self.integers.mul_assign_ref(&mut lhs.1, rhs.1);
211 self.reduce((&mut lhs.0, &mut lhs.1));
212 }
213}
214
215impl<I> RingBase for RationalFieldBase<I>
216 where I: RingStore,
217 I::Type: IntegerRing
218{
219 type Element = RationalFieldEl<I>;
220
221 fn add_assign(&self, lhs: &mut Self::Element, mut rhs: Self::Element) {
222 if self.integers.is_one(&lhs.1) && self.integers.is_one(&rhs.1) {
223 self.integers.add_assign(&mut lhs.0, rhs.0);
224 } else {
225 self.integers.mul_assign_ref(&mut lhs.0, &rhs.1);
226 self.integers.mul_assign_ref(&mut rhs.0, &lhs.1);
227 self.integers.mul_assign(&mut lhs.1, rhs.1);
228 self.integers.add_assign(&mut lhs.0, rhs.0);
229 self.reduce((&mut lhs.0, &mut lhs.1));
230 }
231 }
232
233 fn clone_el(&self, val: &Self::Element) -> Self::Element {
234 RationalFieldEl(self.integers.clone_el(&val.0), self.integers.clone_el(&val.1))
235 }
236
237 fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
238 if self.integers.is_one(&lhs.1) && self.integers.is_one(&rhs.1) {
239 self.integers.add_assign_ref(&mut lhs.0, &rhs.0);
240 } else {
241 self.integers.mul_assign_ref(&mut lhs.0, &rhs.1);
242 self.integers.add_assign(&mut lhs.0, self.integers.mul_ref(&lhs.1, &rhs.0));
243 self.integers.mul_assign_ref(&mut lhs.1, &rhs.1);
244 self.reduce((&mut lhs.0, &mut lhs.1));
245 }
246 }
247
248 fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
249 self.mul_assign_raw(lhs, (&rhs.0, &rhs.1))
250 }
251
252 fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
253 self.integers.mul_assign(&mut lhs.0, rhs.0);
254 self.integers.mul_assign(&mut lhs.1, rhs.1);
255 self.reduce((&mut lhs.0, &mut lhs.1));
256 }
257
258 fn negate_inplace(&self, lhs: &mut Self::Element) {
259 self.integers.negate_inplace(&mut lhs.0);
260 }
261
262 fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
263 self.integers.eq_el(&self.integers.mul_ref(&lhs.0, &rhs.1), &self.integers.mul_ref(&lhs.1, &rhs.0))
264 }
265
266 fn is_zero(&self, value: &Self::Element) -> bool {
267 self.integers.is_zero(&value.0)
268 }
269
270 fn is_one(&self, value: &Self::Element) -> bool {
271 self.integers.eq_el(&value.0, &value.1)
272 }
273
274 fn is_neg_one(&self, value: &Self::Element) -> bool {
275 self.integers.eq_el(&value.0, &self.integers.negate(self.integers.clone_el(&value.1)))
276 }
277
278 fn is_approximate(&self) -> bool {
279 false
280 }
281
282 fn is_commutative(&self) -> bool {
283 true
284 }
285
286 fn is_noetherian(&self) -> bool {
287 true
288 }
289
290 fn characteristic<J: RingStore + Copy>(&self, ZZ: J) -> Option<El<J>>
291 where J::Type: IntegerRing
292 {
293 Some(ZZ.zero())
294 }
295
296 fn dbg_within<'a>(&self, value: &Self::Element, out: &mut std::fmt::Formatter<'a>, env: EnvBindingStrength) -> std::fmt::Result {
297 if self.base_ring().is_one(&value.1) {
298 write!(out, "{}", self.integers.format(&value.0))
299 } else {
300 if env > EnvBindingStrength::Product {
301 write!(out, "({}/{})", self.integers.format(&value.0), self.integers.format(&value.1))
302 } else {
303 write!(out, "{}/{}", self.integers.format(&value.0), self.integers.format(&value.1))
304 }
305 }
306 }
307
308 fn from_int(&self, value: i32) -> Self::Element {
309 RationalFieldEl(self.integers.get_ring().from_int(value), self.integers.one())
310 }
311}
312
313impl<I: RingStore> StrassenHint for RationalFieldBase<I>
314 where I::Type: IntegerRing
315{
316 default fn strassen_threshold(&self) -> usize {
317 usize::MAX
318 }
319}
320
321impl<I: RingStore> KaratsubaHint for RationalFieldBase<I>
322 where I::Type: IntegerRing
323{
324 default fn karatsuba_threshold(&self) -> usize {
325 usize::MAX
326 }
327}
328
329impl<I> RingExtension for RationalFieldBase<I>
330 where I: RingStore,
331 I::Type: IntegerRing
332{
333 type BaseRing = I;
334
335 fn base_ring<'a>(&'a self) -> &'a Self::BaseRing {
336 &self.integers
337 }
338
339 fn from(&self, x: El<Self::BaseRing>) -> Self::Element {
340 RationalFieldEl(x, self.integers.one())
341 }
342
343 fn mul_assign_base(&self, lhs: &mut Self::Element, rhs: &El<Self::BaseRing>) {
344 self.integers.mul_assign_ref(&mut lhs.0, rhs);
345 self.reduce((&mut lhs.0, &mut lhs.1));
346 }
347}
348
349impl<I, J> CanHomFrom<RationalFieldBase<J>> for RationalFieldBase<I>
350 where I: RingStore,
351 I::Type: IntegerRing,
352 J: RingStore,
353 J::Type: IntegerRing
354{
355 type Homomorphism = ();
356
357 fn has_canonical_hom(&self, _from: &RationalFieldBase<J>) -> Option<Self::Homomorphism> {
358 Some(())
359 }
360
361 fn map_in(&self, from: &RationalFieldBase<J>, el: <RationalFieldBase<J> as RingBase>::Element, (): &Self::Homomorphism) -> Self::Element {
362 RationalFieldEl(int_cast(el.0, self.base_ring(), from.base_ring()), int_cast(el.1, self.base_ring(), from.base_ring()))
363 }
364}
365
366impl<I, J> CanIsoFromTo<RationalFieldBase<J>> for RationalFieldBase<I>
367 where I: RingStore,
368 I::Type: IntegerRing,
369 J: RingStore,
370 J::Type: IntegerRing
371{
372 type Isomorphism = ();
373
374 fn has_canonical_iso(&self, _from: &RationalFieldBase<J>) -> Option<Self::Isomorphism> {
375 Some(())
376 }
377
378 fn map_out(&self, from: &RationalFieldBase<J>, el: Self::Element, (): &Self::Homomorphism) -> <RationalFieldBase<J> as RingBase>::Element {
379 RationalFieldEl(int_cast(el.0, from.base_ring(), self.base_ring()), int_cast(el.1, from.base_ring(), self.base_ring()))
380 }
381}
382
383impl<I, J> CanHomFrom<J> for RationalFieldBase<I>
384 where I: RingStore,
385 I::Type: IntegerRing,
386 J: IntegerRing
387{
388 type Homomorphism = ();
389
390 fn has_canonical_hom(&self, _from: &J) -> Option<Self::Homomorphism> {
391 Some(())
392 }
393
394 fn map_in(&self, from: &J, el: <J as RingBase>::Element, (): &Self::Homomorphism) -> Self::Element {
395 RationalFieldEl(int_cast(el, self.base_ring(), &RingRef::new(from)), self.integers.one())
396 }
397}
398
399impl<I> DivisibilityRing for RationalFieldBase<I>
400 where I: RingStore,
401 I::Type: IntegerRing
402{
403 fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
404 if self.is_zero(lhs) && self.is_zero(rhs) {
405 Some(self.zero())
406 } else if self.is_zero(rhs) {
407 None
408 } else {
409 let mut result = self.clone_el(lhs);
410 self.mul_assign_raw(&mut result, (&rhs.1, &rhs.0));
411 Some(result)
412 }
413 }
414
415 fn is_unit(&self, x: &Self::Element) -> bool {
416 !self.is_zero(x)
417 }
418
419 fn balance_factor<'a, J>(&self, elements: J) -> Option<Self::Element>
420 where J: Iterator<Item = &'a Self::Element>,
421 Self:'a
422 {
423 let (num, den) = elements.fold(
424 (self.integers.zero(), self.integers.one()),
425 |x, y| (signed_gcd(x.0, self.base_ring().clone_el(self.num(y)), self.base_ring()), signed_lcm(x.1, self.base_ring().clone_el(self.den(y)), self.base_ring())));
426 return Some(RationalFieldEl(num, den));
427 }
428}
429
430impl_interpolation_base_ring_char_zero!{ <{I}> InterpolationBaseRing for RationalFieldBase<I> where I: RingStore, I::Type: IntegerRing }
431
432impl<I> PrincipalIdealRing for RationalFieldBase<I>
433 where I: RingStore,
434 I::Type: IntegerRing
435{
436 fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
437 if self.is_zero(lhs) && self.is_zero(rhs) {
438 return Some(self.one());
439 }
440 self.checked_left_div(lhs, rhs)
441 }
442
443 fn extended_ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element, Self::Element) {
444 if self.is_zero(lhs) && self.is_zero(rhs) {
445 return (self.zero(), self.zero(), self.zero());
446 } else if self.is_zero(lhs) {
447 return (self.zero(), self.one(), self.clone_el(rhs));
448 } else {
449 return (self.one(), self.zero(), self.clone_el(lhs));
450 }
451 }
452}
453
454impl<I> EuclideanRing for RationalFieldBase<I>
455 where I: RingStore,
456 I::Type: IntegerRing
457{
458 fn euclidean_deg(&self, val: &Self::Element) -> Option<usize> {
459 if self.is_zero(val) { Some(0) } else { Some(1) }
460 }
461
462 fn euclidean_div_rem(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
463 assert!(!self.is_zero(rhs));
464 (self.checked_left_div(&lhs, rhs).unwrap(), self.zero())
465 }
466}
467
468impl<I> Domain for RationalFieldBase<I>
469 where I: RingStore,
470 I::Type: IntegerRing
471{}
472
473impl<I> PerfectField for RationalFieldBase<I>
474 where I: RingStore,
475 I::Type: IntegerRing
476{}
477
478impl<I> Field for RationalFieldBase<I>
479 where I: RingStore,
480 I::Type: IntegerRing
481{}
482
483impl<I> OrderedRing for RationalFieldBase<I>
484 where I: RingStore,
485 I::Type: IntegerRing
486{
487 fn cmp(&self, lhs: &Self::Element, rhs: &Self::Element) -> std::cmp::Ordering {
488 assert!(self.integers.is_pos(&lhs.1) && self.integers.is_pos(&rhs.1));
489 self.integers.cmp(&self.integers.mul_ref(&lhs.0, &rhs.1), &self.integers.mul_ref(&rhs.0, &lhs.1))
490 }
491}
492
493impl<I> PolyTFracGCDRing for RationalFieldBase<I>
494 where I: RingStore,
495 I::Type: IntegerRing
496{
497 fn power_decomposition<P>(poly_ring: P, poly: &El<P>) -> Vec<(El<P>, usize)>
498 where P: RingStore + Copy,
499 P::Type: PolyRing,
500 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
501 {
502 assert!(!poly_ring.is_zero(poly));
503 let QQX = &poly_ring;
504 let QQ = QQX.base_ring();
505 let ZZ = QQ.base_ring();
506
507 let den_lcm = QQX.terms(poly).map(|(c, _)| QQ.get_ring().den(c)).fold(ZZ.one(), |a, b| signed_lcm(a, ZZ.clone_el(b), ZZ));
508
509 let ZZX = DensePolyRing::new(ZZ, "X");
510 let f = ZZX.from_terms(QQX.terms(poly).map(|(c, i)| (ZZ.checked_div(&ZZ.mul_ref(&den_lcm, QQ.get_ring().num(c)), QQ.get_ring().den(c)).unwrap(), i)));
511 let power_decomp = poly_power_decomposition_local(&ZZX, f, DontObserve);
512 let ZZX_to_QQX = QQX.lifted_hom(&ZZX, QQ.inclusion());
513
514 return power_decomp.into_iter().map(|(f, k)| (QQX.normalize(ZZX_to_QQX.map(f)), k)).collect();
515 }
516
517 fn gcd<P>(poly_ring: P, lhs: &El<P>, rhs: &El<P>) -> El<P>
518 where P: RingStore + Copy,
519 P::Type: PolyRing,
520 <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
521 {
522 if poly_ring.is_zero(lhs) {
523 return poly_ring.clone_el(rhs);
524 } else if poly_ring.is_zero(rhs) {
525 return poly_ring.clone_el(lhs);
526 }
527 let QQX = &poly_ring;
528 let QQ = QQX.base_ring();
529 let ZZ = QQ.base_ring();
530
531 let den_lcm_lhs = QQX.terms(lhs).map(|(c, _)| QQ.get_ring().den(c)).fold(ZZ.one(), |a, b| signed_lcm(a, ZZ.clone_el(b), ZZ));
532 let den_lcm_rhs = QQX.terms(rhs).map(|(c, _)| QQ.get_ring().den(c)).fold(ZZ.one(), |a, b| signed_lcm(a, ZZ.clone_el(b), ZZ));
533
534 let ZZX = DensePolyRing::new(ZZ, "X");
535 let lhs = ZZX.from_terms(QQX.terms(lhs).map(|(c, i)| (ZZ.checked_div(&ZZ.mul_ref(&den_lcm_lhs, QQ.get_ring().num(c)), QQ.get_ring().den(c)).unwrap(), i)));
536 let rhs = ZZX.from_terms(QQX.terms(rhs).map(|(c, i)| (ZZ.checked_div(&ZZ.mul_ref(&den_lcm_rhs, QQ.get_ring().num(c)), QQ.get_ring().den(c)).unwrap(), i)));
537 let result = poly_gcd_local(&ZZX, lhs, rhs, DontObserve);
538 let ZZX_to_QQX = QQX.lifted_hom(&ZZX, QQ.inclusion());
539
540 return QQX.normalize(ZZX_to_QQX.map(result));
541 }
542}
543
544#[cfg(test)]
545use crate::primitive_int::StaticRing;
546#[cfg(test)]
547use crate::homomorphism::Homomorphism;
548
549use super::poly::PolyRing;
550
551#[cfg(test)]
552fn edge_case_elements() -> impl Iterator<Item = El<RationalField<StaticRing<i64>>>> {
553 let ring = RationalField::new(StaticRing::<i64>::RING);
554 let incl = ring.into_int_hom();
555 (-6..8).flat_map(move |x| (-2..5).filter(|y| *y != 0).map(move |y| ring.checked_div(&incl.map(x), &incl.map(y)).unwrap()))
556}
557
558#[test]
559fn test_ring_axioms() {
560 let ring = RationalField::new(StaticRing::<i64>::RING);
561
562 let half = ring.checked_div(&ring.int_hom().map(1), &ring.int_hom().map(2)).unwrap();
563 assert!(!ring.is_one(&half));
564 assert!(!ring.is_zero(&half));
565 assert_el_eq!(ring, ring.one(), ring.add_ref(&half, &half));
566 crate::ring::generic_tests::test_ring_axioms(ring, edge_case_elements());
567}
568
569#[test]
570fn test_divisibility_axioms() {
571 let ring = RationalField::new(StaticRing::<i64>::RING);
572 crate::divisibility::generic_tests::test_divisibility_axioms(ring, edge_case_elements());
573}
574
575#[test]
576fn test_principal_ideal_ring_axioms() {
577 let ring = RationalField::new(StaticRing::<i64>::RING);
578 crate::pid::generic_tests::test_euclidean_ring_axioms(ring, edge_case_elements());
579 crate::pid::generic_tests::test_principal_ideal_ring_axioms(ring, edge_case_elements());
580}
581
582#[test]
583fn test_int_hom_axioms() {
584 let ring = RationalField::new(StaticRing::<i64>::RING);
585 crate::ring::generic_tests::test_hom_axioms(&StaticRing::<i64>::RING, ring, -16..15);
586}