Closed flideros closed 8 years ago
@flideros Please edit the issue following the template in ISSUE_TEMPLATE.md, with exact repro steps, and actual/expected output. If possible, please also trim down the problem to a minimal reproducible example. It will normally be possible to reproduce problems with just a few lines of code.
We won't be able to look at this until the issue is re-edited using the issue template. But we really also need a smaller repro.
@dsyme I tried to repro with a simple function but without success. All the source code is in the attached zip.
@flideros Normally you create a minimal repro by starting with the large one, and repeatedly chopping out seemingly irrelevant code, until just before the problem either no longer repros. For example, you might chop out the let powers ...
and replace it by a let powers = constant
or the like. And gradually remove all the prelude code leading up the the problem, again until the problem no longer repros.
I notice you call _degreeGPE u xList
twice in the second variant too - if there are side effects somewhere in the code this could explain the different - though I suspect there aren't.
@dsyme I changed the second variant to:
let degreeGPE u xList =
let x = _degreeGPE u xList
match x = NumberType.Zero with | true -> Undefined | false -> x
and it still works. Let me try and take the code to bare bones and see if it still behaves the same.
Here is the repro code...
type Constant =
| E
| I
| Pi
type Symbol =
| Constant of Constant
| Variable of string
type Function =
| Equals | GreaterThan | LessThan //Binary -> Relational
| Sum | Plus | Positive // n-Ary, Binary, Unary -> Algebraic
| Minus | Negative // Binary, Unary -> Algebraic
| Product | Times // n-Ary, Binary -> Algebraic
| Factorial
| ToThePowerOf
| Sin | Cos | Tan
| FracOp
type Fraction =
{numerator:System.Numerics.BigInteger; denominator:System.Numerics.BigInteger}
with
static member Zero = {numerator = 0I; denominator = 1I}
[<StructuralEquality;NoComparison>]
type NumberType =
| Complex of System.Numerics.Complex
| Rational of Fraction
| Integer of System.Numerics.BigInteger //System.Numerics.BigInteger
| Real of float
| PositiveInfinity
| NegativeInfinity
| ComplexInfinity
| Undefined
type NumberType with
member this.isNegative =
match this with
| Complex x when x.Real < 0.0 -> true
| Rational x when x.numerator < 0I -> true
| Integer x when x < 0I -> true
| Real x when x < 0.0 -> true
| NegativeInfinity -> true
| _ -> false
static member hcf x y =
let rec hcf a b =
match a = 0I, a<b with
| true, _ -> b
| false, true -> hcf a (b - a)
| false, false -> hcf (a - b) b
hcf x y
static member compareNumbers x y =
match x,y with
| Integer x, Integer y when x > y -> 1
| Integer x, Integer y when x = y -> 0
| Integer x, Integer y when x < y -> -1
| Rational x, Rational y when y.numerator * x.denominator > x.numerator * y.denominator -> -1
| Rational x, Rational y when y.numerator * x.denominator < x.numerator * y.denominator -> 1
| Rational x, Rational y when y.numerator * x.denominator = x.numerator * y.denominator -> 0
| Integer x, Rational y when y.numerator > x * y.denominator -> -1
| Integer x, Rational y when y.numerator < x * y.denominator -> 1
| Integer x, Rational y when y.numerator = x * y.denominator -> 0
| Rational x, Integer y when x.numerator > y * x.denominator -> 1
| Rational x, Integer y when x.numerator < y * x.denominator -> -1
| Rational x, Integer y when x.numerator = y * x.denominator -> 0
| _ -> 0
static member One = Integer 1I
static member Zero = Integer 0I
static member (~-) x =
match x with
| Complex x -> Complex (-x)
| Integer x -> Integer (-x)
| Rational f -> Rational {f with numerator = -f.numerator}
| PositiveInfinity -> NegativeInfinity
| NegativeInfinity -> PositiveInfinity
| _ -> Undefined //Use until all defenitions are made
static member (+) (x, y) =
match x, y with
| Complex x, Complex y -> Complex (x + y)
| Integer x, Integer y -> Integer (x + y)
| Rational f1, Rational f2 ->
let nTemp = f1.numerator * f2.denominator + f2.numerator * f1.denominator
let dTemp = f1.denominator * f2.denominator
let hcfTemp = NumberType.hcf (abs nTemp) dTemp
match dTemp / hcfTemp = 1I with
| true -> Integer (nTemp / hcfTemp)
| false -> Rational { numerator = nTemp / hcfTemp; denominator = dTemp / hcfTemp }
| Rational f1, Integer i2 ->
let nTemp = f1.numerator + i2 * f1.denominator
let dTemp = f1.denominator
let hcfTemp = NumberType.hcf (abs nTemp) dTemp
match dTemp / hcfTemp = 1I with
| true -> Integer (nTemp / hcfTemp)
| false -> Rational { numerator = nTemp / hcfTemp; denominator = dTemp / hcfTemp }
| Integer i2, Rational f1 ->
let nTemp = f1.numerator + i2 * f1.denominator
let dTemp = f1.denominator
let hcfTemp = NumberType.hcf (abs nTemp) dTemp
match dTemp / hcfTemp = 1I with
| true -> Integer (nTemp / hcfTemp)
| false -> Rational { numerator = nTemp / hcfTemp; denominator = dTemp / hcfTemp }
| PositiveInfinity, PositiveInfinity -> PositiveInfinity
| NegativeInfinity, NegativeInfinity -> NegativeInfinity
| NegativeInfinity, PositiveInfinity -> Undefined
| PositiveInfinity, NegativeInfinity -> Undefined
| _ -> Undefined //Use until all defenitions are made
static member (*) (x, y) =
match x, y with
| Complex x, Complex y -> Complex (x * y)
| Integer x, Integer y -> Integer (x * y)
| Rational f1, Rational f2 ->
let nTemp = f1.numerator * f2.numerator
let dTemp = f1.denominator * f2.denominator
let hcfTemp = NumberType.hcf (abs nTemp) dTemp
match dTemp / hcfTemp = 1I with
| true -> Integer (nTemp / hcfTemp)
| false -> Rational { numerator = nTemp / hcfTemp; denominator = dTemp / hcfTemp }
| Rational f1, Integer i2 ->
let nTemp = f1.numerator * i2 * f1.denominator
let dTemp = f1.denominator * f1.denominator
let hcfTemp = NumberType.hcf (abs nTemp) dTemp
match dTemp / hcfTemp = 1I with
| true -> Integer (nTemp / hcfTemp)
| false -> Rational { numerator = nTemp / hcfTemp; denominator = dTemp / hcfTemp }
| Integer i2, Rational f1 ->
let nTemp = f1.numerator * i2 * f1.denominator
let dTemp = f1.denominator * f1.denominator
let hcfTemp = NumberType.hcf (abs nTemp) dTemp
match dTemp / hcfTemp = 1I with
| true -> Integer (nTemp / hcfTemp)
| false -> Rational { numerator = nTemp / hcfTemp; denominator = dTemp / hcfTemp }
| PositiveInfinity, PositiveInfinity -> PositiveInfinity
| NegativeInfinity, NegativeInfinity -> NegativeInfinity
| NegativeInfinity, PositiveInfinity -> Undefined
| PositiveInfinity, NegativeInfinity -> Undefined
| _ -> Undefined //Use until all defenitions are made
static member Pow (x, y) =
match x, y with
| Complex x, Complex y -> Complex (x ** y)
| Integer x, Integer y when y >= 0I-> Integer (x ** int y)
| Integer x, Integer y when y < 0I->
Rational {numerator = 1I * System.Numerics.BigInteger x.Sign; denominator = ((abs x) ** int (abs y))}
| Rational r, Integer i when i >= 0I ->
Rational {numerator = r.numerator ** int i; denominator = r.denominator ** int i}
| Rational r, Integer i when i < 0I ->
Rational {numerator = ((System.Numerics.BigInteger r.numerator.Sign) * r.denominator) **
int (abs i); denominator = (abs r.numerator) ** int (abs i)}
| PositiveInfinity, PositiveInfinity -> PositiveInfinity
| NegativeInfinity, NegativeInfinity -> NegativeInfinity
| NegativeInfinity, PositiveInfinity -> Undefined
| PositiveInfinity, NegativeInfinity -> Undefined
| _ -> Undefined //Use until all defenitions are made
static member (!*) x =
let rec factorial n =
match n with
| Integer x when x = 0I || x = 1I -> Integer 1I
| Integer x when x > 1I -> n * factorial (n + -(Integer 1I))
| Integer x when x < 0I -> n * factorial (-n + -(Integer 1I))
| _ -> Undefined
factorial x
type Expression =
| Number of NumberType
| Symbol of Symbol
| BinaryOp of Expression * Function * Expression
| UnaryOp of Function * Expression
| NaryOp of Function * (Expression list)
let rec recurseExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp exp : 'r =
let recurse = recurseExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp
match exp with
| Number n -> eNumber (Number n)
| Symbol v -> eSymbol (Symbol v)
| BinaryOp (a,op,b) -> eBinaryOp (recurse a,op,recurse b)
| UnaryOp (op,a) -> eUnaryOp (op,recurse a)
| NaryOp (op,aList) -> eNaryOp (op,(List.map recurse aList))
let rec foldExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp acc exp : 'r =
let recurse = foldExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp
match exp with
| Number n ->
let finalAcc = eNumber acc (Number n)
finalAcc
| Symbol v ->
let finalAcc = eSymbol acc (Symbol v)
finalAcc
| BinaryOp (a,op,b) ->
let newAcc = eBinaryOp acc (BinaryOp (a,op,b))
[a;b] |> List.fold recurse newAcc
| UnaryOp (op,a) ->
let newAcc = eUnaryOp acc (UnaryOp (op,a))
recurse newAcc a
| NaryOp (op,aList) ->
let newAcc = eNaryOp acc (NaryOp (op,aList))
aList |> List.fold recurse newAcc
let isNumber x =
match x with
| Number n -> true
| _ -> false
let isNegativeNumber x =
match x with
| Number n when n.isNegative -> true
| _ -> false
let Base x =
match x with
| Number n-> Number Undefined
| BinaryOp (a,ToThePowerOf,b) -> a
| _ -> x
let Exponent x =
match x with
| Number n -> Number Undefined
| BinaryOp (a,ToThePowerOf,b) -> b
| _ -> Number NumberType.One
let Term x =
match x with
| Number n -> Number Undefined
| NaryOp(Product,p) when isNumber p.[0] ->
match p.Length with
| 1 -> Number Undefined
| 2 -> p.[1]
| _ -> NaryOp(Product,p.Tail)
| NaryOp(Product,x) when isNumber x.[0] = false -> NaryOp(Product,x)
| a -> a
let Const x =
match x with
| Number n -> Number Undefined
| NaryOp(op,a) when isNumber a.[0] -> a.[0]
| NaryOp(op,a) when isNumber a.[0] = false -> Number NumberType.One
| _ -> Number NumberType.One
//Comparison
let rec compareExpressions x' y' =
match x', y' with
| Number x, Number y -> NumberType.compareNumbers x y //O-1
| Symbol x, Symbol y when x > y -> 1 //O-2
| Symbol x, Symbol y when x < y -> -1 //O-2
| Symbol x, Symbol y when x = y -> 0 //O-2
| NaryOp(op1, x), NaryOp(op2, y) when //O-3.1 & O-6.2.(a)
op1 = op2 &&
(List.rev x).Head <> (List.rev y).Head ->
compareExpressions ((List.rev x).Head) ((List.rev y).Head)
| NaryOp(op1, x), NaryOp(op2, y) when //O-3 & O-6.2
op1 = op2 &&
(List.rev x).Head = (List.rev y).Head ->
match x.Tail.IsEmpty , y.Tail.IsEmpty with
| false, false -> compareExpressions (NaryOp(op1, (List.rev((List.rev x).Tail)))) (NaryOp(op1, (List.rev((List.rev y).Tail)))) //O-3.2 & O-6.2.(b)
| true, false -> 1 //O-3.3 & O-6.2.(c)
| false, true -> -1 //O-3.3 & O-6.2.(c)
| true, true -> 0
| BinaryOp(x1, op1, y1), BinaryOp(x2, op2, y2) when op1 = op2 && x1 <> x2 -> compareExpressions x1 x2 //O-4.1
| BinaryOp(x1, op1, y1), BinaryOp(x2, op2, y2) when op1 = op2 && x1 = x2 -> compareExpressions y1 y2 //O-4.2
| UnaryOp(op1, x), UnaryOp(op2, y) when op1 = op2 -> compareExpressions x y //O-5
| BinaryOp(x1, op1, y1), BinaryOp(x2, op2, y2) when op1 < op2 -> -1 //O-6.1
| BinaryOp(x1, op1, y1), BinaryOp(x2, op2, y2) when op1 > op2 -> 1 //O-6.1
| BinaryOp(x1, op1, y1), NaryOp(op2, y) when op1 < op2 -> -1 //O-6.1
| BinaryOp(x1, op1, y1), NaryOp(op2, y) when op1 > op2 -> 1 //O-6.1
| BinaryOp(x1, op1, y1), UnaryOp(op2, y) when op1 < op2 -> -1 //O-6.1
| BinaryOp(x1, op1, y1), UnaryOp(op2, y) when op1 > op2 -> 1 //O-6.1
| NaryOp(op1, x), NaryOp(op2, y) when op1 < op2 -> -1 //O-6.1
| NaryOp(op1, x), NaryOp(op2, y) when op1 > op2 -> 1 //O-6.1
| NaryOp(op1, x), BinaryOp(x2, op2, y2) when op1 < op2 -> -1 //O-6.1
| NaryOp(op1, x), BinaryOp(x2, op2, y2) when op1 > op2 -> 1 //O-6.1
| NaryOp(op1, x), UnaryOp(op2, y) when op1 < op2 -> -1 //O-6.1
| NaryOp(op1, x), UnaryOp(op2, y) when op1 > op2 -> 1 //O-6.1
| UnaryOp(op1, x), UnaryOp(op2, y) when op1 > op2 -> -1 //O-6.1
| UnaryOp(op1, x), UnaryOp(op2, y) when op1 < op2 -> 1 //O-6.1
| UnaryOp(op1, x), BinaryOp(x2, op2, y2) when op1 > op2 -> -1 //O-6.1
| UnaryOp(op1, x), BinaryOp(x2, op2, y2) when op1 < op2 -> 1 //O-6.1
| UnaryOp(op1, x), NaryOp(op2, y) when op1 > op2 -> -1 //O-6.1
| UnaryOp(op1, x), NaryOp(op2, y) when op1 < op2 -> 1 //O-6.1
| _, Number _ -> 1 //O-7
| Number _, _ -> -1 //O-7
| NaryOp(Product, x), y -> compareExpressions (NaryOp(Product, x)) (NaryOp(Product, [y])) //O-8
| BinaryOp(base', ToThePowerOf, power'), b when base' <> Base b -> compareExpressions base' (Base b) //O-9
| BinaryOp(base', ToThePowerOf, power'), b when base' = Base b -> compareExpressions power' (Exponent b) //O-9
| NaryOp(Sum, s), b -> compareExpressions (NaryOp(Sum, s)) (NaryOp(Sum, [b])) //O-10
| UnaryOp(Factorial, x), b when x = b -> -1 //O-11.1
| UnaryOp(Factorial, x), b when x <> b -> compareExpressions (UnaryOp(Factorial, x)) (UnaryOp(Factorial, b)) //O-11.2
| NaryOp(op, x), Symbol v when x = [Symbol v] -> -1 //O-12.1
| NaryOp(op, x), Symbol v when x <> [Symbol v] -> compareExpressions (NaryOp(op, x)) (NaryOp(op, [Symbol v])) //O-12.2
| BinaryOp(x,op, y), Symbol v when x = Symbol v -> -1 //O-12.1
| BinaryOp(x,op, y), Symbol v when x <> Symbol v -> compareExpressions (BinaryOp(x,op, y)) (BinaryOp(Symbol v,op, y)) //O-12.2
| UnaryOp(op, x), Symbol v when x = Symbol v -> -1 //O-12.1
| UnaryOp(op, x), Symbol v when x <> Symbol v -> compareExpressions (UnaryOp(op, x)) (UnaryOp(op, Symbol v)) //O-12.2
| _ -> -1 * (compareExpressions y' x') //O-13
// Simplification Operators
let rec simplifyPower x =
let rec simplifyIntegerPower x =
match x with
| BinaryOp(Number base',ToThePowerOf,Number(Integer i)) when base' <> NumberType.Zero -> Number (base'**(Integer i)) //SINTPOW-1
| BinaryOp(base',ToThePowerOf,Number(Integer i)) when base' <> Number NumberType.Zero && i = 0I -> Number (Integer 1I) //SINTPOW-2
| BinaryOp(base',ToThePowerOf,Number(Integer i)) when base' <> Number NumberType.Zero && i = 1I-> base' //SINTPOW-3
| BinaryOp((BinaryOp(base',ToThePowerOf,power')),ToThePowerOf,Number(Integer i)) -> //SINTPOW-4
let p = simplifyProduct (NaryOp(Product,[power'; Number(Integer i)]))
match p with
| Number (Integer ii) -> simplifyIntegerPower (BinaryOp(base',ToThePowerOf,p))
| _ -> (BinaryOp(base',ToThePowerOf,p))
| BinaryOp((NaryOp(Product,eList)),ToThePowerOf,Number(Integer i)) -> //SINTPOW-5
let eList' = List.map (fun x -> simplifyIntegerPower (BinaryOp(x,ToThePowerOf,Number(Integer i)))) eList
simplifyProduct (NaryOp(Product,eList'))
| _ -> x //SINTPOW-6
match x with
| BinaryOp(base',ToThePowerOf,power') when base' = Number Undefined || power' = Number Undefined -> Number Undefined //SPOW-1
| BinaryOp(base',ToThePowerOf,Number n) when base' = Number NumberType.Zero && n.isNegative = false -> Number NumberType.Zero //SPOW-2
| BinaryOp(base',ToThePowerOf,power') when base' = Number NumberType.Zero -> Number Undefined //SPOW-2
| BinaryOp(base',ToThePowerOf,power') when base' = Number NumberType.One -> Number NumberType.One //SPOW-3
| BinaryOp(base',ToThePowerOf,Number (Integer n)) -> simplifyIntegerPower x //SPOW-4
| _ -> x //SPOW-5
and simplifyProduct p =
let rec simplifyProductRec p =
match p with
| [NaryOp(Product, x); NaryOp(Product, y)] -> mergeProducts x y //SPRDREC-2.1
| [NaryOp(Product, x); a] -> mergeProducts x [a] //SPRDREC-2.2
| [a; NaryOp(Product, x)] -> mergeProducts [a] x //SPRDREC-2.3
| [a; b] ->
match a, b with
| Number a, Number b -> //SPRDREC-1
let n = Number (a * b)
match n with
| Number x when x = NumberType.One -> [] //SPRDREC-1.1
| _ -> [n] //SPRDREC-1.1
| Number a, b when a = NumberType.One -> [b] //SPRDREC-1.2.a
| a, Number b when b = NumberType.One -> [a] //SPRDREC-1.2.b
| a, b when Base b = Base a -> //SPRDREC-1.3
let s = simplifySum (NaryOp(Sum,[Exponent a; Exponent b]))
let p = simplifyPower (BinaryOp(Base a, ToThePowerOf, s))
match p with
| Number n when n = NumberType.One -> [] //SPRDREC-1.3
| _ -> [p] //SPRDREC-1.3
| a, b when compareExpressions a b = 1 -> [b; a] //SPRDREC-1.4
| _ -> [a; b] //SPRDREC-1.5
| l when List.length l > 2 -> //SPRDREC-3
let w = simplifyProductRec l.Tail
match l.Head with
| NaryOp(Product, x) -> mergeProducts x w //SPRDREC-3.1
| _ -> mergeProducts [l.Head] w //SPRDREC-3.2
| _ -> p
and mergeProducts a b =
let sort = List.sortWith (fun x -> compareExpressions x)
let a' = sort a
let b' = sort b
match a', b' with
| x, [] -> x //MPRD-1
| [], y -> y //MPRD-2
| x, y -> //MPRD-3
let h = simplifyProductRec [x.Head; y.Head]
match h with
| [] -> mergeProducts x.Tail y.Tail //MPRD-3.1
| [h'] -> h'::(mergeProducts x.Tail y.Tail) //MPRD-3.2
| [a; b] when compareExpressions x.Head y.Head = -1 -> x.Head::(mergeProducts x.Tail y) //MPRD-3.3
| _ -> y.Head::(mergeProducts x y.Tail) //MPRD-3.4
match p with
| NaryOp(Product,x) when List.exists (fun elem -> elem = Number Undefined) x || x.IsEmpty -> Number Undefined //SPRD-1
| NaryOp(Product,x) when List.exists (fun elem -> elem = Number NumberType.Zero) x -> Number NumberType.Zero //SPRD-2
| NaryOp(Product,x) when List.length x = 1 -> x.[0] //SPRD-3
| NaryOp(Product,x) -> //SPRD-4
let x' : Expression list = simplifyProductRec x
match x' with
| []-> Number NumberType.One //SPRD-4.3
| [x1] -> x1 //SPRD-4.1
| _ -> NaryOp(Product,x') //SPRD-4.2
| _ -> Number Undefined
and simplifySum p =
let rec simplifySumRec s =
match s with
| [NaryOp(Sum, x); NaryOp(Sum, y)] -> mergeSums x y
| [NaryOp(Sum, x); a] -> mergeSums x [a]
| [a; NaryOp(Sum, x)] -> mergeSums [a] x
| [a'; b'] ->
match a', b' with
| Number a, Number b ->
let n = Number (a + b)
match n with
| Number x when x = NumberType.Zero -> []
| _ -> [n]
| Number a, b when a = NumberType.Zero -> [b]
| a, Number b when b = NumberType.Zero -> [a]
| a, b when Term a = Term b -> [(simplifyProduct (NaryOp(Product,[(simplifySum (NaryOp(Sum, [(Const a); (Const b)]))); Term a])))]
| a, b when compareExpressions a b = 1 -> [b; a]
| _ -> [a'; b']
| l when List.length l > 2 ->
let w = simplifySumRec l.Tail
match l.Head with
| NaryOp(Sum, x) -> mergeSums x w
| _ -> mergeSums [l.Head] w
| _ -> s
and mergeSums a b =
let sort = List.sortWith (fun x -> compareExpressions x)
let a' = sort a
let b' = sort b
match a', b' with
| x, [] -> x //MPRD-1
| [], y -> y //MPRD-2
| x, y -> //MPRD-3
let h = simplifySumRec [x.Head; y.Head]
match h with
| [] -> mergeSums x.Tail y.Tail //MPRD-3.1
| [h'] -> h'::(mergeSums x.Tail y.Tail) //MPRD-3.2
| [a; b] when compareExpressions x.Head y.Head = -1 -> x.Head::(mergeSums x.Tail y) //MPRD-3.3
| _ -> y.Head::(mergeSums x y.Tail) //MPRD-3.4
match p with
| NaryOp(Sum,x) when List.exists (fun elem -> elem = Number Undefined) x || x.IsEmpty -> Number Undefined
| NaryOp(Sum,x) when List.length x = 1 -> x.[0]
| NaryOp(Sum,x) ->
let x' : Expression list = simplifySumRec x
match x' with
| []-> Number NumberType.Zero
| [x1] -> x1
| _ -> NaryOp(Sum,x')
| _ -> Number Undefined
let simplifyFactorial x =
match x with
| UnaryOp(Factorial, a) when isNegativeNumber a -> UnaryOp(Factorial,a)
| UnaryOp(Factorial, Number x) -> Number !*x
| _ -> x
let simplifyExpression x =
let rec simplify a' =
match a' with
| NaryOp(Sum,a) -> simplifySum (NaryOp(Sum,(List.map simplify a)))
| NaryOp(Product,a) -> simplifyProduct (NaryOp(Product,(List.map simplify a)))
| BinaryOp(a,ToThePowerOf,b) -> simplifyPower (BinaryOp(simplify a,ToThePowerOf,simplify b))
| UnaryOp(Factorial,a) -> simplifyFactorial (simplify a)
| _ -> a'
simplify x
let negate x = simplifyProduct (NaryOp(Product, [Number (Integer -1I); x]))
type Expression with
member this.isNumber = isNumber this
member this.isNegativeNumber = isNegativeNumber this
member this.Base = Base this
member this.Exponent = Exponent this
member this.Term = Term this
member this.Const = Const this
static member compareExpressions x y = compareExpressions x y
static member (~+) (x:Expression) = x
static member (~-) (x:Expression) = negate x
static member (+) (x, y) = NaryOp(Sum,[x; y]) |> simplifySum
static member (*) (x, y) = NaryOp(Product,[x; y]) |> simplifyProduct
static member (-) (x, y) = NaryOp(Sum,[x; -y]) |> simplifySum
static member Pow (x, y) = BinaryOp (x, ToThePowerOf, y) |> simplifyPower
static member (/) (x, y) = NaryOp(Product,[x; (y**(-(Number NumberType.One)))]) |> simplifyProduct
static member (!*) x = UnaryOp(Factorial,x) |> simplifyFactorial
let kind x =
match x with
| Number (Integer i) -> "Integer"
| Number (Rational r) -> "Rational"
| _ -> "Undefined"
// etc.
let numberOfOperands x =
match x with
| Number n -> Undefined
| Symbol v -> Undefined
| BinaryOp (a,op,b) -> Integer 2I
| UnaryOp (op,a) -> Integer 1I
| NaryOp (op,aList) -> Integer (System.Numerics.BigInteger aList.Length)
let operand a b =
match a, b with
| UnaryOp (op,u), 1 -> u
| BinaryOp (x,op,y), 1 -> x
| BinaryOp (x,op,y), 2 -> y
| NaryOp (op,u), n when n > 0 && n <= u.Length -> u.[n-1]
| _ -> Number Undefined
let numberOfCompoundExpressions (x:Expression) =
let eNumber acc (n:Expression) = acc
let eSymbol acc (v:Expression) = acc
let eBinaryOp acc x = acc + 1
let eUnaryOp acc x = acc + 1
let eNaryOp acc x = acc + 1
let acc = 0
foldExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp acc x
let numberOfAtomicExpressions (x:Expression) =
let eNumber acc (n:Expression) = 1 + acc
let eSymbol acc (v:Expression) = 1 + acc
let eBinaryOp acc x = acc
let eUnaryOp acc x = acc
let eNaryOp acc x = acc
let acc = 0
foldExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp acc x
let subExpressions (x:Expression) =
let eNumber acc (n:Expression) = n::acc
let eSymbol acc (v:Expression) = v::acc
let eBinaryOp acc x = x::acc
let eUnaryOp acc x = x::acc
let eNaryOp acc x = x::acc
let acc = []
foldExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp acc x
let variables (x:Expression) =
let eNumber acc (n:Expression) = acc
let eSymbol acc (v:Expression) = match v with | Symbol (Variable v) -> Variable v::acc | Symbol (Constant c) -> acc | _ -> acc
let eBinaryOp acc x = acc
let eUnaryOp acc x = acc
let eNaryOp acc x = acc
let acc = []
foldExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp acc x
|> Seq.distinct
|> Seq.toList
// Structure-based Operators
let freeOf u t =
let completeSubExpressions = subExpressions u
not (List.exists (fun x -> x = t) completeSubExpressions)
let substitute (y, t) u =
let eNumber (n:Expression) = (match n = y with | true -> t | false -> n)
let eSymbol (v:Expression) = (match v = y with | true -> t | false -> v)
let eBinaryOp (a,op,b) = (match BinaryOp (a,op,b) = y with | true -> t | false -> BinaryOp (a,op,b))
let eUnaryOp (op,a) = (match UnaryOp (op,a) = y with | true -> t | false -> UnaryOp (op,a))
let eNaryOp (op,aList) = (match NaryOp (op,aList) = y with | true -> t | false -> NaryOp (op,aList))
recurseExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp u
//|> simplifyExpression
let substituteSequential (yList : (Expression*Expression) list) u =
List.fold (fun u' (x,y) -> substitute (x, y) u') u yList
let rec substituteConcurrent (yList : (Expression*Expression) list) = function
| Number n ->
let x = List.exists (fun x -> fst x = Number n) yList
match x with
| true ->
let y = (List.find (fun x -> fst x = Number n) yList)
(snd y)
| false -> Number n
| Symbol v ->
let x = List.exists (fun x -> fst x = Symbol v) yList
match x with
| true ->
let y = (List.find (fun x -> fst x = Symbol v) yList)
(snd y)
| false -> (Symbol v)
| BinaryOp (a,op,b) ->
let x = List.exists (fun x -> fst x = BinaryOp (a,op,b)) yList
match x with
| true ->
let y = (List.find (fun x -> fst x = BinaryOp (a,op,b)) yList)
(snd y)
| false -> (BinaryOp (substituteConcurrent yList a,op,substituteConcurrent yList b))
| UnaryOp (op,a) ->
let x = List.exists (fun x -> fst x = UnaryOp (op,a)) yList
match x with
| true ->
let y = (List.find (fun x -> fst x = UnaryOp (op,a)) yList)
(snd y)
| false -> (UnaryOp (op,substituteConcurrent yList a))
| NaryOp (op,aList) ->
let x = List.exists (fun x -> fst x = NaryOp (op,aList)) yList
match x with
| true ->
let y = (List.find (fun x -> fst x = NaryOp (op,aList)) yList)
(snd y)
| false -> (NaryOp (op,List.map (fun x -> substituteConcurrent yList x) aList))
let rec (|GeneralMonomial|_|) (xList : Expression list) = function
| BinaryOp (a,ToThePowerOf,(Number (Integer b))) when //GME-3
b > 1I && List.exists (fun x -> a = x) xList -> Some (BinaryOp (a,ToThePowerOf,(Number (Integer b))))
| NaryOp (Product, a) -> //GME-4
let test = List.forall (fun x -> (match x with | GeneralMonomial xList x -> true
| _ -> false) = true) a
match test with
| true -> Some (NaryOp (Product, a))
| false -> None
| a when List.forall (fun x -> freeOf (a) x ) xList || //GME-1
List.exists (fun x -> a = x) xList -> Some (a) //GME-2
| _-> None
let (|GeneralPolynomial|_|) (xList : Expression list) = function
| NaryOp (Sum, a) -> //GPE-2
match List.forall (fun x -> (match x with | GeneralMonomial xList x -> true
| _ -> false) = true) a with
| true -> Some (NaryOp (Sum, a))
| false -> None
| x when (match x with | GeneralMonomial xList x -> true //GPE-1
| _ -> false) = true -> Some x
| _-> None
let isPolynomialGPE x xList =
match x with
| GeneralPolynomial xList p -> true
| _ -> false
////////////////////////////////////////////////////////////
//good code
let rec _degreeGPE u xList =
match isPolynomialGPE u xList with
| false -> Undefined
| true ->
match u with
| Number n when n = NumberType.Zero -> NegativeInfinity
| NaryOp (Sum, aList) -> (List.rev (List.sortWith NumberType.compareNumbers (List.map (fun x -> _degreeGPE x xList) aList))).Head
| _ ->
let powers =
let eNumber acc (n:Expression) = match n = Number NumberType.Zero with | false -> (Integer 0I,Base n)::acc | true -> (NegativeInfinity,Base n)::acc
let eSymbol acc (v:Expression) = (Integer 1I,Base v)::acc
let eBinaryOp acc x = match x with | BinaryOp (Symbol(Variable a),ToThePowerOf,Number(Integer b)) -> (Integer b,Base x) :: acc | _ -> acc
let eUnaryOp acc x = acc
let eNaryOp acc x = acc
let acc = []
foldExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp acc u
let compare x' y' = NumberType.compareNumbers (fst x') (fst y')
let out = List.rev(List.sortWith compare powers)
List.collect (fun xx ->
let x' =
match List.exists (fun (a,b) -> xx = b) out with
| true -> List.filter (fun (a,b) -> xx = b ) out
| false -> [NumberType.Zero,xx]
[(List.rev(List.sortWith compare x')).Head]) xList
|> List.fold (fun acc (a,b) -> acc + a) NumberType.Zero
let degreeGPE u xList =
let x = _degreeGPE u xList
match x = NumberType.Zero with | true -> Undefined | false -> x
////////////////////////////////////////////////////////////
//Bad code
let rec degreeGPEBad u xList =
match isPolynomialGPE u xList with
| false -> Undefined
| true ->
match u with
| Number n when n = NumberType.Zero -> NegativeInfinity
| NaryOp (Sum, aList) -> (List.rev (List.sortWith NumberType.compareNumbers (List.map (fun x -> degreeGPE x xList) aList))).Head
| _ ->
let powers =
let eNumber acc (n:Expression) = match n = Number NumberType.Zero with | false -> (Integer 0I,Base n)::acc | true -> (NegativeInfinity,Base n)::acc
let eSymbol acc (v:Expression) = (Integer 1I,Base v)::acc
let eBinaryOp acc x = match x with | BinaryOp (Symbol(Variable a),ToThePowerOf,Number(Integer b)) -> (Integer b,Base x) :: acc | _ -> acc
let eUnaryOp acc x = acc
let eNaryOp acc x = acc
let acc = []
foldExpression eNumber eSymbol eBinaryOp eUnaryOp eNaryOp acc u
let compare x' y' = NumberType.compareNumbers (fst x') (fst y')
let out = List.rev(List.sortWith compare powers)
let check x = match x = NumberType.Zero with | true -> Undefined | false -> x
List.collect (fun xx ->
let x' =
match List.exists (fun (a,b) -> xx = b) out with
| true -> List.filter (fun (a,b) -> xx = b ) out
| false -> [NumberType.Zero,xx]
[(List.rev(List.sortWith compare x')).Head]) xList
|> List.fold (fun acc (a,b) -> acc + a) NumberType.Zero
|> check //if you comment this line to get the correct answer
//otherwise it returns the wrong answer.
//////////////////////////////////////////
//Tests
let y = Symbol (Variable "y")
let z = Symbol (Variable "z")
let a = Symbol (Variable "a")
let b = Symbol (Variable "b")
let c = Symbol (Variable "c")
let three = Number (Integer 3I)
let two = Number (Integer 2I)
let testD = two*z + a*z + three*y + b*y + c
degreeGPE testD [y]
degreeGPEBad testD [y]
@flideros I don't really understand the issue. When you add "check" to the code, it only applies to one (recursive) case of the match. This means it will
(a) apply to the results of some recursive calls (if they happen to go through that branch) and (b) may not apply to the result of the outermost (first) call (depending on whether it goes through that branch, which I think it doesn't)
You then move "check" outside the function, when it will
(c) definitely apply to the first iteration
Secondly, in your "bad" version the recursive call calls the "good" version.
When I changed your "bad" code to only apply check
on the first call, and not recursive calls, the results of OK and Bad code were the same
let rec degreeGPEBad first u xList =
match isPolynomialGPE u xList with
| false -> Undefined |> (if first then check else id)
| true ->
match u with
| Number n when n = NumberType.Zero -> NegativeInfinity |> (if first then check else id)
| NaryOp (Sum, aList) -> (List.rev (List.sortWith NumberType.compareNumbers (List.map (fun x -> degreeGPEBad false x xList) aList))).Head |> (if first then check else id)
| _ ->
I'll close this for now. If you can get this to a standalone repro that is under 50 lines I'll look again :)
You solved it. The recursive call was the culprit. Thank you for you patience.
No problem :) Though it will make me more insistent on minimized repros when others submit future bug reports :)
In the attached code, the degreeGPE function produces an unexpected result when a check function is piped into. When the same function is used outside of degreeGPE is produces the correct result.
Repro steps
Provide the steps required to reproduce the problem
Expected behavior
both a and b should = Integer 1 The only difference is in Step A the check function is piped in and in Step B the check is done in a seperate function.
Actual behavior
Step A result = Undefined Step B result = Integer 1
Known workarounds
Step B is the workaround.
Algebra.zip