Example #1
0
    def __init__( self, value, units = RPNUnits( ), unitName = None, pluralUnitName = None ):
        if not g.unitOperators:
            loadUnitData( )

        #print( '__init__', value, units, unitName, pluralUnitName )

        if isinstance( value, str ):
            self.value = mpmathify( value )
            self.units = RPNUnits( units )
        elif isinstance( value, RPNMeasurement ):
            self.value = value.getValue( )
            self.units = RPNUnits( value.getUnits( ) )
        else:
            self.value = value
            self.units = RPNUnits( units )

        self.unitName = unitName
        self.pluralUnitName = pluralUnitName
Example #2
0
    def getReduced( self ):
        debugPrint( 'getReduced 1:', self, [ ( i, self.units[ i ] ) for i in self.units ] )
        if not g.unitConversionMatrix:
            loadUnitConversionMatrix( )

        value = self.value
        units = RPNUnits( )

        debugPrint( 'value', value )

        for unit in self.units:
            newUnit = g.basicUnitTypes[ getUnitType( unit ) ].baseUnit
            debugPrint( 'unit', unit, 'newUnit', newUnit )

            if unit != newUnit:
                value = fmul( value, power( mpf( g.unitConversionMatrix[ ( unit, newUnit ) ] ), self.units[ unit ] ) )

            units.update( RPNUnits( g.unitOperators[ newUnit ].representation + "^" + str( self.units[ unit ] ) ) )
            debugPrint( 'value', value )

        reduced = RPNMeasurement( value, units )
        debugPrint( 'getReduced 2:', reduced )
        return reduced
Example #3
0
    def convertValue( self, other, tryReverse=True ):
        if self.isEquivalent( other ):
            return self.getValue( )

        if self.isCompatible( other ):
            conversions = [ ]

            if isinstance( other, list ):
                result = [ ]
                source = self

                for count, measurement in enumerate( other ):
                    with extradps( 1 ):
                        conversion = source.convertValue( measurement )

                    if count < len( other ) - 1:
                        result.append( RPNMeasurement( floor( conversion ), measurement.getUnits( ) ) )
                        source = RPNMeasurement( chop( frac( conversion ) ), measurement.getUnits( ) )
                    else:
                        result.append( RPNMeasurement( conversion, measurement.getUnits( ) ) )

                return result

            units1 = self.getUnits( )
            units2 = other.getUnits( )

            unit1String = units1.getUnitString( )
            unit2String = units2.getUnitString( )

            debugPrint( 'unit1String: ', unit1String )
            debugPrint( 'unit2String: ', unit2String )

            if unit1String == unit2String:
                return fmul( self.getValue( ), other.getValue( ) )

            if unit1String in g.operatorAliases:
                unit1String = g.operatorAliases[ unit1String ]

            if unit2String in g.operatorAliases:
                unit2String = g.operatorAliases[ unit2String ]

            exponents = { }

            if not g.unitConversionMatrix:
                loadUnitConversionMatrix( )

            # look for a straight-up conversion
            unit1NoStar = unit1String.replace( '*', '-' )
            unit2NoStar = unit2String.replace( '*', '-' )

            debugPrint( 'unit1NoStar: ', unit1NoStar )
            debugPrint( 'unit2NoStar: ', unit2NoStar )

            if ( unit1NoStar, unit2NoStar ) in g.unitConversionMatrix:
                value = fmul( self.value, mpmathify( g.unitConversionMatrix[ ( unit1NoStar, unit2NoStar ) ] ) )
            elif ( unit1NoStar, unit2NoStar ) in specialUnitConversionMatrix:
                value = specialUnitConversionMatrix[ ( unit1NoStar, unit2NoStar ) ]( self.value )
            else:
                # otherwise, we need to figure out how to do the conversion
                conversionValue = mpmathify( 1 )

                # if that isn't found, then we need to do the hard work and break the units down
                newUnits1 = RPNUnits( )

                for unit in units1:
                    newUnits1.update( RPNUnits( g.unitOperators[ unit ].representation + "^" +
                                                str( units1[ unit ] ) ) )

                newUnits2 = RPNUnits( )

                for unit in units2:
                    newUnits2.update( RPNUnits( g.unitOperators[ unit ].representation + "^" +
                                                str( units2[ unit ] ) ) )

                debugPrint( 'units1:', units1 )
                debugPrint( 'units2:', units2 )
                debugPrint( 'newUnits1:', newUnits1 )
                debugPrint( 'newUnits2:', newUnits2 )

                debugPrint( )
                debugPrint( 'iterating through units:' )

                for unit1 in newUnits1:
                    foundConversion = False

                    for unit2 in newUnits2:
                        debugPrint( 'units 1:', unit1, newUnits1[ unit1 ], getUnitType( unit1 ) )
                        debugPrint( 'units 2:', unit2, newUnits2[ unit2 ], getUnitType( unit2 ) )

                        if getUnitType( unit1 ) == getUnitType( unit2 ):
                            conversions.append( [ unit1, unit2 ] )
                            exponents[ ( unit1, unit2 ) ] = units1[ unit1 ]
                            foundConversion = True
                            break

                    if not foundConversion:
                        debugPrint( 'didn\'t find a conversion, try reducing' )
                        reduced = self.getReduced( )

                        debugPrint( 'reduced:', self.units, 'becomes', reduced.units )

                        reducedOther = other.getReduced( )

                        debugPrint( 'reduced other:', other.units, 'becomes', reducedOther.units )

                        # check to see if reducing did anything and bail if it didn't... bail out
                        if ( reduced.units == self.units ) and ( reducedOther.units == other.units ):
                            break

                        reduced = reduced.convertValue( reducedOther )
                        return RPNMeasurement( fdiv( reduced, reducedOther.value ), reducedOther.getUnits( ) ).getValue( )

                debugPrint( )

                value = conversionValue

                if not foundConversion:
                    # This is a cheat.  The conversion logic has flaws, but if it's possible to do the
                    # conversion in the opposite direction, then we can do that and return the reciprocal.
                    # This allows more conversions without fixing the underlying problems, which will
                    # require some redesign.
                    if tryReverse:
                        return fdiv( 1, other.convertValue( self, False ) )
                    else:
                        raise ValueError( 'unable to convert ' + self.getUnitString( ) +
                                          ' to ' + other.getUnitString( ) )

                for conversion in conversions:
                    if conversion[ 0 ] == conversion[ 1 ]:
                        continue  # no conversion needed

                    debugPrint( 'unit conversion:', g.unitConversionMatrix[ tuple( conversion ) ] )
                    debugPrint( 'exponents', exponents )

                    conversionValue = mpmathify( g.unitConversionMatrix[ tuple( conversion ) ] )
                    conversionValue = power( conversionValue, exponents[ tuple( conversion ) ] )
                    debugPrint( 'conversion: ', conversion, conversionValue )

                    value = fmul( value, conversionValue )

                value = fmul( self.value, value )

            return value
        else:
            if isinstance( other, list ):
                otherUnit = '[ ' + ', '.join( [ unit.getUnitString( ) for unit in other ] ) + ' ]'
            else:
                otherUnit = other.getUnitString( )

            raise ValueError( 'incompatible units cannot be converted: ' + self.getUnitString( ) +
                              ' and ' + otherUnit )
Example #4
0
class RPNMeasurement( object ):
    '''This class represents a measurement, which includes a numerical value
    and an RPNUnits instance.'''
    value = mpf( )
    units = RPNUnits( )
    unitName = None
    pluralUnitName = None

    def __init__( self, value, units = RPNUnits( ), unitName = None, pluralUnitName = None ):
        if not g.unitOperators:
            loadUnitData( )

        #print( '__init__', value, units, unitName, pluralUnitName )

        if isinstance( value, str ):
            self.value = mpmathify( value )
            self.units = RPNUnits( units )
        elif isinstance( value, RPNMeasurement ):
            self.value = value.getValue( )
            self.units = RPNUnits( value.getUnits( ) )
        else:
            self.value = value
            self.units = RPNUnits( units )

        self.unitName = unitName
        self.pluralUnitName = pluralUnitName

    def __eq__( self, other ):
        if isinstance( other, RPNMeasurement ):
            result = mpf.__eq__( self.value, other.value )
        else:
            result = mpf.__eq__( self.value, other )

        if not result:
            return result

        if isinstance( other, RPNMeasurement ):
            result = ( self.units == other.units )

        return result

    def __ne__( self, other ):
        return not __eq__( self, other )

    #def __repr__( self ):
    #    return 'RPNMeasurement(\'' + str( mpf( self ) ) + '\',' + \
    #           ( '{}' if self.units is None else '\'' + self.units.getUnitString( ) + '\'' ) + \
    #           '\'' + self.unitName + '\',\'' + self.pluralUnitName + '\')'

    #def __str__( self ):
    #    return repr( self )

    def increment( self, unit, amount = 1 ):
        self.unitName = None
        self.pluralUnitName = None
        self.units[ unit ] += amount

    def decrement( self, unit, amount = 1 ):
        increment( self, unit, -amount )

    def add( self, other ):
        if isinstance( other, RPNMeasurement ):
            if self.getUnits( ) == other.getUnits( ):
                return RPNMeasurement( fadd( self.value, other.value ), self.getUnits( ),
                                       self.getUnitName( ), self.getPluralUnitName( ) )
            else:
                newOther = other.convertValue( self )

                return RPNMeasurement( fadd( self.value, newOther ), self.getUnits( ),
                                       self.getUnitName( ), self.getPluralUnitName( ) )
        else:
            return RPNMeasurement( fadd( self.value, other ), self.getUnits( ),
                                   self.getUnitName( ), self.getPluralUnitName( ) )

    def subtract( self, other ):
        if isinstance( other, RPNMeasurement ):
            if self.getUnits( ) == other.getUnits( ):
                return RPNMeasurement( fsub( self.value, other.value ), self.getUnits( ),
                                       self.getUnitName( ), self.getPluralUnitName( ) )
            else:
                newOther = other.convertValue( self )
                return RPNMeasurement( fsub( self.value, newOther ), self.getUnits( ),
                                       self.getUnitName( ), self.getPluralUnitName( ) )

        else:
            return RPNMeasurement( fsub( self.value, other ), self.getUnits( ),
                                   self.getUnitName( ), self.getPluralUnitName( ) )

    def multiply( self, other ):
        if isinstance( other, RPNMeasurement ):
            newValue = fmul( self.value, other.value )

            factor, newUnits = combineUnits( self.getUnits( ), other.getUnits( ) )

            self = RPNMeasurement( fmul( newValue, factor ), newUnits )
        else:
            newValue = fmul( self.value, other )

            self = RPNMeasurement( newValue, self.getUnits( ),
                                   self.getUnitName( ), self.getPluralUnitName( ) )

        return self.normalizeUnits( )

    def divide( self, other ):
        if isinstance( other, RPNMeasurement ):
            newValue = fdiv( self.value, other.value )

            factor, newUnits = combineUnits( self.getUnits( ), other.getUnits( ).inverted( ) )

            self = RPNMeasurement( fmul( newValue, factor ), newUnits )
        else:
            newValue = fdiv( self.value, other )

            self = RPNMeasurement( newValue, self.getUnits( ),
                                   self.getUnitName( ), self.getPluralUnitName( ) )

        return self.normalizeUnits( )

    def exponentiate( self, exponent ):
        if ( floor( exponent ) != exponent ):
            raise ValueError( 'cannot raise a measurement to a non-integral power' )

        newValue = power( self.value, exponent )

        for unit in self.units:
            self.units[ unit ] *= exponent

        self = RPNMeasurement( newValue, self.units )

        return self

    def invert( self ):
        units = self.getUnits( )

        newUnits = RPNUnits( )

        for unit in units:
            newUnits[ unit ] = -units[ unit ]

        return RPNMeasurement( self.value, newUnits )

    def normalizeUnits( self ):
        units = self.getUnits( )

        newUnits = RPNUnits( )

        for unit in units:
            if units[ unit ] != 0 and unit != '_null_unit':
                newUnits[ unit ] = units[ unit ]

        return RPNMeasurement( self.value, newUnits, self.getUnitName( ), self.getPluralUnitName( ) )

    def update( self, units ):
        for i in self.units:
            del self.units[ i ]

        if not isinstance( units, dict ):
            raise ValueError( 'dict expected' )

        self.unitName = None
        self.pluralUnitName = None

        self.units.update( units )

    def isCompatible( self, other ):
        if isinstance( other, RPNUnits ):
            return self.getUnitTypes( ) == other.getUnitTypes( )
        elif isinstance( other, dict ):
            return self.getUnitTypes( ) == other
        elif isinstance( other, list ):
            result = True

            for item in other:
                result = self.isCompatible( item )

                if not result:
                    break

            return result
        elif isinstance( other, RPNMeasurement ):
            debugPrint( 'units: ', self.units, other.units )
            debugPrint( 'types: ', self.getUnitTypes( ), other.getUnitTypes( ) )
            debugPrint( 'dimensions: ', self.getDimensions( ), other.getDimensions( ) )

            if self.getUnitTypes( ) == other.getUnitTypes( ):
                return True
            elif self.getDimensions( ) == other.getDimensions( ):
                return True
            else:
                debugPrint( 'RPNMeasurement.isCompatible exiting with false...' )
                return False
        else:
            raise ValueError( 'RPNMeasurement or dict expected' )

    def isEquivalent( self, other ):
        if isinstance( other, list ):
            result = True

            for item in other:
                result = self.isEquivalent( item )

                if not result:
                    break

            return result
        elif isinstance( other, RPNMeasurement ):
            return self.getUnits( ) == other.getUnits( )
        else:
            raise ValueError( 'RPNMeasurement or dict expected' )

    def getValue( self ):
        return self.value

    def getUnits( self ):
        return self.units

    def getUnitString( self ):
        return self.units.getUnitString( )

    def getUnitName( self ):
        if self.unitName:
            return self.unitName

        unitString = self.getUnitString( )

        if unitString in g.unitOperators:
            return g.unitOperators[ unitString ].representation
        else:
            return unitString


    def getPluralUnitName( self ):
        if self.pluralUnitName:
            return self.pluralUnitName

        unitString = self.getUnitString( )

        if unitString in g.unitOperators:
            return g.unitOperators[ unitString ].plural
        else:
            return unitString

    def getUnitTypes( self ):
        types = RPNUnits( )

        for unit in self.units:
            if unit not in g.unitOperators:
                raise ValueError( 'undefined unit type \'{}\''.format( unit ) )

            unitType = getUnitType( unit )

            if unitType in types:
                types[ unitType ] += self.units[ unit ]
            elif self.units[ unit ] != 0:
                types[ unitType ] = self.units[ unit ]

        return types

    def getDimensions( self ):
        return self.units.getDimensions( )

    def getReduced( self ):
        debugPrint( 'getReduced 1:', self, [ ( i, self.units[ i ] ) for i in self.units ] )
        if not g.unitConversionMatrix:
            loadUnitConversionMatrix( )

        value = self.value
        units = RPNUnits( )

        debugPrint( 'value', value )

        for unit in self.units:
            newUnit = g.basicUnitTypes[ getUnitType( unit ) ].baseUnit
            debugPrint( 'unit', unit, 'newUnit', newUnit )

            if unit != newUnit:
                value = fmul( value, power( mpf( g.unitConversionMatrix[ ( unit, newUnit ) ] ), self.units[ unit ] ) )

            units.update( RPNUnits( g.unitOperators[ newUnit ].representation + "^" + str( self.units[ unit ] ) ) )
            debugPrint( 'value', value )

        reduced = RPNMeasurement( value, units )
        debugPrint( 'getReduced 2:', reduced )
        return reduced

    def convert( self, other ):
        if isinstance( other, RPNMeasurement ):
            return RPNMeasurement( self.convertValue( other ), other.getUnits( ) )
        elif isinstance( other, ( RPNUnits, dict, str ) ):
            measurement = RPNMeasurement( 1, other )
            return RPNMeasurement( self.convertValue( measurement ), measurement.getUnits( ) )
        else:
            raise ValueError( 'convert doesn\'t know what to do with this argument' )

    def isLarger( self, other ):
        newValue = self.convert( other )
        return ( newValue.getValue( ) > other.getValue( ) )

    def isNotLarger( self, other ):
        return not self.isLarger( other )

    def isSmaller( self, other ):
        newValue = self.convert( other )
        return ( newValue.getValue( ) < other.getValue( ) )

    def isNotSmaller( self, other ):
        return not self.isSmaller( other )

    def isEqual( self, other ):
        newValue = self.convert( other )
        #print( newValue.getValue( ), other.getValue( ) )
        return ( newValue.getValue( ) == other.getValue( ) )

    def isNotEqual( self, other ):
        return not self.isEqual( other )

    def convertValue( self, other, tryReverse=True ):
        if self.isEquivalent( other ):
            return self.getValue( )

        if self.isCompatible( other ):
            conversions = [ ]

            if isinstance( other, list ):
                result = [ ]
                source = self

                for count, measurement in enumerate( other ):
                    with extradps( 1 ):
                        conversion = source.convertValue( measurement )

                    if count < len( other ) - 1:
                        result.append( RPNMeasurement( floor( conversion ), measurement.getUnits( ) ) )
                        source = RPNMeasurement( chop( frac( conversion ) ), measurement.getUnits( ) )
                    else:
                        result.append( RPNMeasurement( conversion, measurement.getUnits( ) ) )

                return result

            units1 = self.getUnits( )
            units2 = other.getUnits( )

            unit1String = units1.getUnitString( )
            unit2String = units2.getUnitString( )

            debugPrint( 'unit1String: ', unit1String )
            debugPrint( 'unit2String: ', unit2String )

            if unit1String == unit2String:
                return fmul( self.getValue( ), other.getValue( ) )

            if unit1String in g.operatorAliases:
                unit1String = g.operatorAliases[ unit1String ]

            if unit2String in g.operatorAliases:
                unit2String = g.operatorAliases[ unit2String ]

            exponents = { }

            if not g.unitConversionMatrix:
                loadUnitConversionMatrix( )

            # look for a straight-up conversion
            unit1NoStar = unit1String.replace( '*', '-' )
            unit2NoStar = unit2String.replace( '*', '-' )

            debugPrint( 'unit1NoStar: ', unit1NoStar )
            debugPrint( 'unit2NoStar: ', unit2NoStar )

            if ( unit1NoStar, unit2NoStar ) in g.unitConversionMatrix:
                value = fmul( self.value, mpmathify( g.unitConversionMatrix[ ( unit1NoStar, unit2NoStar ) ] ) )
            elif ( unit1NoStar, unit2NoStar ) in specialUnitConversionMatrix:
                value = specialUnitConversionMatrix[ ( unit1NoStar, unit2NoStar ) ]( self.value )
            else:
                # otherwise, we need to figure out how to do the conversion
                conversionValue = mpmathify( 1 )

                # if that isn't found, then we need to do the hard work and break the units down
                newUnits1 = RPNUnits( )

                for unit in units1:
                    newUnits1.update( RPNUnits( g.unitOperators[ unit ].representation + "^" +
                                                str( units1[ unit ] ) ) )

                newUnits2 = RPNUnits( )

                for unit in units2:
                    newUnits2.update( RPNUnits( g.unitOperators[ unit ].representation + "^" +
                                                str( units2[ unit ] ) ) )

                debugPrint( 'units1:', units1 )
                debugPrint( 'units2:', units2 )
                debugPrint( 'newUnits1:', newUnits1 )
                debugPrint( 'newUnits2:', newUnits2 )

                debugPrint( )
                debugPrint( 'iterating through units:' )

                for unit1 in newUnits1:
                    foundConversion = False

                    for unit2 in newUnits2:
                        debugPrint( 'units 1:', unit1, newUnits1[ unit1 ], getUnitType( unit1 ) )
                        debugPrint( 'units 2:', unit2, newUnits2[ unit2 ], getUnitType( unit2 ) )

                        if getUnitType( unit1 ) == getUnitType( unit2 ):
                            conversions.append( [ unit1, unit2 ] )
                            exponents[ ( unit1, unit2 ) ] = units1[ unit1 ]
                            foundConversion = True
                            break

                    if not foundConversion:
                        debugPrint( 'didn\'t find a conversion, try reducing' )
                        reduced = self.getReduced( )

                        debugPrint( 'reduced:', self.units, 'becomes', reduced.units )

                        reducedOther = other.getReduced( )

                        debugPrint( 'reduced other:', other.units, 'becomes', reducedOther.units )

                        # check to see if reducing did anything and bail if it didn't... bail out
                        if ( reduced.units == self.units ) and ( reducedOther.units == other.units ):
                            break

                        reduced = reduced.convertValue( reducedOther )
                        return RPNMeasurement( fdiv( reduced, reducedOther.value ), reducedOther.getUnits( ) ).getValue( )

                debugPrint( )

                value = conversionValue

                if not foundConversion:
                    # This is a cheat.  The conversion logic has flaws, but if it's possible to do the
                    # conversion in the opposite direction, then we can do that and return the reciprocal.
                    # This allows more conversions without fixing the underlying problems, which will
                    # require some redesign.
                    if tryReverse:
                        return fdiv( 1, other.convertValue( self, False ) )
                    else:
                        raise ValueError( 'unable to convert ' + self.getUnitString( ) +
                                          ' to ' + other.getUnitString( ) )

                for conversion in conversions:
                    if conversion[ 0 ] == conversion[ 1 ]:
                        continue  # no conversion needed

                    debugPrint( 'unit conversion:', g.unitConversionMatrix[ tuple( conversion ) ] )
                    debugPrint( 'exponents', exponents )

                    conversionValue = mpmathify( g.unitConversionMatrix[ tuple( conversion ) ] )
                    conversionValue = power( conversionValue, exponents[ tuple( conversion ) ] )
                    debugPrint( 'conversion: ', conversion, conversionValue )

                    value = fmul( value, conversionValue )

                value = fmul( self.value, value )

            return value
        else:
            if isinstance( other, list ):
                otherUnit = '[ ' + ', '.join( [ unit.getUnitString( ) for unit in other ] ) + ' ]'
            else:
                otherUnit = other.getUnitString( )

            raise ValueError( 'incompatible units cannot be converted: ' + self.getUnitString( ) +
                              ' and ' + otherUnit )