1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use crate::math::compare::s_compare;
use crate::math::sub::nz_sub;
use crate::types::builder::Builder;
use crate::types::scientific::Scientific;
use crate::types::sign::Sign;
use crate::types::trimmer::Trimmer;
use core::cmp::Ordering;
use core::mem::swap;
use core::ops::Add;

impl Add for &Scientific {
  type Output = Scientific;

  #[inline(always)]
  fn add(self, rhs: Self) -> Self::Output {
    export_add(self, rhs)
  }
}

fn export_add(lhs: &Scientific, rhs: &Scientific) -> Scientific {
  if lhs.is_zero() {
    return rhs.clone();
  } else if rhs.is_zero() {
    return lhs.clone();
  }
  if lhs.sign == rhs.sign {
    nz_add(lhs, rhs, lhs.sign)
  } else {
    match s_compare::<false>(lhs, rhs) {
      Ordering::Less => nz_sub(rhs, lhs, rhs.sign),
      Ordering::Equal => Scientific::ZERO,
      Ordering::Greater => nz_sub(lhs, rhs, lhs.sign),
    }
  }
}

pub(crate) fn nz_add<'a>(
  mut lhs: &'a Scientific,
  mut rhs: &'a Scientific,
  sign: Sign,
) -> Scientific {
  let mut lhs_exponent0 = lhs.exponent0();
  let mut rhs_exponent0 = rhs.exponent0();

  if lhs_exponent0 < rhs_exponent0 {
    swap(&mut lhs, &mut rhs);
    swap(&mut lhs_exponent0, &mut rhs_exponent0);
  }

  let min_exponent = lhs.exponent.min(rhs.exponent);
  let result_len = 1 + (lhs_exponent0.max(rhs_exponent0) - min_exponent);

  let (result, mut result_ptr) = Builder::new(sign, result_len, min_exponent);

  let mut carry = 0;

  lhs.data.copy_to_nonoverlapping(lhs.len, result_ptr, 1);
  result_ptr.mut_offset(result_len - (rhs.exponent - min_exponent));
  let mut rhs_ptr = rhs.data.offset(rhs.len);
  while rhs.data < rhs_ptr {
    rhs_ptr.dec();
    result_ptr.dec();
    let mut value = *result_ptr + *rhs_ptr + carry;
    if value >= 10 {
      value -= 10;
      carry = 1;
    } else {
      carry = 0;
    }
    *result_ptr = value;
  }

  while carry != 0 {
    result_ptr.dec();
    let mut value = *result_ptr + carry;
    if value >= 10 {
      value -= 10;
      carry = 1;
    } else {
      carry = 0;
    }
    *result_ptr = value;
  }

  result.finish(Trimmer::Basic)
}