Skip to content

Commit

Permalink
added 'primitive_units', which replaces 'base_units', although 'base_…
Browse files Browse the repository at this point in the history
…units' still does the same thing.
  • Loading branch information
ConceptJunkie committed Jul 3, 2019
1 parent 79e5470 commit ca451f7
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 48 deletions.
90 changes: 65 additions & 25 deletions rpn/makeHelp.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
PROGRAM_NAME = 'makeHelp'
PROGRAM_DESCRIPTION = 'rpnChilada help generator'

maxExampleCount = 1485
maxExampleCount = 1489

os.chdir( getUserDataPath( ) ) # SkyField doesn't like running in the root directory

Expand Down Expand Up @@ -527,17 +527,19 @@ def makeCommandExample( command, indent=0, slow=False ):
Using 'for_each' on a nested list should give a nice error message.
Using 'for_each_list' on a non-nested list crashes.
'rpn [ 1 2 3 ] lambda x 2 + for_each' crashes. Honestly, the distinction
between 'for_each' and 'eval' is pretty vague. I'm not sure 'for_each'
really needs to exist.
-i doesn't work for lists.
'(' and ')' (multiple operators) don't work with generators because the
generator only works once.
generator only works once. The structure of the evaluator won't allow me to
fix this, I think.. It may have to wait until I convert all rpn expressions to
Python before this can be fixed.
'collate' does not work with generators.
'unlist' doesn't seem to do anything any more.
Chained calls to 'next_new_moon' give the same answer over and over. Other
related operators probably do the same thing.
Expand All @@ -558,8 +560,6 @@ def makeCommandExample( command, indent=0, slow=False ):
operators that take more than 2 arguments don't handle recursive list
arguments.
Cousin primes seem to be broken starting with index 99, according to OEIS.
'reversal_addition' doesn't work with generators.
See 'rpn help TODO'.
Expand All @@ -570,26 +570,49 @@ def makeCommandExample( command, indent=0, slow=False ):
gets smaller.
* 'humanize' - like 'name' but only 2 significant digits when > 1000
* 'name' should handle fractions smaller than 1 gracefully (right now it prints nothing)
* support date comparisons, etc. before the epoch (Arrow doesn't work before the epoch apparently!)
* 'name' should handle fractions smaller than 1 gracefully (right now it
prints nothing)
* support date comparisons, etc. before the epoch (Arrow doesn't work before
the epoch apparently!)
* create an output handler for RPNLocation
* 'result' doesn't work with measurements
* https://en.wikipedia.org/wiki/American_wire_gauge
* 'mean' should work with measurements
* units aren't supported in user-defined functions
* http://en.wikipedia.org/wiki/Physical_constant
* http://stackoverflow.com/questions/14698104/how-to-predict-tides-using-harmonic-constants
* OEIS comment text occasionally contains non-ASCII characters, and rpn chokes on that
* *_primes_ operators seem to be unreasonably slow
* OEIS comment text occasionally contains non-ASCII characters, and rpn chokes
on that
* 'fraction' needs to figure out what precision is needed and set it itself
Long-term goals
* Performance, performance, performance. There's a lot of functionality in rpn which is way too slow.
* This is a big one, and may not be possible with the current syntax, but I would love to support nested lambdas.
* Turn rpn into a full-blown scripting language. It's 2/3 of the way there. Why not go all the way?
* Redesign the parsing logic. It's excessively complex has lots of edge cases where it breaks down.
* Lambdas are converted into Python code, compiled and run. Perhaps all expressions should work this way!
* The biggest change I want to do is completely rewrite parsing and evaluating.
I want the parser to generate Python code, then the evaluator can simply go
away, because Python will do it for us! The current parsing logic has been
extended beyond all reasonableness. It's excessively complex has lots of
edge cases where it breaks down.
* Performance, performance, performance. There's a lot of functionality in
rpn which is way too slow.
* Converting to using numpy arrays instead of lists should improve performance.
* I would love to support nested lambdas, but this won't happen until the
parser is redesigned.
* Turn rpn into a full-blown scripting language. It's 2/3 of the way there.
Why not go all the way? Once the parser generates Python, I think I'll be
90% of the way there.
See 'rpn help bugs'.
''',
Expand Down Expand Up @@ -1878,7 +1901,9 @@ def makeCommandExample( command, indent=0, slow=False ):
'''
''' + makeCommandExample( '1440 24 /' ) + '''
''' + makeCommandExample( '2520 1 10 range /' ) + '''
''' + makeCommandExample( 'miles hour / furlongs fortnight / convert' ),
''' + makeCommandExample( 'miles hour / furlongs fortnight / convert' ) + '''
How long would 4 AA batteries power a Raspberry Pi 4, which draws 7 watts?
''' + makeCommandExample( '4 aa_battery 7 watts / hms', indent=4 ),
[ 'multiply', 'add', 'subtract', 'reciprocal' ] ],

'equals_one_of' : [
Expand Down Expand Up @@ -4941,7 +4966,7 @@ def makeCommandExample( command, indent=0, slow=False ):
''' + makeCommandExample( '78 kg [ pound ounce ] convert' ) + '''
This conversions suffers from a minor rounding error I haven't been able to
fix yet:
''' + makeCommandExample( '150,000 seconds [ day hour minute second ] convert' ),
''' + makeCommandExample( '150,000 seconds [ day hour minute second ] convert', indent=4 ),
[ ] ],

'dhms' : [
Expand Down Expand Up @@ -11063,13 +11088,15 @@ def makeCommandExample( command, indent=0, slow=False ):
'base_units' : [
'special', 'returns a measurement converted to base units',
'''
Currently, this is only for informational purposes.
Currently, this is only for informational purposes. This operator also
currently does the same thing as 'primitive_units'. I have plans for it,
though.
''',
'''
''' + makeCommandExample( '50 watts' ) + '''
''' + makeCommandExample( '120 millivolts' ) + '''
''' + makeCommandExample( '300 kilonewtons' ),
[ 'dimensions' ] ],
''' + makeCommandExample( '50 watts base_units' ) + '''
''' + makeCommandExample( '120 millivolts base_units' ) + '''
''' + makeCommandExample( '300 kilonewtons base_units' ),
[ 'dimensions', 'primitive_units' ] ],

'constant' : [
'special', 'creates a user-defined constant',
Expand Down Expand Up @@ -11099,7 +11126,7 @@ def makeCommandExample( command, indent=0, slow=False ):
''' + makeCommandExample( 'newton dimensions' ) + '''
''' + makeCommandExample( 'volt dimensions' ) + '''
''' + makeCommandExample( 'coulomb dimensions' ),
[ 'base_units' ] ],
[ 'base_units', 'primitive_units' ] ],

'dump_config' : [
'special', 'dumps all configuration settings',
Expand Down Expand Up @@ -11398,6 +11425,19 @@ def makeCommandExample( command, indent=0, slow=False ):
'5d6x2'.''',
[ 'roll_dice', 'roll_dice_', 'enumerate_dice', 'enumerate_dice_' ] ],

'primitive_units' : [
'special', 'returns a measurement converted to primitive units',
'''
Currently, this is only for informational purposes. This operator also
currently does the same thing as 'base_units'. I have plans to change
'base_units'.
''',
'''
''' + makeCommandExample( '50 watts primitive_units' ) + '''
''' + makeCommandExample( '120 millivolts primitive_units' ) + '''
''' + makeCommandExample( '300 kilonewtons primitive_units' ),
[ 'dimensions', 'base_units' ] ],

'random_integer' : [
'special', 'returns a random integer from 0 to n - 1',
'''
Expand Down
67 changes: 58 additions & 9 deletions rpn/rpnMeasurement.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from rpn.rpnPersistence import loadUnitConversionMatrix
from rpn.rpnUnitClasses import getUnitDimensionList, getUnitDimensions, \
getUnitType, RPNUnits
from rpn.rpnUnitTypes import basicUnitTypes
from rpn.rpnUtils import debugPrint, flattenList, getPowerset, \
oneArgFunctionEvaluator

Expand Down Expand Up @@ -229,13 +230,15 @@ def subtract( self, other ):
def multiply( self, other ):
if isinstance( other, RPNMeasurement ):
factor, newUnits = self.units.combineUnits( other.units )
newUnits = RPNMeasurement( 1, newUnits ).simplifyUnits( ).units
return RPNMeasurement( fmul( fmul( self.value, other.value ), factor ), newUnits ).normalizeUnits( )
else:
return RPNMeasurement( fmul( self.value, other ), self.units ).normalizeUnits( )

def divide( self, other ):
if isinstance( other, RPNMeasurement ):
factor, newUnits = self.units.combineUnits( other.units.inverted( ) )
newUnits = RPNMeasurement( 1, newUnits ).simplifyUnits( ).units
return RPNMeasurement( fmul( fdiv( self.value, other.value ), factor ), newUnits ).normalizeUnits( )
else:
return RPNMeasurement( fdiv( self.value, other ), self.units ).normalizeUnits( )
Expand Down Expand Up @@ -276,7 +279,7 @@ def getRoot( self, operand ):
else:
name = getOrdinalName( operand )

baseUnits = self.convertToBaseUnits( )
baseUnits = self.convertToPrimitiveUnits( )

if ( baseUnits != self ):
return baseUnits.getRoot( operand )
Expand Down Expand Up @@ -522,9 +525,9 @@ def getDimensions( self ):
def doDimensionsCancel( self ):
return self.units.doDimensionsCancel( )

def convertToBaseUnits( self ):
def convertToPrimitiveUnits( self ):
debugPrint( )
debugPrint( 'convertToBaseUnits:', self.value, self.units )
debugPrint( 'convertToPrimitiveUnits:', self.value, self.units )

if not g.unitConversionMatrix:
loadUnitConversionMatrix( )
Expand All @@ -549,7 +552,7 @@ def convertToBaseUnits( self ):
else:
if unit == '1' and newUnits == '_null_unit':
reduced = RPNMeasurement( value, units )
debugPrint( 'convertToBaseUnits 2:', reduced.value, reduced.units )
debugPrint( 'convertToPrimitiveUnits 2:', reduced.value, reduced.units )
return reduced
else:
raise ValueError( 'cannot find a conversion for ' + unit + ' and ' + newUnits )
Expand All @@ -564,10 +567,42 @@ def convertToBaseUnits( self ):
debugPrint( 'value', value )

baseUnits = RPNMeasurement( value, units )
debugPrint( 'convertToBaseUnits 3:', baseUnits.value, baseUnits.units )
debugPrint( 'convertToPrimitiveUnits 3:', baseUnits.value, baseUnits.units )
debugPrint( )
return baseUnits

def simplifyUnits( self ):
'''
This is a subset of convertToPrimitiveUnits' functionality. It calls
convertToPrimitiveUnits( ), but if the value changes (i.e., there's a conversion
factor needed, then it will ignore the conversion. The reason we are doing
this is because we want joules/watt to convert to seconds, but we don't want
miles/hour to convert to meters/second.
'''
originalValue = self.value

# Try converting to base units, but only keep it if there's no conversion factor.
baseUnits = self.convertToPrimitiveUnits( )

if baseUnits.value == originalValue:
result = baseUnits
else:
result = self

# Let's see if we have a base unit type, and use it if we do, since that's a great
# simplification. This way you can multiply watts by seconds and get joules. However,
# again we don't want a conversion factor. e.g., meters^3 should not be converted to liters.
for name, unitTypeInfo in basicUnitTypes.items( ):
if result.getUnitName( ) == unitTypeInfo.primitiveUnit:
test = RPNMeasurement( result )

if test.convert( unitTypeInfo.baseUnit ).value == result.value:
result = RPNMeasurement( originalValue, unitTypeInfo.baseUnit )

break

return result

def convert( self, other ):
if isinstance( other, RPNMeasurement ):
return RPNMeasurement( self.convertValue( other ), other.units )
Expand Down Expand Up @@ -667,7 +702,7 @@ def convertValue( self, other ):
else:
# TODO: Should we just convert to base units regardless? It would be safer...
if other.doDimensionsCancel( ):
other = other.convertToBaseUnits( )
other = other.convertToPrimitiveUnits( )
units2 = other.units
value = fdiv( value, other.value )

Expand Down Expand Up @@ -704,11 +739,11 @@ def convertValue( self, other ):
debugPrint( )
debugPrint( 'didn\'t find a conversion, try reducing' )
debugPrint( )
reduced = self.convertToBaseUnits( )
reduced = self.convertToPrimitiveUnits( )

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

reducedOther = other.convertToBaseUnits( )
reducedOther = other.convertToPrimitiveUnits( )

reduced.value = fdiv( reduced.value, reducedOther.value )

Expand Down Expand Up @@ -1083,7 +1118,21 @@ def getDimensions( n ):
@oneArgFunctionEvaluator( )
def convertToBaseUnits( n ):
if isinstance( n, RPNMeasurement ):
return n.convertToBaseUnits( )
return n.convertToPrimitiveUnits( )
else:
return n


# //******************************************************************************
# //
# // convertToPrimitiveUnits
# //
# //******************************************************************************

@oneArgFunctionEvaluator( )
def convertToPrimitiveUnits( n ):
if isinstance( n, RPNMeasurement ):
return n.convertToPrimitiveUnits( )
else:
return n

Expand Down
10 changes: 7 additions & 3 deletions rpn/rpnOperators.py
Original file line number Diff line number Diff line change
Expand Up @@ -4429,12 +4429,13 @@ def createSizedRangeOperator( a, b, c ):
'ordinal_name' : RPNOperator( getOrdinalName,
1, [ RPNArgumentType.Integer ], [ ] ),

'result' : RPNOperator( loadResult,
0, [ ], [ ] ),

'permute_dice' : RPNOperator( permuteDiceGenerator,
1, [ RPNArgumentType.String ], [ ] ),

'primitive_units' : RPNOperator( convertToPrimitiveUnits,
1, [ RPNArgumentType.Measurement ], [ ],
RPNOperator.measurementsAllowed ),

'random' : RPNOperator( getRandomNumber,
0, [ ], [ ] ),

Expand All @@ -4447,6 +4448,9 @@ def createSizedRangeOperator( a, b, c ):
'random_integer_' : RPNOperator( getRandomIntegersGenerator,
2, [ RPNArgumentType.PositiveInteger, RPNArgumentType.PositiveInteger ], [ ] ),

'result' : RPNOperator( loadResult,
0, [ ], [ ] ),

'roll_dice' : RPNOperator( rollDice,
1, [ RPNArgumentType.String ], [ ] ),

Expand Down
13 changes: 7 additions & 6 deletions rpn/rpnPrimeUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -891,12 +891,13 @@ def getNthDoubleBalancedPrimeElement( arg, first = False ):
@oneArgFunctionEvaluator( )
def getNthDoubleBalancedPrimeList( arg ):
p = getNthDoubleBalancedPrimeElement( arg, first = False )

result = [ 0, 0, p, 0, 0 ]
middle = 2

for i in range( 0, 2 ):
p[ middle + i + 1 ] = getPreviousPrime( p[ middle + i ] )
p[ middle - ( i + 1 ) ] = getNextPrime( p[ middle - i ] )
result[ middle + i + 1 ] = getNextPrime( result[ middle + i ] )
result[ middle - ( i + 1 ) ] = getPreviousPrime( result[ middle - i ] )

return result

Expand Down Expand Up @@ -986,8 +987,8 @@ def getNthTripleBalancedPrimeList( arg ):
middle = 3

for i in range( 0, 3 ):
p[ middle + i + 1 ] = getPreviousPrime( p[ middle + i ] )
p[ middle - ( i + 1 ) ] = getNextPrime( p[ middle - i ] )
result[ middle + i + 1 ] = getNextPrime( result[ middle + i ] )
result[ middle - ( i + 1 ) ] = getPreviousPrime( result[ middle - i ] )

return result

Expand Down Expand Up @@ -1079,8 +1080,8 @@ def getNthQuadrupleBalancedPrimeList( arg ):
middle = 4

for i in range( 0, 4 ):
p[ middle + i + 1 ] = getPreviousPrime( p[ middle + i ] )
p[ middle - ( i + 1 ) ] = getNextPrime( p[ middle - i ] )
result[ middle + i + 1 ] = getNextPrime( result[ middle + i ] )
result[ middle - ( i + 1 ) ] = getPreviousPrime( result[ middle - i ] )

return result

Expand Down
Loading

0 comments on commit ca451f7

Please sign in to comment.