No more trying to force truncation of int division

This commit is contained in:
2020-12-02 14:48:38 -07:00
parent ff4c7a34df
commit 625213aa15
4 changed files with 30 additions and 55 deletions

View File

@@ -5,17 +5,17 @@ import haxe.ds.Either;
/**
Arithmetic operands
**/
abstract Operand(Either<String, Either<Int, Float>>) from Either<String, Either<Int, Float>> to Either<String, Either<Int, Float>> {
abstract Operand(Either<String, Float>) from Either<String, Float> to Either<String, Float> {
@:from inline static function fromString(s:String):Operand {
return Left(s);
}
@:from inline static function fromInt(f:Int):Operand {
return Right(Left(f));
@:from inline static function fromInt(i:Int):Operand {
return Right(0.0 + i);
}
@:from inline static function fromFloat(f:Float):Operand {
return Right(Right(f));
return Right(f);
}
// Doing this one implicitly just wasn't working in conjunction with Lambda.fold
@@ -23,8 +23,7 @@ abstract Operand(Either<String, Either<Int, Float>>) from Either<String, Either<
/* @:from */
public inline static function fromDynamic(d:Dynamic):Operand {
return switch (Type.typeof(d)) {
case TInt: Right(Left(d));
case TFloat: Right(Right(d));
case TInt | TFloat: Right(d);
default:
if (Std.isOfType(d, String)) {
Left(d);
@@ -34,7 +33,7 @@ abstract Operand(Either<String, Either<Int, Float>>) from Either<String, Either<
else if (Std.isOfType(d, Either)) {
d;
} else {
throw '$d cannot be Operand';
throw '$d cannot be converted to Operand';
};
};
}
@@ -48,16 +47,14 @@ abstract Operand(Either<String, Either<Int, Float>>) from Either<String, Either<
@:to public inline function toFloat():Null<Float> {
return switch (this) {
case Right(Right(f)): f;
case Right(Left(i)): i;
case Right(f): f;
default: null;
};
}
@:to public inline function toInt():Null<Int> {
return switch (this) {
case Right(Left(i)): i;
case Right(Right(f)): Math.floor(f);
case Right(f): Math.floor(f);
default: null;
};
}
@@ -69,8 +66,7 @@ abstract Operand(Either<String, Either<Int, Float>>) from Either<String, Either<
return if (Std.isOfType(o, Either)) {
var o:Operand = cast o;
switch (o) {
case Right(Left(i)): i;
case Right(Right(f)): f;
case Right(f): f;
case Left(str): str;
};
} else {

View File

@@ -8,65 +8,46 @@ import haxe.ds.Either;
class Prelude {
// Kiss arithmetic will incur overhead because of these switch statements, but the results will not be platform-dependent
public static function add(a:Operand, b:Operand):Operand {
return switch (a) {
case Left(str):
var firstStr = if (b.toString() != null) {
b.toString();
} else {
throw 'cannot add string $str and float ${b.toFloat()}';
};
Left(firstStr + str);
case Right(Left(i)):
switch (b) {
case Right(Right(f)):
add(b, a);
case Right(Left(bI)):
Right(Left(i + bI));
case Left(s): throw 'cannot add int $i and string $s';
}
case Right(Right(f)):
Right(Right(f + if (b.toFloat() != null) b.toFloat() else throw 'cannot add float $f and string ${b.toString()}'));
return switch ([a, b]) {
case [Left(str), Left(str2)]:
Left(str2 + str);
case [Right(f), Right(f2)]:
Right(f + f2);
default:
throw 'cannot add mismatched types ${Operand.toDynamic(a)} and ${Operand.toDynamic(b)}';
};
}
public static function subtract(val:Operand, from:Operand):Operand {
return switch ([from, val]) {
case [Right(Left(from)), Right(Left(val))]:
Right(Left(from - val));
case [Right(_), Right(_)]:
Right(Right(from.toFloat() - val.toFloat()));
case [Right(from), Right(val)]:
Right(from - val);
default:
throw 'cannot subtract $val from $from';
throw 'cannot subtract ${Operand.toDynamic(val)} from ${Operand.toDynamic(from)}';
}
}
public static function multiply(a:Operand, b:Operand):Operand {
return switch ([a, b]) {
case [Right(Right(f)), Right(Left(_)) | Right(Right(_))]:
Right(Right(f * b.toFloat()));
case [Right(Left(i)), Right(Right(_))]:
multiply(b, a);
case [Right(Left(i)), Right(Left(bI))]:
Right(Left(i * bI));
case [Right(f), Right(f2)]:
Right(f * f2);
case [Left(a), Left(b)]:
throw 'cannot multiply strings "$a" and "$b"';
case [Right(Left(i)), Left(s)] | [Left(s), Right(Left(i))]:
case [Right(i), Left(s)] | [Left(s), Right(i)] if (i % 1 == 0):
var result = "";
for (_ in 0...i) {
for (_ in 0...Math.floor(i)) {
result += s;
}
Left(result);
default:
throw 'cannot multiply $a and $b';
throw 'cannot multiply ${Operand.toDynamic(a)} and ${Operand.toDynamic(b)}';
};
}
public static function divide(bottom:Operand, top:Operand):Operand {
return switch ([top, bottom]) {
case [Right(Left(top)), Right(Left(bottom))]:
Math.floor(top / bottom);
case [Right(_), Right(_)]:
top.toFloat() / bottom.toFloat();
case [Right(f), Right(f2)]:
Right(f / f2);
default:
throw 'cannot divide $top by $bottom';
};
@@ -97,7 +78,7 @@ class Prelude {
}
public static function areEqual(a:Operand, b:Operand):Operand {
return if (Operand.toDynamic(a) == Operand.toDynamic(b)) a else Right(Right(Math.NaN));
return if (Operand.toDynamic(a) == Operand.toDynamic(b)) a else Right(Math.NaN);
}
public static function groups<T>(a:Array<T>, size, keepRemainder = false) {

View File

@@ -63,8 +63,7 @@ class BasicTestCase extends Test {
}
function testVariadicDivide() {
Assert.equals(0, BasicTestCase.myQuotientInt);
Assert.equals(0.5, BasicTestCase.myQuotientFloat);
Assert.equals(0.5, BasicTestCase.myQuotient);
}
function testMod() {

View File

@@ -31,9 +31,8 @@
(defvar myProduct (* 2 5 6))
(defvar myQuotientInt (/ 6 3 2 2))
(defvar myQuotientFloat (/ 6 3 2 2.0))
// All math operations return floats, none truncate by default
(defvar myQuotient (/ 6 3 2 2))
(defvar myRemainder (% 10 6))