From 5df10926531efcf84d8d25ab3ef9426701368187 Mon Sep 17 00:00:00 2001 From: Henrik Tidefelt Date: Fri, 11 Dec 2015 22:17:25 +0100 Subject: [PATCH] Refined selection of remainder functions --- source/coreelem.cc | 273 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 233 insertions(+), 40 deletions(-) diff --git a/source/coreelem.cc b/source/coreelem.cc index bc761080..35176970 100644 --- a/source/coreelem.cc +++ b/source/coreelem.cc @@ -52,24 +52,227 @@ namespace Shapes { namespace Lang { - class Core_mod : public Lang::CoreFunction + + class Core_nonNegativeModulo : public Lang::CoreFunction { + Lang::Float::ValueType floatImpl( Lang::Float::ValueType num, Lang::Float::ValueType den ) const + { + Lang::Float::ValueType absDen = fabs( den ); + return num - floor( num / absDen ) * absDen; + } public: - Core_mod( const RefCountPtr< const Ast::NamespacePath > & ns, const char * name ) : CoreFunction( ns, name ) { } + Core_nonNegativeModulo( const RefCountPtr< const Ast::NamespacePath > & ns, const char * name ) + : CoreFunction( ns, name, new Kernel::EvaluatedFormals( Ast::FileID::build_internal( name ), true ) ) + { + formals_->appendEvaluatedCoreFormal( "dividend", Kernel::THE_SLOT_VARIABLE ); + formals_->appendEvaluatedCoreFormal( "divisor", Kernel::THE_SLOT_VARIABLE ); + } virtual void call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const { - const size_t ARITY = 2; - CHECK_ARITY( args, ARITY, id_ ); + args.applyDefaults( ); try { typedef const Lang::Integer ArgType; + Lang::Integer::ValueType num = Helpers::try_cast_CoreArgument< ArgType >( args.getValue( 0 ) )->val_; + Lang::Integer::ValueType den = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 1, callLoc )->val_; + + /* Division num / abs(den) with rounding towards negative infinity. + */ + Lang::Integer::ValueType absDen = (den >= 0) ? den : (-den); + Lang::Integer::ValueType n = 0; /* Initialize to suppress warnings. */ + if (den != 0) { + if (num >= 0) + n = num / absDen; + else + n = -( 1 + (-num - 1) / absDen ); + } else { + throw Exceptions::CoreOutOfRange( id_, args, 1, "The remainder is not defined when the denominator is zero." ); + } + + Kernel::ContRef cont = evalState->cont_; + cont->takeValue( Kernel::ValueRef( new Lang::Integer( num - n * absDen ) ), + evalState ); + return; + } + catch( const NonLocalExit::NotThisType & ball ) + { + /* Wrong type; never mind!.. but see below! + */ + } + + try + { + typedef const Lang::Float ArgType; + RefCountPtr< ArgType > num = Helpers::try_cast_CoreArgument< ArgType >( args.getValue( 0 ) ); + RefCountPtr< ArgType > den = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 1, callLoc ); + + Kernel::ContRef cont = evalState->cont_; + cont->takeValue( Kernel::ValueRef( new Lang::Float( floatImpl( num->val_, den->val_ ) ) ), + evalState ); + return; + } + catch( const NonLocalExit::NotThisType & ball ) + { + /* Wrong type; never mind!.. but see below! + */ + } + + try + { + typedef const Lang::Length ArgType; + RefCountPtr< ArgType > num = Helpers::try_cast_CoreArgument< ArgType >( args.getValue( 0 ) ); + RefCountPtr< ArgType > den = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 1, callLoc ); + + Kernel::ContRef cont = evalState->cont_; + cont->takeValue( Kernel::ValueRef( new Lang::Length( floatImpl( num->getScalar( ), den->getScalar( ) ) ) ), + evalState ); + return; + } + catch( const NonLocalExit::NotThisType & ball ) + { + /* Wrong type; never mind!.. but see below! + */ + } + + throw Exceptions::CoreTypeMismatch( callLoc, id_, args, 0, Helpers::typeSetString( Lang::Integer::staticTypeName( ), Lang::Float::staticTypeName( ), Lang::Length::staticTypeName( ) ) ); + } + }; + + class Core_denominatorSignModulo : public Lang::CoreFunction + { + Lang::Float::ValueType floatImpl( Lang::Float::ValueType num, Lang::Float::ValueType den ) const + { + return num - floor( num / den ) * den; + } + public: + Core_denominatorSignModulo( const RefCountPtr< const Ast::NamespacePath > & ns, const char * name ) + : CoreFunction( ns, name, new Kernel::EvaluatedFormals( Ast::FileID::build_internal( name ), true ) ) + { + formals_->appendEvaluatedCoreFormal( "dividend", Kernel::THE_SLOT_VARIABLE ); + formals_->appendEvaluatedCoreFormal( "divisor", Kernel::THE_SLOT_VARIABLE ); + } + virtual void + call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const + { + args.applyDefaults( ); + + try + { + typedef const Lang::Integer ArgType; + Lang::Integer::ValueType num = Helpers::try_cast_CoreArgument< ArgType >( args.getValue( 0 ) )->val_; + Lang::Integer::ValueType den = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 1, callLoc )->val_; + + /* Division with rounding towards negative infinity. + */ + Lang::Integer::ValueType n = 0; /* Initialize to suppress warnings. */ + if (den > 0) { + if (num >= 0) + n = num / den; + else + n = -( 1 + (-num - 1) / den ); + } else if (den < 0) { + if (num > 0) + n = -( 1 + (num - 1) / (-den) ); + else + n = (-num) / (-den); + } else { + throw Exceptions::CoreOutOfRange( id_, args, 1, "The remainder is not defined when the denominator is zero." ); + } + + Kernel::ContRef cont = evalState->cont_; + cont->takeValue( Kernel::ValueRef( new Lang::Integer( num - n * den ) ), + evalState ); + return; + } + catch( const NonLocalExit::NotThisType & ball ) + { + /* Wrong type; never mind!.. but see below! + */ + } + + try + { + typedef const Lang::Float ArgType; RefCountPtr< ArgType > num = Helpers::try_cast_CoreArgument< ArgType >( args.getValue( 0 ) ); RefCountPtr< ArgType > den = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 1, callLoc ); Kernel::ContRef cont = evalState->cont_; - cont->takeValue( Kernel::ValueRef( new Lang::Integer( num->val_ % den->val_ ) ), + cont->takeValue( Kernel::ValueRef( new Lang::Float( floatImpl( num->val_, den->val_ ) ) ), + evalState ); + return; + } + catch( const NonLocalExit::NotThisType & ball ) + { + /* Wrong type; never mind!.. but see below! + */ + } + + try + { + typedef const Lang::Length ArgType; + RefCountPtr< ArgType > num = Helpers::try_cast_CoreArgument< ArgType >( args.getValue( 0 ) ); + RefCountPtr< ArgType > den = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 1, callLoc ); + + Kernel::ContRef cont = evalState->cont_; + cont->takeValue( Kernel::ValueRef( new Lang::Length( floatImpl( num->getScalar( ), den->getScalar( ) ) ) ), + evalState ); + return; + } + catch( const NonLocalExit::NotThisType & ball ) + { + /* Wrong type; never mind!.. but see below! + */ + } + + throw Exceptions::CoreTypeMismatch( callLoc, id_, args, 0, Helpers::typeSetString( Lang::Integer::staticTypeName( ), Lang::Float::staticTypeName( ), Lang::Length::staticTypeName( ) ) ); + } + }; + + class Core_numeratorSignModulo : public Lang::CoreFunction + { + Lang::Float::ValueType floatImpl( Lang::Float::ValueType num, Lang::Float::ValueType den ) const + { + return fmod( num, den ); + } + public: + Core_numeratorSignModulo( const RefCountPtr< const Ast::NamespacePath > & ns, const char * name ) + : CoreFunction( ns, name, new Kernel::EvaluatedFormals( Ast::FileID::build_internal( name ), true ) ) + { + formals_->appendEvaluatedCoreFormal( "dividend", Kernel::THE_SLOT_VARIABLE ); + formals_->appendEvaluatedCoreFormal( "divisor", Kernel::THE_SLOT_VARIABLE ); + } + virtual void + call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const + { + args.applyDefaults( ); + + try + { + typedef const Lang::Integer ArgType; + Lang::Integer::ValueType num = Helpers::try_cast_CoreArgument< ArgType >( args.getValue( 0 ) )->val_; + Lang::Integer::ValueType den = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 1, callLoc )->val_; + + /* Division with rounding towards zero. + */ + Lang::Integer::ValueType n = 0; /* Initialize to suppress warnings. */ + if (den > 0) { + if (num >= 0) + n = num / den; + else + n = -( (-num) / den ); + } else if (den < 0) { + if (num >= 0) + n = -( num / (-den) ); + else + n = (-num) / (-den); + } else { + throw Exceptions::CoreOutOfRange( id_, args, 1, "The remainder is not defined when the denominator is zero." ); + } + + Kernel::ContRef cont = evalState->cont_; + cont->takeValue( Kernel::ValueRef( new Lang::Integer( num - n * den ) ), evalState ); return; } @@ -86,7 +289,7 @@ namespace Shapes RefCountPtr< ArgType > den = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 1, callLoc ); Kernel::ContRef cont = evalState->cont_; - cont->takeValue( Kernel::ValueRef( new Lang::Float( fmod( num->val_, den->val_ ) ) ), + cont->takeValue( Kernel::ValueRef( new Lang::Float( floatImpl( num->val_, den->val_ ) ) ), evalState ); return; } @@ -103,7 +306,7 @@ namespace Shapes RefCountPtr< ArgType > den = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 1, callLoc ); Kernel::ContRef cont = evalState->cont_; - cont->takeValue( Kernel::ValueRef( new Lang::Length( fmod( num->getScalar( ), den->getScalar( ) ) ) ), + cont->takeValue( Kernel::ValueRef( new Lang::Length( floatImpl( num->getScalar( ), den->getScalar( ) ) ) ), evalState ); return; } @@ -130,6 +333,8 @@ namespace Shapes typedef const Lang::Float ArgType; RefCountPtr< ArgType > arg = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 0, callLoc ); + /* Warning, unhandled overflow in the static_cast from floating point to integral type. + */ Kernel::ContRef cont = evalState->cont_; cont->takeValue( Kernel::ValueRef( new Lang::Integer( static_cast< Lang::Integer::ValueType >( floor( arg->val_ ) ) ) ), evalState ); @@ -149,16 +354,18 @@ namespace Shapes typedef const Lang::Float ArgType; RefCountPtr< ArgType > arg = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 0, callLoc ); + /* Warning, unhandled overflow in the static_cast from floating point to integral type. + */ Kernel::ContRef cont = evalState->cont_; cont->takeValue( Kernel::ValueRef( new Lang::Integer( static_cast< Lang::Integer::ValueType >( ceil( arg->val_ ) ) ) ), evalState ); } }; - class Core_rint : public Lang::CoreFunction + class Core_round : public Lang::CoreFunction { public: - Core_rint( const RefCountPtr< const Ast::NamespacePath > & ns, const char * name ) : CoreFunction( ns, name ) { } + Core_round( const RefCountPtr< const Ast::NamespacePath > & ns, const char * name ) : CoreFunction( ns, name ) { } virtual void call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const { @@ -168,8 +375,12 @@ namespace Shapes typedef const Lang::Float ArgType; RefCountPtr< ArgType > arg = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 0, callLoc ); + /* Warning, there are two sources of unhandled overflow here. + * 1) The range of a double is much greater than the range of a long (the result of lround). + * 2) lround produces a long, while Lang::Integer::ValueType is currently int. + */ Kernel::ContRef cont = evalState->cont_; - cont->takeValue( Kernel::ValueRef( new Lang::Integer( static_cast< Lang::Integer::ValueType >( rint( arg->val_ ) ) ) ), + cont->takeValue( Kernel::ValueRef( new Lang::Integer( lround( arg->val_ ) ) ), evalState ); } }; @@ -480,6 +691,11 @@ namespace Shapes typedef const Lang::Float ArgType; RefCountPtr< ArgType > arg = Helpers::down_cast_CoreArgument< ArgType >( id_, args, 0, callLoc ); + if( arg->val_ < 0.0 ) + { + throw Exceptions::CoreOutOfRange( id_, args, 0, "The square root is not defined for negative values." ); + } + Kernel::ContRef cont = evalState->cont_; cont->takeValue( Kernel::ValueRef( new Lang::Float( sqrt( arg->val_ ) ) ), evalState ); @@ -995,30 +1211,6 @@ namespace Shapes } }; - class Core_randomNatural : public Lang::CoreFunction - { - public: - Core_randomNatural( const RefCountPtr< const Ast::NamespacePath > & ns, const char * name ) - : CoreFunction( ns, name, new Kernel::EvaluatedFormals( Ast::FileID::build_internal( name ), true ) ) - { - formals_->appendCoreStateFormal( "state" ); - } - virtual void - call( Kernel::EvalState * evalState, Kernel::Arguments & args, const Ast::SourceLocation & callLoc ) const - { - args.applyDefaults( ); - - typedef Kernel::WarmRandomState StateType; - StateType * st = Helpers::down_cast_CoreState< StateType >( id_, args, 0, callLoc ); - - st->setState( ); - - Kernel::ContRef cont = evalState->cont_; - cont->takeValue( Kernel::ValueRef( new Lang::Float( TWO_DIV_RANDOM_MAX * static_cast< double >( random( ) ) - 1 ) ), - evalState ); - } - }; - class Core_randomBall1D : public Lang::CoreFunction { public: @@ -1138,10 +1330,12 @@ RefCountPtr< const Lang::CoreFunction > Lang::THE_FUNCTION_ABS( new Lang::Core_a void Kernel::registerCore_elem( Kernel::Environment * env ) { - env->initDefineCoreFunction( new Lang::Core_mod( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "mod" ) ); + env->initDefineCoreFunction( new Lang::Core_nonNegativeModulo( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "modulo" ) ); + env->initDefineCoreFunction( new Lang::Core_denominatorSignModulo( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "mod" ) ); + env->initDefineCoreFunction( new Lang::Core_numeratorSignModulo( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "rem" ) ); env->initDefineCoreFunction( new Lang::Core_ceil( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "ceil" ) ); env->initDefineCoreFunction( new Lang::Core_floor( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "floor" ) ); - env->initDefineCoreFunction( new Lang::Core_rint( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "round" ) ); + env->initDefineCoreFunction( new Lang::Core_round( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "round" ) ); env->initDefineCoreFunction( new Lang::Core_cos( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "cos" ) ); env->initDefineCoreFunction( new Lang::Core_sin( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "sin" ) ); env->initDefineCoreFunction( new Lang::Core_tan( Lang::THE_NAMESPACE_Shapes_Numeric_Math, "tan" ) ); @@ -1159,10 +1353,9 @@ Kernel::registerCore_elem( Kernel::Environment * env ) env->initDefineCoreFunction( new Lang::Core_cross( Lang::THE_NAMESPACE_Shapes_Geometry, "cross" ) ); env->initDefineCoreFunction( new Lang::Core_orthogonal( Lang::THE_NAMESPACE_Shapes_Geometry, "orthogonal" ) ); - env->initDefineCoreFunction( new Lang::Core_randomNatural( Lang::THE_NAMESPACE_Shapes_Numeric_Random, "randomN" ) ); - env->initDefineCoreFunction( new Lang::Core_randomBall1D( Lang::THE_NAMESPACE_Shapes_Numeric_Random, "random1D" ) ); - env->initDefineCoreFunction( new Lang::Core_randomBall2D( Lang::THE_NAMESPACE_Shapes_Numeric_Random, "random2D" ) ); - env->initDefineCoreFunction( new Lang::Core_randomBall3D( Lang::THE_NAMESPACE_Shapes_Numeric_Random, "random3D" ) ); + env->initDefineCoreFunction( new Lang::Core_randomBall1D( Lang::THE_NAMESPACE_Shapes_Numeric_Random, "ball1D" ) ); + env->initDefineCoreFunction( new Lang::Core_randomBall2D( Lang::THE_NAMESPACE_Shapes_Numeric_Random, "ball2D" ) ); + env->initDefineCoreFunction( new Lang::Core_randomBall3D( Lang::THE_NAMESPACE_Shapes_Numeric_Random, "ball3D" ) ); env->initDefineCoreFunction( new Lang::Core_gensym( Lang::THE_NAMESPACE_Shapes, "gensym" ) ); } -- 2.11.4.GIT