All Downloads are FREE. Search and download functionalities are using the official Maven repository.

expr.Fraction.scala Maven / Gradle / Ivy

The newest version!
package galileo.expr

import galileo.environment.Environment
import galileo.linalg.Matrix
import galileo.proof.Conversion
import galileo.trigonometry.{CosF1, SinF1}

case class Fraction(numerator:Expr, denominator:Expr) extends FunF2 {
	val a = numerator
	val b = denominator 
	override def toString() = numerator.factorToString() + "/" + denominator.denominatorToString()
	override def denominatorToString() = factorToString()
	override def factorToString() = toString() //"(" + toString() + ")"
	override def toStringWithSign():String = "+" + toString()
	override def info(env:Option[Environment]=None) = "Fraction(" + numerator + "," + denominator + ")" 

	def conversions(depth:Int):List[Conversion] = {
		var rv:List[Conversion] = List()
		rv = rv :+ Conversion( "Denominator move to numerator", Product( this.numerator, Power( this.denominator, Number( -1 ) ) ) )
		rv = rv :+ Conversion( "Normalize", this.visit() )
		this.numerator match {
			case f:Fraction => { 
				val p = Product( f.denominator, this.denominator )
				rv = rv :+ Conversion( "Fraction denominator is moved", Fraction( f.numerator, p ) )
				
				//for( c <- p.conversions( depth - 1 ) )
				//	rv = rv :+ Conversion( )

			}
			case Number( 1 ) => rv = rv :+ Conversion( "1/x->x^(-1)", Power( denominator, Number( -1 ) ) )
			//case Power(o,e) => rv = rv :+ Conversion( "Denominator move to n")
			case _ => None
		}
		return rv
	}

	// Todo:
	// * Before visiting num and den, look for common factors and eliminate them
	override def visit( env:Option[Environment] = None ):Expr = ( numerator.visit( env ), denominator.visit( env ) ) match {
		case (Number(0),Number(0)) => Fraction( Number(0), Number(0)) // todo, apply limits, l'hopital?
		case (a:Expr,b:Expr) if ( a == b ) => Number( 1 )
		case (a, Number( 0 )) => Fraction( a, Number( 0 ) )
		case (Number(0),_) => Number( 0 )
		case (m:Matrix, e ) => ( m * Fraction( Number( 1 ), e ) ).visit()
		
		// some common, relatively short cases that we can explicity map
		// The simplify method finds common factors in more complex fractions already
		case (Product(a,b),c) if ( a == c ) => b
		case (Product(a,b),c) if ( -a == c ) => (-b)
		case (Product(a,b),c) if ( b == c ) => a
		case (a:Expr,Product(b,c)) if ( a == b ) => Fraction( Number( 1 ), c ).visit( env )
		case (a:Expr,Product(b,c)) if ( a == c ) => Fraction( Number( 1 ), b ).visit( env )
		case (a:Expr,Product(b,c)) if ( -a == b ) => Fraction( Number( -1 ), c ).visit( env )
		case (a:Expr,Product(b,c)) if ( -a == c ) => Fraction( Number( -1 ), b ).visit( env )

		//case (Product(a,b),c) if ( a == c ) => b
		


		case (Product(a,b),Product(c,d)) if (a == c ) => Fraction( b, d ).visit( env )
		case (Product(a,b),Product(c,d)) if (a == d ) => Fraction( b, c ).visit( env )
		case (Product(a,b),Product(c,d)) if (b == c ) => Fraction( a, d ).visit( env )
		case (Product(a,b),Product(c,d)) if (b == d ) => Fraction( a, c ).visit( env )

		case (Product(a,b),Product(c,d)) if (-a == c ) => Fraction( -b, d ).visit( env )
		case (Product(a,b),Product(c,d)) if (-a == d ) => Fraction( -b, c ).visit( env )
		case (Product(a,b),Product(c,d)) if (-b == c ) => Fraction( -a, d ).visit( env )
		case (Product(a,b),Product(c,d)) if (-b == d ) => Fraction( -a, c ).visit( env )	

		// Don't move from denominator to numerator just because
		// But we can move this: a/b^(-n) -> a*b^n
		case ( a:Expr, Power( b:Expr, Number( c ) ) ) if ( c < 0 ) => Product( a, Power( b, Number( -c ) ) ).visit( env )
	
		case ( Fraction( a, b ), Fraction( c, d ) ) if ( b == d ) => Fraction( a, c ).visit() //Product( a, d ), Product( b, c ) ).visit()
		case ( Fraction( a, b ), Fraction( c, d ) ) if ( a == c ) => Fraction( d, b ).visit() //Product( a, d ), Product( b, c ) ).visit()
		case ( Fraction( a, Number( b ) ), Fraction( c, Number( d ) ) ) if ( d % b == 0 ) => Fraction( Product( Number( d / b ), a ), c ).visit()
		case ( Fraction( Number( a ), b ), Fraction( Number( c ), d ) ) if ( a % c == 0 ) => Fraction( Product( Number( a / c ), d ), b ).visit()
		case ( Fraction( a, b ), Fraction( c, d ) ) => Fraction( Product( a, d ), Product( b, c ) ).visit()
		case ( Fraction( a, b), c:Expr ) => Fraction( a, Product( b, c ) ).visit()
		case ( a, Fraction( b, c ) ) => Fraction( Product( a, c ), b ).visit()
		
		//case (Number( a ), Number( b )) if ( a == b ) => Number( 1 ) //Number( a / b ) 
		//case (Number( a ), Number( b )) => Number( a / b ) 
		case ( a, Number( 1 ) ) => a
		//case ( Number( 1 ), a ) => Fraction( Number( 1 ), a )
		case ( a:Expr, b:Expr ) if ( a == b ) => Number( 1 )
		case ( Number( a ), Number( b ) ) if ( a > 0 && b < 0) => Fraction( Number( -a ), Number( -b ) ).visit()
		case ( Number( a ), Number( b ) ) if ( a < 0 && b < 0) => Fraction( Number( -a ), Number( -b ) ).visit()
		case ( Number( a ), Number( b ) ) if ( a % b == 0 ) => Number( a / b )
		case ( Number( a ), Number( b ) ) if ( b % a == 0 && b*a>0) => Fraction( Number(  1 ), Number(  b / a ) )
		case ( Number( a ), Number( b ) ) if ( b % a == 0 && b*a<0) => Fraction( Number( -1 ), Number( -b / a ) )

		// this seems to be too agressive	
		// case ( Number( 1 ), Sum( Fraction( a, b ), c ) ) => Fraction( b, Sum( a, Product( b,c ) ) ).visit()

		case ( a, Sum( b, Fraction( c, d ) ) ) => Fraction( Product( a, d ), Sum( Product( b, d ), c ) ).visit()
		case ( a, Sum( Fraction( b, c ), d ) ) => Fraction( Product( a, c ), Sum( b, Product( c, d ) ) ).visit()

		//case ( Product( Number( a ), b ), s:Sum ) => Number( a )
		//case (n:Product,d:Product) if( Product.commonExpressions( n , d ) ) => n.
		case (a:Expr, b:Expr) => Fraction( a, b )
	}

	override def eval():Expr = ( numerator.eval(), denominator.eval() ) match {
		//case ( Number( a ), Number)
		case ( Number( a ), Number( b ) ) => Number( a / b )
		case ( a:Expr, b:Expr ) => Fraction( a, b )
	}

	override def simplify:Expr = {
		//val pf = numerator.possibleFactors
		var nn = numerator.simplify
		var nd = denominator.simplify
		for( possibleFactor <- numerator.possibleFactors ++ denominator.possibleFactors ) {
			//println( "possibleFactor:" + possibleFactor )
			( nn.extractFactor( possibleFactor ), nd.extractFactor( possibleFactor ) ) match {
				case (Some(a),Some(b)) => { nn = a; nd = b }
				case _ => Nil
			}
		}
		// negative numbers in denominator
		val negd = nd.flatFactors.collect( { case Number( n ) if n < 0 => n } )
		negd.size match { 
			case 1 => { nn = Product( Number( -1 ), nn ); nd = Product( Number( -1 ), nd ) }
			case 0 => Nil 
		}

		nd.visit() match {
			case Number( 1 ) => nn.visit()
			case Number( -1 ) => Product( Number( -1 ), nn ).visit()
			// very specific trigonometry cases
			case Product( Number( 2 ), Power( SinF1(psi:Expr), Number( 2 ) ) ) => {
				nn.visit() match {
					case s:Sum => s.flatTerms match {
						case ( Power( CosF1( psi2:Expr ), Number( 2 ) ) :: Product( Number( -1 ), Power( SinF1( psi3:Expr ), Number( 2 ) ) ) :: Number( -1 ) :: Nil ) if ( psi == psi2 && psi == psi3 ) => Number( -1 )
						case _ => Fraction( nn, nd ) // no visit here - cycle
					}
					case _ => Fraction( nn, nd ) // no visit here - cycle
				}
			}

			case Power( SinF1(psi:Expr), Number( 2 ) ) => {
				nn.visit() match {
					case s:Sum => s.flatTerms match {
						// (((-1.0)*(cos(psi)^2.0-1.0*sin(psi)^2.0)+1.0)/sin(psi)^2.0 -> 2
						case Product( Number( -1 ), Sum( Power( CosF1( psi2:Expr ), Number( 2 ) ), Product( Number( -1 ), Power( SinF1( psi3:Expr ), Number( 2 ) ) ) ) ) :: Number( 1 ) :: Nil  if ( psi == psi2 && psi == psi3 ) => Number( 2 )
						case Product( Number( -4 ), Power( CosF1( psi2:Expr ), Number( 2 ) ) ) :: Number( 4 ) :: Nil  if ( psi == psi2 ) => Number( 4 )
						case Product( Number( -6 ), Power( CosF1( psi2:Expr ), Number( 2 ) ) ) :: Number( 6 ) :: Nil  if ( psi == psi2 ) => Number( 6 )
						case ( Power( CosF1( psi2:Expr ), Number( 2 ) ) :: Product( Number( -1 ), Power( SinF1( psi3:Expr ), Number( 2 ) ) ) :: Number( -1 ) :: Nil ) if ( psi == psi2 && psi == psi3 ) => Number( -2 )
						case _ => Fraction( nn, nd )//.visit() // no visit here - cycle
					}
					case _ => Fraction( nn, nd ) //.visit() // no visit here - cycle
				}
			}
		
			case Number( 2 ) => {
				nn.visit() match {
					case s:Sum => s.flatTerms match {
						case ( Product( Number( -1 ), Power( CosF1( psi1:Expr ), Number( 2 ) ) ) :: Product( Number( 5 ), Power( SinF1( psi2:Expr ), Number( 2 ) ) ) :: Number( 1 ) :: Nil ) if ( psi1 == psi2 ) => Product( Number( 3 ), Power( SinF1( psi1 ), Number( 2 ) ) )
						case ( Power( CosF1( psi1:Expr ), Number( 2 ) ) :: Product( Number( -5 ), Power( SinF1( psi2:Expr ), Number( 2 ) ) ) :: Number( 1 ) :: Nil ) if ( psi1 == psi2 ) => Sum( Power( CosF1( psi1 ), Number( 2 ) ), Product( Number( -2 ), Power( SinF1( psi1 ), Number( 2 ) ) ) )
						case ( Power( CosF1( psi1:Expr ), Number( 2 ) ) :: Product( Number( 3 ), Power( SinF1( psi2:Expr ), Number( 2 ) ) ) :: Number( 1 ) :: Nil ) if ( psi1 == psi2 ) => Sum( Power( CosF1( psi1 ), Number( 2 ) ), Product( Number( 2 ), Power( SinF1( psi1 ), Number( 2 ) ) ) )
						case _ => Fraction( nn, nd ).visit()
					}
					case _ => Fraction( nn, nd ).visit()
				}
			}
			
			case _ => Fraction( nn,nd ).visit()		
		}
		// Move negative numbers to numerator
	}

	// simplify by finding common factors in the numerator and denominator of a fraction
	def simplifyOld:Expr = {
		// simplify things with simple num/denom, like a^2/a^3
		def simplifyOne(f:Fraction):Option[Fraction] = (f.numerator,f.denominator) match {
			case (a:Expr,b:Expr) if ( a == b ) => Some( Fraction( Number( 1 ), Number( 1 ) ) )
			case (Number(a),Number(b)) if ( a % b == 0 ) => Some( Fraction( Number( a / b ), Number( 1 ) ) )
			case (Number(a),Number(b)) if ( b % a == 0 ) => Some( Fraction( Number( 1 ), Number( b / a ) ) )
			case (Number(a),Number(b)) if ( -a == b ) => Some( Fraction( Number( -1 ), Number( 1 ) ) )
			case (a:Expr,Power(b,c)) if ( a == b && c != Number( 1 ) ) => Some( Fraction( Number( 1 ), Power( b, Sum( c, Number( -1 ) ).visit() ) ) )
			case (Power(a,b),c:Expr) if ( a == c && b != Number( 1 ) ) => Some( Fraction( Power( a, Sum( b, Number( -1 ) ).visit() ), Number( 1 ) ) )
			case (Power(a,b:Number),Power(c,d:Number)) if ( a == c && d.value > b.value ) => Some( Fraction( Number( 1 ), Power( a, Diff( d, b ) ) ) )
			case (Power(a,b),Power(c,d)) if ( a == c ) => Some( Fraction( Power( a, Diff( b, d ) ), Number( 1 ) ) )
			case (s:Sum,d:Expr) => s.extractFactor( d ) match {
				case Some(r) => Some( Fraction( r, Number( 1 ) ) ) // s = r * d, so simplify r * d / d to r
 				case None => None
			}
			case (p:Product,d:Expr) => p.extractFactor( d ) match {
				case Some(r) => Some( Fraction( r, Number( 1 ) ) ) // p = r * d, so simplify r * d / d to r
 				case None => None
			}
			case _ => None
		}

		val n = numerator.simplify.flatFactors
		val d = denominator.simplify.flatFactors

		var newn:List[Expr] = n
		var newd:List[Expr] = d
		var indn = 0 // numerator index
		var indd = 0 // denominator index

		// loop over all factors in numerator and denominator
		while( indn < newn.size && indd < newd.size ) {
			val candn = newn(indn)
			val candd = newd(indd)
			//println( "candn:" + candn.info() )
			//println( "candd:" + candd.info() )
			//println( "Calling simplyOne on:" + candn + "/" + candd )
			simplifyOne( Fraction( candn,candd ) ) match {
				case Some(newf) => { 
					//println( "Fraction" + Fraction(candn,candd) + " simplified to " + newf )
					newn = newn.updated( indn, newf.numerator )
					newd = newd.updated( indd, newf.denominator )
					indd = indd + 1
				}
				case None => indd = indd+1
			}
			
			// loop over all in indd first, then indn again
			if( indd >= newd.size ) {
				indn = indn+1
				indd = 0
			}
		}
		// then do a final sweep to clean up all remaining factor 'Number(1)' etc.
		Fraction(Product(newn),Product(newd)).visit()
	}

	override def expand = Fraction( numerator.expand, denominator.expand ).visit()

	override def extractFactor(possibleFactor:Expr):Option[Expr] = possibleFactor match {
		case Fraction(n,d) => (numerator.extractFactor(n),denominator.extractFactor(d)) match {
			case (Some(nn),Some(nd)) => Some( Fraction(nn,nd) )
			case _ => None
		}
		case e:Expr => (numerator.extractFactor( e ) ) match {
			case Some(nn) => Some(Fraction(nn,denominator))
			case _ => None
		}
	}

	override def possibleFactors = List( this ) ++ numerator.possibleFactors ++ denominator.possibleFactors.map( x => Fraction( Number( 1 ), x ) )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy