package org.maths
{
import org.maths.Fraction;
import flash.events.EventDispatcher;
import flash.events.Event;
public class CFraction extends EventDispatcher
{
public var parts:Array;
public var sign:int = 1;
function CFraction() {
parts = [];
}
protected function clone():CFraction {
var f:CFraction = new CFraction();
for(var i:int = 0; i < parts.length; i++) {
var s:Fraction = parts[i] as Fraction;
f.parts[i] = new Fraction(s.p, s.q);
}
return f;
}
public function add(f:Fraction):void {
var end:int = 0;
if(parts.length < 1)
return; var g:Fraction = f.clone();
if(sign < 0)
g.negate();
parts[end] = (parts[end] as Fraction).plus(g);
normaliseSign();
notifyChange();
}
public function negate():void {
sign = -sign;
normaliseSign();
}
private function invertSigns():void {
for(var i:int = 0; i < parts.length; i++)
(parts[i] as Fraction).negate();
}
public function unshift(f:Fraction):void {
var g:Fraction = f.clone();
if(sign < 0) g.negate();
parts.unshift(g);
normaliseSign();
notifyChange();
}
public function getPartAt(i:int):Fraction {
return parts[i] as Fraction;
}
public function evaluate(n:int = 0):Fraction {
if(parts.length == 0)
return new Fraction(1,0);;
var g:Fraction = getPartAt(n);
var f:Fraction = new Fraction(g.p,g.q);
if(n == parts.length-1) {
return f;
}
else {
var h:Fraction = evaluate(n+1).reciprocal;
return f.plus(h);
}
}
public function parse(text:String):void {
parts = [];
text = text.replace(/(\[|\]| )*/,"");
var sparts:Array = text.split(",");
for(var i:int = 0; i < sparts.length; i++) {
var s:String = (sparts[i] as String);
var pq:Array = s.split("/");
var pp:int;
switch(pq.length) {
case 1: {
pp = pq[0];
if(isNaN(pp))
throw new Error("Bad Continued Fraction: " + s);
unshift(new Fraction(pp,1));
break;
}
case 2: {
pp = pq[0] as int;
if(isNaN(pp))
throw new Error("Bad Continued Fraction: " + s);
var qq:int = pq[1];
if(isNaN(qq))
throw new Error("Bad Continued Fraction: " + s);
unshift(new Fraction(pp,qq));
break;
}
default:
throw new Error("Bad Continued Fraction: " + s);
}
}
notifyChange();
}
public function simplify():void {
var i:int;
var f:Fraction;
var g:Fraction;
var a:Fraction;
var b:Fraction;
if(parts.length < 2) return;
for(i = 1; i >= 1 && i+1 < parts.length; i++) {
while(parts[i]==0 && i>=1 && i+1 < parts.length) {
parts[i-1] = (parts[i-1] as Fraction).plus(parts[i+1] as Fraction);
parts.splice(i--,2);
}
}
if(parts.length < 2) {
notifyChange();
return;
}
if((parts[parts.length-1] as Fraction).p==0) {
parts.pop();
parts.pop();
trace("R1");
notifyChange();
return;
}
f = (parts[parts.length-1] as Fraction);
f.simplify();
if((f.p==1 || f.p==-1) && f.q==1) {
parts.pop();
parts[parts.length-1] = (parts[parts.length-1] as Fraction).plus(f);
trace("R2");
notifyChange();
return;
}
f = (parts[parts.length-1] as Fraction);
f.simplify();
if(f.p < 0) {
sign = -sign;
invertSigns();
}
for(i = parts.length-2; i >= 0; i--) {
f = parts[i] as Fraction;
f.simplify();
if(f.p >= 0) continue;
if(f.p==-1 && i > 0) {
g = parts[i-1] as Fraction;
g.simplify();
var a0:Array;
if(g.p == 1) {
a0 = parts.splice(0,i); a0.pop();
a0.push(new Fraction(0,1)); parts.shift(); invertSigns(); b = parts[0] as Fraction;
b.simplify();
b.p += b.q; parts = a0.concat(parts);
trace("R3 special 1");
notifyChange();
return;
}
g = parts[i+1] as Fraction;
g.simplify();
if(g.p == 1) {
a0 = parts.splice(0,i+1); a0.pop();
a0.push(new Fraction(0,1)); parts.shift(); invertSigns(); b = parts[0] as Fraction;
b.simplify();
b.p -= b.q; parts = a0.concat(parts);
trace("R3 special 2");
notifyChange();
return;
}
}
b = f;
var insert:Array = [];
if(i == 0) {
if(parts.length == 2) {
sign = -sign;
a = (parts[0] as Fraction);
a.p = -a.p; a.p -= a.q;
b = (parts[1] as Fraction); b.p -= b.q;
parts = [a,new Fraction(1,1), b]
trace("R3 @0 length 2");
notifyChange();
return;
}
else {
sign = -sign;
var ab:Array = parts.splice(0,2); a = ab[0] as Fraction;
a.negate();
if(a.p < 0) throw new Error("unexpected -a"); b = ab[1] as Fraction;
if(b.p < 0) throw new Error("unexpected -b"); a.p -= a.q; b.p -= b.q parts = [a, new Fraction(1,1), b].concat(parts);
trace("R3 @0");
notifyChange();
return;
}
}
b.negate();
b.p -= b.q;
var c:Fraction = parts[i+1] as Fraction;
c.p -= c.q;
insert = [new Fraction(1,1), c];
for(var j:int = i-1; j >= 0; j--) {
a = parts[j] as Fraction;
a.simplify();
if(a.p < 0) {
insert.unshift(b);
b = a;
}
else {
b.p -= b.q;
insert.unshift(b);
insert.unshift(new Fraction(1,1));
a.p -= a.q;
insert.unshift(a);
break;
}
}
parts.splice(j,i-j+2);
for(var k:int = 0; k < insert.length; k++) {
parts.splice(j+k,0,insert[k]);
}
trace("R3 general");
break;
}
notifyChange();
return;
}
public function normaliseSign():void {
for(var i:int=0; i < parts.length; i++) {
var a:Fraction = parts[i] as Fraction;
a.simplify();
if(a.p > 0)
break;
else if(a.p < 0) {
sign = -sign;
invertSigns();
break;
}
}
}
override public function toString():String {
var s:String = sign>0 ? "[" : "-[";
var sep:String = "";
for(var i:int=0; i < parts.length; i++) {
var f:Fraction = getPartAt(i);
s += (sep + f);
sep = ",";
}
var n:Fraction = evaluate();
n.negate();
return s + "]=" + (((sign > 0) ? evaluate() : n)) ;
}
public function notifyChange():void {
normaliseSign();
dispatchEvent(new Event("cfracChanged"));
}
public function get text():String {
return toString();
}
public function set text(s:String):void {
parse(s);
}
}
}