def common_denominator_lcm( lhs: FractionNumber, rhs: FractionNumber) -> (FractionNumber, FractionNumber): # Sign is only kept on the numerator, not the denominator log(f'Getting {lhs} and {rhs} to common denominator equivalent fractions' ) if lhs._denominator != rhs._denominator: log_indent() log(f'Calculating common denominator...') common_denominator = lcm_prime_factorize( rhs._denominator.magnitude, lhs._denominator.magnitude) common_denominator = IntegerNumber(Sign.POSITIVE, common_denominator) log(f'{common_denominator}') log(f'Calculating numerator for LHS ({lhs})...') multiple, _ = common_denominator / lhs._denominator lhs_numerator = lhs._numerator * multiple log(f'{lhs_numerator}') log(f'Calculating numerator for RHS ({rhs})...') multiple, _ = common_denominator / rhs._denominator rhs_numerator = rhs._numerator * multiple log(f'{rhs_numerator}') log_unindent() lhs_equiv = FractionNumber(lhs_numerator, common_denominator) rhs_equiv = FractionNumber(rhs_numerator, common_denominator) log(f'Result: {lhs} -> {lhs_equiv}, {rhs} -> {rhs_equiv}') return lhs_equiv, rhs_equiv else: log(f'Fractions already have a common denominator') return lhs, rhs
def common_denominator_naive( lhs: FractionNumber, rhs: FractionNumber) -> (FractionNumber, FractionNumber): # Sign is only kept on the numerator, not the denominator log(f'Getting {lhs} and {rhs} to common denominator equivalent fractions' ) if lhs._denominator != rhs._denominator: log_indent() log(f'Calculating common denominator...') common_denominator = rhs._denominator * lhs._denominator log(f'{common_denominator}') log(f'Calculating numerator for LHS ({lhs})...') lhs_numerator = lhs._numerator * rhs._denominator log(f'{lhs_numerator}') log(f'Calculating numerator for RHS ({rhs})...') rhs_numerator = rhs._numerator * lhs._denominator log(f'{rhs_numerator}') log_unindent() lhs_equiv = FractionNumber(lhs_numerator, common_denominator) rhs_equiv = FractionNumber(rhs_numerator, common_denominator) log(f'Result: {lhs} -> {lhs_equiv}, {rhs} -> {rhs_equiv}') return lhs_equiv, rhs_equiv else: log(f'Fractions already have a common denominator') return lhs, rhs
def __eq__(lhs: DecimalNumber, rhs: DecimalNumber) -> bool: class FailedTestException(Exception): pass log(f'Equality testing {lhs} and {rhs}...') log_indent() try: log(f'Testing sign...') sign_eq = lhs.sign == rhs.sign if not sign_eq: raise FailedTestException() log(f'Equal') log(f'Testing whole...') whole_eq = lhs.whole == rhs.whole if not whole_eq: raise FailedTestException() log(f'Equal') log(f'Testing fractional...') fractional_eq = lhs.fractional == rhs.fractional if not fractional_eq: raise FailedTestException() log(f'Equal') ret = True except FailedTestException: log(f'Not equal') ret = False log_unindent() log(f'{ret}') return ret
def __mul__(lhs: IntegerNumber, rhs: IntegerNumber) -> IntegerNumber: log(f'Multiplying {lhs} and {rhs}') log_indent() def determine_sign(magnitude: WholeNumber, default_sign: Sign) -> Sign: if magnitude == WholeNumber.from_int(0): return None else: return default_sign if lhs.sign is None: # when sign isn't set, magnitude is always 0 -- 0 * a = 0 sign = None magnitude = WholeNumber.from_int(0) elif rhs.sign is None: # when sign isn't set, magnitude is always 0 -- a * 0 = 0 sign = None magnitude = WholeNumber.from_int(0) elif (lhs.sign == Sign.POSITIVE and rhs.sign == Sign.POSITIVE) \ or (lhs.sign == Sign.NEGATIVE and rhs.sign == Sign.NEGATIVE): magnitude = lhs.magnitude * rhs.magnitude sign = determine_sign(magnitude, Sign.POSITIVE) elif (lhs.sign == Sign.POSITIVE and rhs.sign == Sign.NEGATIVE) \ or (lhs.sign == Sign.NEGATIVE and rhs.sign == Sign.POSITIVE): magnitude = lhs.magnitude * rhs.magnitude sign = determine_sign(magnitude, Sign.NEGATIVE) log_unindent() log(f'sign: {sign}, magnitude: {magnitude}') return IntegerNumber(sign, magnitude)
def __add__(lhs: IntegerNumber, rhs: IntegerNumber) -> IntegerNumber: log(f'Adding {lhs} and {rhs}') log_indent() def determine_sign(magnitude: WholeNumber, default_sign: Sign) -> Sign: if magnitude == WholeNumber.from_int(0): return None else: return default_sign if lhs.sign is None: # sign of None is only when magnitude is 0, 0 + a = a sign = rhs.sign magnitude = rhs.magnitude elif rhs.sign is None: # sign of None is only when magnitude is 0, a + 0 = a sign = lhs.sign magnitude = lhs.magnitude elif lhs.sign == rhs.sign: magnitude = lhs.magnitude + rhs.magnitude sign = determine_sign(magnitude, lhs.sign) elif lhs.sign != rhs.sign: if rhs.magnitude >= lhs.magnitude: magnitude = rhs.magnitude - lhs.magnitude sign = determine_sign(magnitude, rhs.sign) else: magnitude = lhs.magnitude - rhs.magnitude sign = determine_sign(magnitude, lhs.sign) log_unindent() log(f'sign: {sign}, magnitude: {magnitude}') return IntegerNumber(sign, magnitude)
def choose_start_test_num_for_divte( input1: DecimalNumber, expected_product: DecimalNumber) -> DecimalNumber: log(f'Choosing a starting number to find {input1} \\* ? = {expected_product}...' ) log_indent() log(f'Checking which case should apply...') if input1 < DecimalNumber.from_str( '1.0') and expected_product >= DecimalNumber.from_str('1.0'): log(f'{input1} has {len(input1.fractional.digits)} fractional digits' ) log(f'{expected_product}\'s has {len(expected_product.whole.digits)} whole digits' ) num_of_zeros = len(expected_product.whole.digits) + len( input1.fractional.digits) - 1 start_test_num = DecimalNumber.from_str('1' + '0' * num_of_zeros) else: log(f'{input1} has {len(input1.whole.digits)} whole digits') log(f'{expected_product}\'s has {len(expected_product.whole.digits)} whole digits' ) num_of_zeros = len(expected_product.whole.digits) - len( input1.whole.digits) start_test_num = DecimalNumber.from_str('1' + '0' * num_of_zeros) log(f'Starting number: {start_test_num}') log_unindent() log(f'{start_test_num}') return start_test_num
def __gt__(self: IntegerNumber, other: IntegerNumber) -> bool: log(f'Greater than testing {self} and {other}...') log_indent() self_sign = self.sign if self_sign is None: # assume 0 is a positive -- it simplifies logic below self_sign = Sign.POSITIVE other_sign = other.sign if other_sign is None: # assume 0 is a positive -- it simplifies logic below other_sign = Sign.POSITIVE if self_sign == Sign.POSITIVE and other_sign == Sign.POSITIVE: log(f'{self.sign} > {other.sign}: Applying whole number less than...') ret = self.magnitude > other.magnitude elif self_sign == Sign.NEGATIVE and other_sign == Sign.NEGATIVE: log(f'{self.sign} > {other.sign}: Turning positive and applying whole number greater than...') ret = self.magnitude < other.magnitude elif self_sign == Sign.POSITIVE and other_sign == Sign.NEGATIVE: log(f'{self.sign} > {other.sign}:: Different signs -- number being tested is positive...') ret = True elif self_sign == Sign.NEGATIVE and other_sign == Sign.POSITIVE: log(f'{self.sign} > {other.sign}: Different signs -- number being tested is negative...') ret = False log(f'{ret}') log_unindent() log(f'{ret}') return ret
def ladder(num: WholeNumber) -> Set[WholeNumber]: prime_factors: List[WholeNumber] = [] log(f'Testing primes (using ladder method) to see which is factor of {num}...' ) log_indent() while not is_prime(num): prime_to_test = WholeNumber.from_int(2) while True: log(f'Testing if {prime_to_test} is divisible by {num}...') (new_num, remainder) = num / prime_to_test if remainder == WholeNumber.from_int(0): break prime_to_test = calculate_next_prime(prime_to_test) log(f'Found! {prime_to_test} is a prime factor -- {new_num} * {prime_to_test} = {num}' ) prime_factors.append(prime_to_test) num = new_num log(f'Testing primes to see which is factor of {num}...') log(f'{num} itself is a prime!') prime_factors.append(num) log_unindent() log(f'Prime factors: {prime_factors}') return prime_factors
def __truediv__(lhs: IntegerNumber, rhs: IntegerNumber) -> (IntegerNumber, IntegerNumber): log(f'Dividing {lhs} and {rhs}') log_indent() def determine_sign(magnitude: WholeNumber, default_sign: Sign) -> Sign: if magnitude == WholeNumber.from_int(0): return None else: return default_sign if lhs.sign is None: # when sign isn't set, magnitude is always 0 -- 0 / a = 0 (quotient_magnitude, remainder_magnitude) = lhs.magnitude / rhs.magnitude quotient_sign = None remainder_sign = None elif rhs.sign is None: # when sign isn't set, magnitude is always 0 -- a / 0 = err raise Exception('Cannot divide by 0') elif (lhs.sign == Sign.POSITIVE and rhs.sign == Sign.POSITIVE) \ or (lhs.sign == Sign.NEGATIVE and rhs.sign == Sign.NEGATIVE): (quotient_magnitude, remainder_magnitude) = lhs.magnitude / rhs.magnitude quotient_sign = determine_sign(quotient_magnitude, Sign.POSITIVE) remainder_sign = determine_sign(remainder_magnitude, Sign.POSITIVE) elif (lhs.sign == Sign.POSITIVE and rhs.sign == Sign.NEGATIVE) \ or (lhs.sign == Sign.NEGATIVE and rhs.sign == Sign.POSITIVE): (quotient_magnitude, remainder_magnitude) = lhs.magnitude / rhs.magnitude quotient_sign = determine_sign(quotient_magnitude, Sign.NEGATIVE) remainder_sign = determine_sign(remainder_magnitude, Sign.NEGATIVE) log_unindent() log(f'QUOTIENT: sign: {quotient_sign}, magnitude: {quotient_magnitude}') log(f'REMAINDER: sign: {remainder_sign}, magnitude: {remainder_magnitude}') return IntegerNumber(quotient_sign, quotient_magnitude), IntegerNumber(remainder_sign, remainder_magnitude)
def simplify(self: FractionNumber) -> FractionNumber: # Sign is on the numerator log(f'Simplifying {self}...') log_indent() log(f'Calculating GCD for ({self._numerator.magnitude}) and ({self._denominator.magnitude})...' ) gcd = gcd_euclid(self._numerator.magnitude, self._denominator.magnitude) log(f'GCD is {gcd}') log(f'Dividing numerator ({self._numerator.magnitude}) by {gcd}...') new_num, _ = self._numerator.magnitude / gcd log(f'New numerator is {new_num}...') log(f'Dividing denominator ({self._denominator.magnitude}) by {gcd}...' ) new_den, _ = self._denominator.magnitude / gcd log(f'New numerator is {new_den}...') # Sign of fraction is on the numerator if self.sign == Sign.NEGATIVE: # if original was negative, so will the simplified res = FractionNumber(IntegerNumber(Sign.NEGATIVE, new_num), IntegerNumber(Sign.POSITIVE, new_den)) elif self.sign == Sign.POSITIVE: # if original was positive, so will the simplified res = FractionNumber(IntegerNumber(Sign.POSITIVE, new_num), IntegerNumber(Sign.POSITIVE, new_den)) else: # if original was 0 (no sign), so will the simplified res = FractionNumber(IntegerNumber(None, new_num), IntegerNumber(Sign.POSITIVE, new_den)) log_unindent() log(f'{self} simplified to: {res}') return res
def as_fraction(self: DecimalNumber) -> FractionNumber: log(f'Converting {self} to fraction number...') log_indent() log(f'Determining denominator based on length of fractional portion ({self.fractional})...' ) denom = IntegerNumber.from_str('1' + '0' * len(self.fractional.digits)) log(f'{denom}') log(f'Converting fractional portion ({self.fractional} to fraction...') fractional_fraction = FractionNumber( IntegerNumber.from_str(str(self.fractional)), denom) log(f'{fractional_fraction}') log(f'Converting whole portion ({self.whole}) to fraction...') whole_fraction = FractionNumber.from_whole(self.whole) log(f'{whole_fraction}') log(f'Adding ({whole_fraction}) to ({fractional_fraction})...') fraction = whole_fraction + fractional_fraction log(f'{fraction}') log(f'Applying sign of ({self.sign}) to {fraction}...') if self.sign == Sign.NEGATIVE: fraction = fraction * FractionNumber.from_str( "-1/1") # make sign negative log(f'{fraction}') log_unindent() return fraction
def common_divisibility_test(num: WholeNumber) -> Set[WholeNumber]: log_indent() try: ret: Set[WholeNumber] = set() last_digit: WholeNumber = WholeNumber.from_digit(num.digits[0]) # last digit is always at 0 idx log(f'Testing if {num} divisible by 2...') if last_digit == WholeNumber.from_int(0) \ or last_digit == WholeNumber.from_int(2) \ or last_digit == WholeNumber.from_int(4) \ or last_digit == WholeNumber.from_int(6) \ or last_digit == WholeNumber.from_int(8): log(f'Yes') ret.add(WholeNumber.from_int(2)) else: log(f'No') log(f'Testing if {num} divisible by 5...') if last_digit == WholeNumber.from_int(0) \ or last_digit == WholeNumber.from_int(5): log(f'Yes'); ret.add(WholeNumber.from_int(5)) else: log(f'No'); log(f'Testing if {num} divisible by 10...') if last_digit == WholeNumber.from_int(0): log(f'Yes'); ret.add(WholeNumber.from_int(10)) else: log(f'No'); log(f'Testing if {num} divisible by 3...') reduced_num: WholeNumber = num.copy() while True: digits = reduced_num.digits if len(digits) == 1: break reduced_num = sum([WholeNumber.from_int(d) for d in digits], WholeNumber.from_int(0)) if reduced_num == WholeNumber.from_int(3) \ or reduced_num == WholeNumber.from_int(6) \ or reduced_num == WholeNumber.from_int(9): log(f'Yes') ret.add(WholeNumber.from_int(3)) else: log(f'No') log(f'Testing if {num} divisible by 6...') if WholeNumber.from_int(2) in ret and WholeNumber.from_int(3) in ret: log(f'Yes') ret.add(WholeNumber.from_int(6)) else: log(f'NO') return ret finally: log_unindent()
def __gt__(lhs: DecimalNumber, rhs: DecimalNumber) -> bool: class PassedTestException(Exception): pass class FailedTestException(Exception): pass log(f'Greater than testing {lhs} and {rhs}...') log_indent() try: log(f'Testing sign...') sign_gt = lhs.sign == Sign.POSITIVE and (rhs.sign == Sign.NEGATIVE or rhs.sign is None) if sign_gt: raise PassedTestException() sign_eq = lhs.sign == rhs.sign if not sign_eq: raise FailedTestException() log(f'Equal') log(f'Testing whole...') if lhs.sign != Sign.NEGATIVE: whole_gt = lhs.whole > rhs.whole else: whole_gt = lhs.whole < rhs.whole if whole_gt: raise PassedTestException() whole_eq = lhs.whole == rhs.whole if not whole_eq: raise FailedTestException() log(f'Equal') log(f'Testing fractional...') if lhs.sign != Sign.NEGATIVE: fractional_gt = lhs.fractional > rhs.fractional else: fractional_gt = lhs.fractional < rhs.fractional if fractional_gt: raise PassedTestException() fractional_eq = lhs.fractional == rhs.fractional if not fractional_eq: raise FailedTestException() log(f'Equal') ret = False except PassedTestException: log(f'Greater') ret = True except FailedTestException: log(f'Not greater or equal') ret = False log_unindent() log(f'{ret}') return ret
def __eq__(lhs: FractionalNumber, rhs: FractionalNumber) -> bool: if not isinstance(rhs, FractionalNumber): raise Exception() log(f'Equality testing {lhs} and {rhs}...') log_indent() ret = lhs.digits == rhs.digits log_unindent() log(f'{ret}') return ret
def choose_start_modifier_for_divte( start_test_num: DecimalNumber) -> DecimalNumber: log(f'Choosing a starting modifier for {start_test_num}...') log_indent() log(f'{start_test_num} has {len(start_test_num.whole.digits)} digits') num_of_zeros = len(start_test_num.whole.digits) - 1 start_modifier_num = DecimalNumber.from_str('1' + '0' * num_of_zeros) log(f'Starting modifier: {start_modifier_num}') log_unindent() log(f'{start_modifier_num}') return start_modifier_num
def will_division_terminate(lhs: DecimalNumber, rhs: DecimalNumber) -> bool: log(f'Checking if {lhs} / {rhs} results in a non-terminating decimal...' ) log_indent() adjust_len_self = len(lhs.fractional.digits) adjust_len_other = len(rhs.fractional.digits) log(f'Generating mock integer number for {lhs}...') lhs_extra_0s = adjust_len_self - len(lhs.fractional.digits) lhs_combined_digits = lhs.fractional.digits + lhs.whole.digits lhs_combined_digits[0:0] = [Digit(0)] * lhs_extra_0s mock_self = IntegerNumber(lhs.sign, WholeNumber(lhs_combined_digits)) log(f'{mock_self}') log(f'Generating mock integer number for {rhs}...') rhs_extra_0s = adjust_len_other - len(rhs.fractional.digits) rhs_combined_digits = rhs.fractional.digits + rhs.whole.digits rhs_combined_digits[0:0] = [Digit(0)] * rhs_extra_0s mock_other = IntegerNumber(rhs.sign, WholeNumber(rhs_combined_digits)) log(f'{mock_other}') log(f'Generating mock fraction for {lhs} / {rhs}...') mock_fraction = FractionNumber(mock_self, mock_other) log(f'{mock_fraction}') log(f'Simplifying mock fraction...') mock_fraction = mock_fraction.simplify() log(f'{mock_fraction}') log(f'Checking if prime factors of denom ({mock_fraction.denominator}) is {{}}, {{2}}, {{5}}, or {{2,5}}...' ) mock_fraction_denom_prime_factors = set( factor_tree(mock_fraction.denominator).get_prime_factors()) if not ( {WholeNumber.from_str('2'), WholeNumber.from_str('5')} == mock_fraction_denom_prime_factors or {WholeNumber.from_str('2')} == mock_fraction_denom_prime_factors or {WholeNumber.from_str('5')} == mock_fraction_denom_prime_factors or 0 == len(mock_fraction_denom_prime_factors)): ret = False log(f'{ret} -- Won\'t terminate.') else: ret = True log(f'{ret} -- Will terminate.') log_unindent() log(f'{ret}') return ret
def __add__(lhs: FractionNumber, rhs: FractionNumber) -> FractionNumber: # Sign is only kept on the numerator, not the denominator log(f'Adding {lhs} and {rhs}...') log_indent() log(f'Converting {lhs} and {rhs} to equivalent fractions with least common denominator...' ) lhs, rhs = FractionNumber.common_denominator_lcm(lhs, rhs) log(f'Equivalent fractions: {lhs} and {rhs}') log(f'Adding numerators of {lhs} and {rhs}...') res = FractionNumber(lhs._numerator + rhs._numerator, lhs._denominator) log_unindent() log(f'Result: {res}') return res
def is_prime(num: WholeNumber) -> bool: log(f'Test if {num} is prime...') log_indent() num_factors = factor_fastest(num) # At a minimum, all counting numbers have the factors 1 and the number itself (2 factors). If # there are more factore than that, it's a composite. Otherwise, it's a primse. log_unindent() if len(num_factors) == 2: log(f'{num}\'s factors are {num_factors} -- it is a prime') return True else: log(f'{num}\'s factors are {num_factors} -- it is a composite') return False
def __eq__(self: IntegerNumber, other: IntegerNumber) -> bool: log(f'Equality testing {self} and {other}...') log_indent() log(f'Testing sign equality ({self.sign} vs {other.sign})...') sign_eq = self.sign == other.sign log(f'{sign_eq}') log(f'Testing magnitude equality ({self.magnitude} vs {other.magnitude})...') mag_eq = self.magnitude == other.magnitude log(f'{mag_eq}') log_unindent() ret = sign_eq and mag_eq log(f'{ret}') return ret
def __mul__(lhs: FractionNumber, rhs: FractionNumber) -> FractionNumber: # Sign is only kept on the numerator, not the denominator log(f'Multiplying {lhs} and {rhs}') log_indent() log(f'Multiplying numerators {lhs._numerator} and {rhs._numerator}...') numerator = lhs._numerator * rhs._numerator log(f'Multiplying denominators {lhs._denominator} and {rhs._denominator}...' ) denominator = lhs._denominator * rhs._denominator res = FractionNumber(numerator, denominator) log_unindent() log(f'Result: {res}') return res
def __mul__(lhs: DecimalNumber, rhs: DecimalNumber) -> DecimalNumber: log(f'Multiplying {lhs} and {rhs}...') log_indent() adjust_len_self = len(lhs.fractional.digits) adjust_len_other = len(rhs.fractional.digits) log(f'Generating mock integer number for {lhs}...') lhs_extra_0s = adjust_len_self - len(lhs.fractional.digits) lhs_combined_digits = lhs.fractional.digits + lhs.whole.digits lhs_combined_digits[0:0] = [Digit(0)] * lhs_extra_0s mock_self = IntegerNumber(lhs.sign, WholeNumber(lhs_combined_digits)) log(f'{mock_self}') log(f'Generating mock integer number for {rhs}...') rhs_extra_0s = adjust_len_other - len(rhs.fractional.digits) rhs_combined_digits = rhs.fractional.digits + rhs.whole.digits rhs_combined_digits[0:0] = [Digit(0)] * rhs_extra_0s mock_other = IntegerNumber(rhs.sign, WholeNumber(rhs_combined_digits)) log(f'{mock_other}') log(f'Performing {mock_self} * {mock_other}...') mock_ret = mock_self * mock_other log(f'{mock_ret}') log(f'Unmocking {mock_ret} back to decimal...') unadjust_len = adjust_len_self + adjust_len_other ret_sign = mock_ret.sign ret_fractional_digits = [ mock_ret.magnitude[i] for i in range(0, unadjust_len) ] ret_whole_digits = [ mock_ret.magnitude[i] for i in range(unadjust_len, len(mock_ret.magnitude.digits)) ] ret = DecimalNumber(ret_sign, WholeNumber(ret_whole_digits), FractionalNumber(ret_fractional_digits)) log(f'{ret}') log_unindent() log(f'{ret}') return ret
def from_fraction(value: FractionNumber) -> DecimalNumber: log(f'Converting {value} to a decimal number...') log_indent() log(f'Converting {value} to suitable fraction...') value = DecimalNumber.to_suitable_fraction(value) log(f'{value}') num = value.numerator denom = value.denominator if not str(denom).startswith('1'): raise Exception('Denominator must be power of 10') elif not set(str(denom)[1:]) == set('0') and not set( str(denom)[1:]) == set(): raise Exception('Denominator must be power of 10') log(f'Resolving fraction {value}...') whole, remaining = num / denom log(f'{whole} wholes and {remaining} remaining') log(f'Converting {remaining} of {denom} to fractional value...') num_digits_in_rem = len(remaining.digits) num_0s_in_den = len( denom.digits ) - 1 # starts with 1 followed by 0s, so - 1 to ignore the starting 1 num_0s_to_prepend_to_rem = num_0s_in_den - num_digits_in_rem fractional_digits = remaining.digits[:] # copy digits fractional_digits = fractional_digits + [Digit( 0)] * num_0s_to_prepend_to_rem # this prepending 0s... # you might be confused because the # 0s are being addeed to the end, but # that's how FractionalNumber expects # digits -- in reversed order fractional = FractionalNumber(fractional_digits) log(f'{fractional}') sign = value.sign log_unindent() ret = DecimalNumber(sign, whole, fractional) log(f'Decimal number: {ret}') return ret
def factor_fast(num: WholeNumber) -> Set[WholeNumber]: log(f'Factoring {num}...') log_indent() factors: Set[WholeNumber] = set() for factor1 in WholeNumber.range(WholeNumber.from_int(1), num, end_inclusive=True): log(f'Test if {factor1} is a factor...') factor2, remainder = num / factor1 if remainder == WholeNumber.from_int(0): factors.add(factor1) factors.add(factor2) log(f'Yes: ({factor1} and {factor2} are factors)') else: log(f'No') log_unindent() log(f'{factors}') return factors
def gcd_factor(num1: WholeNumber, num2: WholeNumber) -> WholeNumber: log(f'Calculating gcd for {num1} and {num2}...') log_indent() log(f'Calculating factors for {num1}...') factors1 = factor_fastest(num1) log(f'Factors for {num1}: {factors1}') log(f'Calculating factors for {num2}...') factors2 = factor_fastest(num2) log(f'Factors for {num2}: {factors2}') log(f'Finding common factors...') common_factors = factors1 & factors2 # set intersection log(f'Common factors for {num1} and {num2}: {common_factors}') found = max(common_factors) log_unindent() log(f'GCD is {found}') return found
def factor_naive(num: WholeNumber) -> Set[WholeNumber]: log(f'Factoring {num}...') log_indent() factors: Set[WholeNumber] = set() for factor1 in WholeNumber.range(WholeNumber.from_int(1), num, end_inclusive=True): for factor2 in WholeNumber.range(WholeNumber.from_int(1), num, end_inclusive=True): log(f'Testing if {factor1} and {factor2} are factors...') if factor1 * factor2 == num: factors.add(factor1) factors.add(factor2) log(f'Yes') else: log(f'No') log_unindent() log(f'{factors}') return factors
def __gt__(lhs: FractionalNumber, rhs: FractionalNumber) -> bool: if not isinstance(rhs, FractionalNumber): raise Exception() log(f'Greater than testing {lhs} and {rhs}...') log_indent() count = max(len(lhs.digits), len(rhs.digits)) for pos in range(0, count): # from smallest to largest component log(f'Test digits {lhs[pos]} and {rhs[pos]}...') if lhs[pos] > rhs[pos]: log(f'{lhs[pos]} > {rhs[pos]} -- {lhs} is greater than {rhs}') return True elif lhs[pos] < rhs[pos]: log(f'{lhs[pos]} < {rhs[pos]} -- {lhs} is NOT greater than {rhs}, it is less than' ) return False else: log(f'{lhs[pos]} == {rhs[pos]} -- continuing testing') log(f'No more digits to test -- {lhs} is NOT greater than {rhs}, it is equal' ) return False
def gcd_naive(num1: WholeNumber, num2: WholeNumber) -> WholeNumber: log(f'Calculating gcd for {num1} and {num2}...') log_indent() log(f'Sorting to determine smaller input...') min_num = min(num1, num2) log(f'Testing up to smaller input ({min_num})...') log_indent() for i in WholeNumber.range(WholeNumber.from_str('1'), min_num, True): log(f'Testing {i}...') quotient1, remainder1 = num1 / i quotient2, remainder2 = num2 / i if remainder1 == 0 and remainder2 == 0: log(f'{num1} and {num2} are both divisible by {i}...') found = i else: log(f'{num1} and {num2} are NOT both divisible by {i}...') log_unindent() log_unindent() log(f'GCD is {found}') return found
def gcd_euclid(num1: WholeNumber, num2: WholeNumber) -> WholeNumber: log(f'Calculating gcd for {num1} and {num2}...') log_indent() next_nums = [num1, num2] while True: log(f'Sorting {next_nums}...') next_nums.sort() # sort smallest to largest next_nums.reverse() # reverse it so that it's largest to largest log(f'Checking if finished ({next_nums[1]} == 0?)...') if next_nums[1] == WholeNumber.from_int(0): found = next_nums[0] break log(f'Dividing {next_nums} and grabbing the remainder for the next test...' ) _, remainder = next_nums[0] / next_nums[1] next_nums = [next_nums[1], remainder] log_unindent() log(f'GCD is {found}') return found
def __sub__(lhs: IntegerNumber, rhs: IntegerNumber) -> IntegerNumber: log(f'Subtracting {lhs} and {rhs}') log_indent() def determine_sign(magnitude: WholeNumber, default_sign: Sign) -> Sign: if magnitude == WholeNumber.from_int(0): return None else: return default_sign def flip_sign(sign: Sign) -> Sign: if sign == Sign.POSITIVE: return Sign.NEGATIVE elif sign == Sign.NEGATIVE: return Sign.POSITIVE if lhs.sign is None: # sign of None is only when magnitude is 0, 0 - a = -a sign = flip_sign(rhs.sign) magnitude = rhs.magnitude elif rhs.sign is None: # sign of None is only when magnitude is 0, a - 0 = a sign = lhs.sign magnitude = lhs.magnitude elif lhs.sign == rhs.sign: if rhs.magnitude >= lhs.magnitude: magnitude = rhs.magnitude - lhs.magnitude sign = determine_sign(magnitude, flip_sign(lhs.sign)) else: magnitude = lhs.magnitude - rhs.magnitude sign = determine_sign(magnitude, lhs.sign) elif lhs.sign != rhs.sign: magnitude = lhs.magnitude + rhs.magnitude sign = determine_sign(magnitude, lhs.sign) log_unindent() log(f'sign: {sign}, magnitude: {magnitude}') return IntegerNumber(sign, magnitude)
def __gt__(lhs: FractionNumber, rhs: FractionNumber) -> bool: log(f'Greater than testing {lhs} and {rhs}...') log_indent() # Sign is only kept on the numerator, not the denominator log(f'Checking if denominators are the same...') if lhs._denominator != rhs._denominator: log(f'Not same -- finding equivalent fractions with common denominator...' ) log_indent() log(f'Calculating common denominator...') denominator = rhs._denominator * lhs._denominator log(f'{denominator}') log(f'Scaling numerator for {lhs} so denominator becomes {denominator}...' ) lhs_numerator = lhs._numerator * rhs._denominator log(f'Numerator: {lhs_numerator} Denominator: {denominator}') log(f'Scaling numerator for {rhs} so denominator becomes {denominator}...' ) rhs_numerator = rhs._numerator * lhs._denominator log(f'Numerator: {rhs_numerator} Denominator: {denominator}') log_unindent() else: log(f'Same') lhs_numerator = lhs._numerator rhs_numerator = rhs._numerator denominator = rhs._denominator log(f'Testing {lhs_numerator} > {rhs_numerator}...') ret = lhs_numerator > rhs_numerator log(f'{ret}') return ret