public eeListNode(eeObject obj, eeNumber index, eeListNode prev, eeListNode next) { this.obj = obj; this.index = index; this.prev = prev; this.next = next; }
private eeNumber IntegerDivision(eeNumber divisor, out eeNumber mod) { // base case if (this.AbsoluteValue() < divisor.AbsoluteValue()) { mod = this; return(new eeNumber(0)); } Queue <byte> bytesQue = new Queue <byte>(this.bytes); // a queue of the dividend as bytes List <eeNumber> quotient = new List <eeNumber>(); // quotient eeNumber divPart = null; while (bytesQue.Count() > 0) { /* Grab the next digit from dividend */ var asList = divPart == null ? new List <byte>() : divPart.bytes.ToList(); var nextByte = bytesQue.Dequeue(); asList.Add(nextByte); /* Keep grabbing until we run out or need more */ divPart = new eeNumber(asList); while (bytesQue.Count() != 0 && divPart < divisor) { quotient.Add(new eeNumber(0)); asList.Add(bytesQue.Dequeue()); divPart = new eeNumber(asList); divPart.TrimZeros(); } divPart.TrimZeros(); /* Divide it and append the division to the quotient */ eeNumber q = new eeNumber(1); // number of times divisor fits into this divPart while (divisor * q <= divPart) { q += ONE; } q -= ONE; quotient.Add(q); // keep track of quotient /* Figure out the remainder*/ divPart = divPart - (divisor * q); } // remainder mod = divPart; var intergerQuotient = new eeNumber(quotient); intergerQuotient.TrimZeros(); return(intergerQuotient); }
public eeObject this[eeNumber key] { get // retrieve { return(null); } set // assign { return; } }
// removes the denominator of this number and returns it private eeNumber PopDenominator() { // if not a fraction if (this.IsInt()) { return(new eeNumber(1)); } var den = this.denominator; this.denominator = null; return(den); }
private static eeNumber FromBinary(bool[] bin) { var num = new eeNumber(0); for (int idx = 0; idx < bin.Length; idx++) { if (bin[idx]) { num += new eeNumber((ulong)Math.Pow(2, bin.Length - idx - 1)); } } return(num); }
// returns a copy of this eeNumber public eeNumber Copy() { var copy = new eeNumber(0); copy.negative = this.negative; copy.bytes = (byte[])this.bytes.Clone(); if (this.IsFrac()) { copy.denominator = new eeNumber(0); copy.denominator.bytes = (byte[])this.denominator.bytes.Clone(); } return(copy); }
public static eeNumber RShift(eeNumber num1, eeNumber num2) { if (num1.IsFrac() || num2.IsFrac()) { throw new Exception("Cannot shift a non-int or by a non-int"); } List <bool> bin1 = num1.ToBinary(); for (eeNumber i = ZERO.Copy(); i < num2; i += ONE) { bin1.RemoveAt(bin1.Count() - 1); } return(FromBinary(bin1.ToArray())); }
public void Append(eeObject obj) { _count += eeNumber.ONE; if (LastNode == null) // if this is the second object to enter the list { var node = new eeListNode(obj.Copy(), _count.Copy(), HeadNode, null); LastNode = node; HeadNode.next = node; return; } var newNode = new eeListNode(obj.Copy(), _count.Copy(), LastNode, null); this.LastNode.next = newNode; this.LastNode = newNode; }
/* https://stackoverflow.com/a/11419400/9628054 */ public static eeNumber Power(eeNumber base_, eeNumber exp) { var y = ONE.Copy(); while (true) { if ((exp & ONE) != ZERO) { y *= base_; } exp >>= 1; if (exp == ZERO) { return(y); } base_ *= base_; } }
// Greatest Common Factor using Euclid's algorithm public eeNumber GCF(eeNumber prod2_orig) { var prod1 = this.Copy().AbsoluteValue(); var prod2 = prod2_orig.Copy().AbsoluteValue(); while (prod1 != ZERO && prod2 != ZERO) { if (prod1 > prod2) { prod1 %= prod2; } else { prod2 %= prod1; } } return(prod1 | prod2); }
private string ApproximateDivision(long accurateTo) { if (this.denominator == null) { throw new Exception("Cannot approximate division on an integer"); } eeNumber denom = this.PopDenominator(), remainder, integerQuotient = this.IntegerDivision(denom, out remainder); string approx = integerQuotient.NumeratorToString(); if (remainder == ZERO) { return(approx); } const string ZEROSTR = "0"; string decimalAprx = ""; // If the dividend is longer than the max accuracy, iterate to that, otherwise, to max accuracy long c = denom.bytes.LongCount(); if (c > accurateTo) { accurateTo += c; } for (int i = 1; i <= accurateTo; i++) { eeNumber dec = new eeNumber( remainder.NumeratorToString() + ZEROSTR ); decimalAprx += dec.IntegerDivision(denom, out eeNumber rem).NumeratorToString(); remainder = rem; } decimalAprx = decimalAprx.TrimEnd('0'); this.denominator = denom; return($"{approx}.{decimalAprx}"); }
public eeNumber Factorial() { if (this < ZERO) { throw new Exception(); } else if (this == ZERO) { return(ONE.Copy()); } var sum = ONE.Copy(); for (eeNumber i = this.Copy(); i > ZERO; i -= ONE) { sum *= i; } return(sum); }
public static eeNumber LShift(eeNumber num1, eeNumber num2) { if (num1.IsFrac() || num2.IsFrac()) { throw new Exception("Cannot shift a non-int or by a non-int"); } List <bool> bin1 = num1.ToBinary(); // trim the tail //for (eeNumber i = ZERO.Copy(); i < num2; i += ONE) // bin1.RemoveAt(bin1.Count() - 1); // add to the front for (eeNumber i = ZERO.Copy(); i < num2; i += ONE) { bin1.Add(false); } return(FromBinary(bin1.ToArray())); }
/* Note: * We cannot use +, - (non-integer), or / operators in this function or it will cause an infinite recursion loop * We use the IntergerDivision function as a replacement for / */ private void Simplify() { if (this.IsInt()) { return; } var denom = PopDenominator(); if (this == ZERO || denom == ONE) // if the numerator is zero or denom is 1, return as-is (without denom) { return; } // if the denominator is a power of 10, leave it as is, as it's easier to deal with. else if (denom.IsPowerOf10()) { this.denominator = denom; return; } // use the GCF to simplify eeNumber gcf = this.GCF(denom); if (gcf == ONE) // no change needed { this.denominator = denom; } else { var newObj = this.IntegerDivision(gcf, out _); this.bytes = newObj.bytes; if (this.denominator != gcf) // if denom won't be 1 { this.denominator = denom.IntegerDivision(gcf, out _); } } }
public eeNumber(string num) { if (num.StartsWith("-")) // If it's negative { negative = true; num = num.Replace("-", ""); } if (num.Contains('.')) // if it's a decimal/double { var split = num.Split('.'); // seperate integer and decimal // set the denominator denominator = new eeNumber("1" + new string('0', split[1].Length)); var fracNumerator = new eeNumber(split[1]); var intPart = new eeNumber(split[0]); // continue for the numerator normally num = ((intPart * denominator) + fracNumerator).NumeratorToString(); } bytes = num.ToCharArray().Select(x => byte.Parse(x.ToString())).ToArray(); }
// Warning: Subtraction will destroy the contents of num2 and set the contents of num1 to the result // Make sure to pass a copy of num2 if you are keeping its value public static eeNumber operator -(eeNumber num1_orig, eeNumber num2_orig) { eeNumber num1 = num1_orig.Copy(), num2 = num2_orig.Copy(); // If either number is a fraction, cross multiply if (num1.IsFrac() || num2.IsFrac()) { eeNumber frac1 = num1.PopDenominator(), frac2 = num2.PopDenominator(); num1 *= frac2; num2 *= frac1; var ret = (num1 - num2) / (frac1 * frac2); ret.TrimZeros(); ret.Simplify(); return(ret); } // if the two nums are the same if (num1 == num2) { return(ZERO.Copy()); } // if first num is negative else if (num1.negative && !num2.negative) { // add them then negate it num1.negative = false; var sum = num1 + num2; sum.negative = true; return(sum); } // if second num is negative else if (num2.negative && !num1.negative) { // minus and negatives cancel num2.negative = false; return(num1 + num2); } // If they're both negative else if (num1.negative && num2.negative) { // Treat this as adding a negative to a positive num2.negative = false; return(num1 + num2); } // If we're going to get a negative answer bool negate = false; if (num2 > num1) { // reverse the two var buf = num1; num1 = num2; num2 = buf; // and negate negate = true; } // Reverse because we're subtracting from right to left. byte[] l_bytes = num1.bytes.Reverse().ToArray(), r_bytes = num2.bytes.Reverse().ToArray(); bool carry = false; int i; for (i = 0; i < r_bytes.Length; i++) { // Account for the previous carry if (carry) { r_bytes[i]++; carry = false; } // Subtract the digits byte digitDiff; if (l_bytes[i] < r_bytes[i]) // account for carry { digitDiff = (byte)((10 + l_bytes[i]) - r_bytes[i]); carry = true; } else { digitDiff = (byte)(l_bytes[i] - r_bytes[i]); } // Set the digit l_bytes[i] = digitDiff; } // If there is still carry left while (carry) { // subtract one from the next value if (l_bytes[i] == 0) { l_bytes[i] = 9; i++; } else { l_bytes[i] -= 1; carry = false; } } // Put it in order again l_bytes = l_bytes.Reverse().ToArray(); num1.bytes = l_bytes; num1.negative = negate; num1.TrimZeros(); return(num1); }
public static eeNumber operator +(eeNumber num1, eeNumber num2) { // If either number is a fraction, cross multiply if (num1.IsFrac() || num2.IsFrac()) { eeNumber frac1 = num1.PopDenominator(), frac2 = num2.PopDenominator(); num1 *= frac2; num2 *= frac1; var ret = (num1 + num2) / (frac1 * frac2); ret.TrimZeros(); ret.Simplify(); return(ret); } bool negate = false; // if first num is negative if (num1.negative && !num2.negative) { num1.negative = false; return(num2 - num1.Copy()); } // If 2nd num is negative else if (num2.negative && !num1.negative) { num2.negative = false; return(num1 - num2.Copy()); } // They're both negative else if (num1.negative && num2.negative) { num1.negative = false; num2.negative = false; negate = true; } if (num2.bytes.Length > num1.bytes.Length) { // switch so that num1 is larger var buf = num1; num1 = num2; num2 = buf; } // Reverse because we're adding from right to left. byte[] lhs = num1.bytes.Reverse().ToArray(), rhs = num2.bytes.Reverse().ToArray(); int i; for (i = 0; i < rhs.Length; i++) { byte digitSum = (byte)(rhs[i] + lhs[i]); lhs[i] = digitSum; } num1.bytes = lhs.Reverse().ToArray(); num1.CarryOver(); num1.negative = negate; return(num1); }
public static eeNumber operator *(eeNumber num1_orig, eeNumber num2_orig) { eeNumber num1 = num1_orig.Copy(), num2 = num2_orig.Copy(); // if either num is 0, return 0 if (num1 == ZERO || num2 == ZERO) { return(new eeNumber(0)); } bool negate = false; /* first account for negatives */ // if either num is negative if (num1.negative ^ num2.negative) { // set both to non-negative to multiply them num1.negative = false; num2.negative = false; // apply negative afterwards negate = true; } else if (num1.negative && num2.negative) { // otherwise, negatives just cancel out num1.negative = false; num2.negative = false; } eeNumber final; // if only one num is a fraction if (num1.IsFrac() ^ num2.IsFrac()) { eeNumber frac = num1.denominator != null ? num1 : num2, reg = num1.denominator != null ? num2 : num1; var den = frac.PopDenominator(); var newNum = (frac * reg) / den; final = newNum; } // if both nums are fractions else if (num1.IsFrac() && num2.IsFrac()) { eeNumber frac1 = num1.PopDenominator(), frac2 = num2.PopDenominator(); final = (num1 * num2) / (frac1 * frac2); } // regular nums else { // Reverse because we're going from right to left. byte[] lhs = num1.bytes.Reverse().ToArray(), rhs = num2.bytes.Reverse().ToArray(); // new digit eeNumber finalNum = new eeNumber(0); // foreach digit in on the top num for (int i = 0; i < rhs.Length; i++) { byte carry = 0; List <byte> product = new List <byte>(); // foreach digit in the bottom num for (int j = 0; j < lhs.Length; j++) { // multiply them byte digitProduct = (byte)(lhs[j] * rhs[i]); if (carry != 0) { digitProduct += carry; carry = 0; } if (digitProduct > 9) { carry = (byte)(digitProduct / 10); digitProduct %= 10; } product.Add(digitProduct); } // If carry is left if (carry != 0) { product.Add(carry); } product.Reverse(); // Add the needed amount of trailing zeros for place value for (int z = 0; z < i; z++) { product.Add(0); } finalNum += new eeNumber(product.ToArray()); } final = finalNum; } final.negative = negate; return(final); }
public static eeNumber operator /(eeNumber num1_orig, eeNumber num2_orig) { /* eeNumbers do not perform traditional division. That is only done * when the number needs to be approximated for a text representation. * For divison, we always keep the fractional form for arbitrary accuracy. */ eeNumber num1 = num1_orig.Copy(), num2 = num2_orig.Copy(); if (num2 == ZERO) { throw new DivisionByZeroError(num1); } bool negate = false; /* first, account for negatives */ // if either num is negative if (num1.negative ^ num2.negative) { // set both to non-negative to multiply them num1.negative = false; num2.negative = false; // apply negative afterwards negate = true; } else if (num1.negative && num2.negative) { // otherwise, negatives just cancel out num1.negative = false; num2.negative = false; } /* then, divide */ bool a = num1.IsFrac(), b = num2.IsFrac(); // frac divided by num if (a && !b) { num1.denominator *= num2; num1.negative = negate; num1.Simplify(); return(num1); } // num divided by num else if (!a && !b) { // check if right is a factor of left if (num1 > num2) { var m = num1 % num2; if (m == ZERO) { return(num1 % num2); } } num1.denominator = num2; num1.negative = negate; num1.Simplify(); return(num1); } // num divided by frac else if (!a && b) { eeNumber numerator = num1 * num2.denominator; eeNumber denominator = num2; var newNum = numerator / denominator; newNum.negative = negate; return(newNum); } // frac divided by frac else { var denom1 = num1.PopDenominator(); eeNumber numerator = num1 * num2.PopDenominator(); eeNumber denominator = num2 * denom1; var newNum = numerator / denominator; newNum.negative = negate; return(newNum); } }
public static bool operator >(eeNumber num1, eeNumber num2) { bool flip = false; // if at least one number is a fraction if (num1.IsFrac() || num2.IsFrac()) { eeNumber numerator1 = num1, denom1 = num1.PopDenominator(), numerator2 = num2, denom2 = num2.PopDenominator(); // cross multiply & compare return((numerator1 * denom2) > (numerator2 * denom1)); } // lhs is neg and rhs isn't else if (num1.negative && !num2.negative) { return(false); } // lhs isn't neg and rhs is else if (!num1.negative && num2.negative) { return(true); } // both negative else if (num1.negative && num2.negative) { /* Copy the numbers, compare the positive ones in the inverse order. * Simply flipping the comparison without altering the numbers creates an infinite loop * and removing the negative from the original object could cause damage higher up. */ var num1_copy = num1.Copy(); num1_copy.negative = false; var num2_copy = num2.Copy(); num2_copy.negative = false; return(num2_copy > num1_copy); } // if both are whole numbers, compare digits normally. byte[] l_bytes = num1.bytes, r_bytes = num2.bytes; // If lhs has more digits if (l_bytes.Length > r_bytes.Length) { return(true); } // If rhs has more digits else if (l_bytes.Length < r_bytes.Length) { return(false); } else // if they have an equal number of digits { // iterate through each digit for (int i = 0; i < l_bytes.Length; i++) { // If the lhs digit is greater, return true if (l_bytes[i] > r_bytes[i]) { return(true); } // If they're the same, move on to the next digit else if (l_bytes[i] == r_bytes[i]) { continue; } else // if lhs is smaller, return false { return(false); } } } // they're equal return(false); }