diff --git a/tests/Furnace.Tests/Furnace.Tests.fsproj b/tests/Furnace.Tests/Furnace.Tests.fsproj
index 0adbdb94..8c2b6b0c 100644
--- a/tests/Furnace.Tests/Furnace.Tests.fsproj
+++ b/tests/Furnace.Tests/Furnace.Tests.fsproj
@@ -36,6 +36,7 @@
+
diff --git a/tests/Furnace.Tests/TestBranchCoverage.fs b/tests/Furnace.Tests/TestBranchCoverage.fs
new file mode 100644
index 00000000..1edd6db7
--- /dev/null
+++ b/tests/Furnace.Tests/TestBranchCoverage.fs
@@ -0,0 +1,275 @@
+// Copyright (c) 2016- University of Oxford (Atılım Güneş Baydin )
+// and other contributors, see LICENSE in root of repository.
+//
+// BSD 2-Clause License. See LICENSE in root of repository.
+
+namespace Tests
+
+open System
+open NUnit.Framework
+open Furnace
+
+[]
+type TestBranchCoverage() =
+
+ []
+ member _.TestTensorCastingBranches() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+ let t = combo.tensor([1.0f; 2.0f; 3.0f])
+
+ // Test cast to same type (should return same tensor)
+ let sameCast = t.cast(Dtype.Float32)
+ Assert.AreSame(t, sameCast)
+
+ // Test different type casts
+ let intCast = t.cast(Dtype.Int32)
+ Assert.AreEqual(Dtype.Int32, intCast.dtype)
+
+ let doubleCast = t.cast(Dtype.Float64)
+ Assert.AreEqual(Dtype.Float64, doubleCast.dtype)
+
+ []
+ member _.TestTensorBackendMoveBranches() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+ let t = combo.tensor([1.0f; 2.0f; 3.0f])
+
+ // Test move to same backend (should return same tensor)
+ let sameBackend = t.move(Backend.Reference)
+ Assert.AreSame(t, sameBackend)
+
+ []
+ member _.TestTensorDeviceMoveBranches() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+ let t = combo.tensor([1.0f; 2.0f; 3.0f])
+
+ // Test move to same device (should return same tensor)
+ let sameDevice = t.move(Device.CPU)
+ Assert.AreSame(t, sameDevice)
+
+ []
+ member _.TestGenericCastingBranches() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+ let t = combo.tensor([1.0f; 2.0f; 3.0f])
+
+ // Test different generic cast types
+ let float32Cast = t.cast()
+ Assert.AreEqual(Dtype.Float32, float32Cast.dtype)
+
+ let float64Cast = t.cast()
+ Assert.AreEqual(Dtype.Float64, float64Cast.dtype)
+
+ let int32Cast = t.cast()
+ Assert.AreEqual(Dtype.Int32, int32Cast.dtype)
+
+ let int64Cast = t.cast()
+ Assert.AreEqual(Dtype.Int64, int64Cast.dtype)
+
+ let int16Cast = t.cast()
+ Assert.AreEqual(Dtype.Int16, int16Cast.dtype)
+
+ let int8Cast = t.cast()
+ Assert.AreEqual(Dtype.Int8, int8Cast.dtype)
+
+ let bytecast = t.cast()
+ Assert.AreEqual(Dtype.Byte, bytecast.dtype)
+
+ let boolCast = t.cast()
+ Assert.AreEqual(Dtype.Bool, boolCast.dtype)
+
+ []
+ member _.TestInvalidGenericCastBranch() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+ let t = combo.tensor([1.0f; 2.0f; 3.0f])
+
+ // Test invalid cast type should throw
+ Assert.Throws(fun () ->
+ t.cast() |> ignore) |> ignore
+
+ []
+ member _.TestTensorComparisons() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+ let t1 = combo.tensor([1.0f; 2.0f; 3.0f])
+ let t2 = combo.tensor([1.0f; 2.0f; 3.0f])
+ let t3 = combo.tensor([1.0f; 2.0f; 4.0f])
+
+ // Test equality branches
+ Assert.True(t1.Equals(t2))
+ Assert.False(t1.Equals(t3))
+ Assert.False(t1.Equals(null))
+
+ []
+ member _.TestTensorShapeValidation() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+
+ // Test zero-sized tensors
+ let empty = FurnaceImage.zeros([0], dtype=combo.dtype, backend=combo.backend, device=combo.device)
+ Assert.AreEqual(0, empty.nelement)
+
+ // Test single element tensors
+ let single = combo.tensor([42.0f])
+ Assert.AreEqual(1, single.nelement)
+
+ []
+ member _.TestTensorIndexingBoundaries() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+ let t = combo.tensor([1.0f; 2.0f; 3.0f; 4.0f; 5.0f])
+
+ // Test valid indexing
+ Assert.DoesNotThrow(fun () -> t[0] |> ignore)
+ Assert.DoesNotThrow(fun () -> t[4] |> ignore)
+
+ // Test boundary conditions for slicing
+ let slice1 = t[0..2]
+ Assert.AreEqual([|3|], slice1.shape)
+
+ let slice2 = t[1..4]
+ Assert.AreEqual([|4|], slice2.shape)
+
+ []
+ member _.TestTensorOperationEdgeCases() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+
+ // Test operations with different sized tensors
+ let t1x1 = combo.tensor([[1.0f]])
+ let t2x2 = combo.tensor([[1.0f; 2.0f]; [3.0f; 4.0f]])
+
+ // Test broadcasting operations
+ let broadcast1 = t1x1 + t2x2
+ Assert.AreEqual([|2; 2|], broadcast1.shape)
+
+ let broadcast2 = t2x2 * combo.tensor([2.0f])
+ Assert.AreEqual([|2; 2|], broadcast2.shape)
+
+ []
+ member _.TestBoolTensorOperations() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Bool)
+
+ // Test boolean tensor creation and operations
+ let b1 = combo.tensor([true; false; true])
+ let b2 = combo.tensor([false; true; true])
+
+ // Test boolean operations that may have specific branches
+ Assert.AreEqual(Dtype.Bool, b1.dtype)
+ Assert.AreEqual(3, b1.nelement)
+
+ // Test boolean comparisons
+ let eq = b1.eq(b2)
+ Assert.AreEqual(Dtype.Bool, eq.dtype)
+
+ []
+ member _.TestTensorCreationEdgeCases() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+
+ // Test creating tensors from different input types
+ let fromInt = combo.tensor([1; 2; 3])
+ Assert.AreEqual(Dtype.Float32, fromInt.dtype) // Should be cast to combo dtype
+
+ let fromFloat = combo.tensor([1.0; 2.0; 3.0])
+ Assert.AreEqual(Dtype.Float32, fromFloat.dtype)
+
+ // Test nested arrays
+ let nested = combo.tensor([[[1.0f]]])
+ Assert.AreEqual([|1; 1; 1|], nested.shape)
+ Assert.AreEqual(3, nested.dim)
+
+ []
+ member _.TestTensorMemoryLayout() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+
+ // Test different memory layouts and shapes
+ let t1d = combo.tensor([1.0f; 2.0f; 3.0f; 4.0f])
+ let t2d = t1d.view([2; 2])
+
+ Assert.AreEqual([|4|], t1d.shape)
+ Assert.AreEqual([|2; 2|], t2d.shape)
+ Assert.AreEqual(t1d.nelement, t2d.nelement)
+
+ // Test transpose operations
+ let transposed = t2d.transpose()
+ Assert.AreEqual([|2; 2|], transposed.shape)
+
+ []
+ member _.TestTensorReductionEdgeCases() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+
+ // Test reductions on different tensor shapes
+ let t1d = combo.tensor([1.0f; 2.0f; 3.0f; 4.0f])
+ let sum1d = t1d.sum()
+ Assert.True(abs(sum1d.toScalar().toSingle() - 10.0f) < 0.001f)
+
+ let t2d = combo.tensor([[1.0f; 2.0f]; [3.0f; 4.0f]])
+ let sum2d = t2d.sum()
+ Assert.True(abs(sum2d.toScalar().toSingle() - 10.0f) < 0.001f)
+
+ // Test specific dimension reductions
+ let sumDim0 = t2d.sum(0)
+ Assert.AreEqual([|2|], sumDim0.shape)
+
+ let sumDim1 = t2d.sum(1)
+ Assert.AreEqual([|2|], sumDim1.shape)
+
+ []
+ member _.TestLowPrecisionTypeOperations() =
+ // Test BFloat16 specific operations for branch coverage
+ let comboBF16 = ComboInfo(Backend.Reference, Device.CPU, Dtype.BFloat16)
+ let t1 = comboBF16.tensor([1.0f; 2.0f; 3.0f])
+ let t2 = comboBF16.tensor([2.0f; 3.0f; 4.0f])
+
+ // Test various comparison operations
+ let eq = t1.eq(t2)
+ let ne = t1.ne(t2)
+ let lt = t1.lt(t2)
+ let le = t1.le(t2)
+ let gt = t1.gt(t2)
+ let ge = t1.ge(t2)
+
+ Assert.AreEqual(Dtype.Bool, eq.dtype)
+ Assert.AreEqual(Dtype.Bool, ne.dtype)
+ Assert.AreEqual(Dtype.Bool, lt.dtype)
+ Assert.AreEqual(Dtype.Bool, le.dtype)
+ Assert.AreEqual(Dtype.Bool, gt.dtype)
+ Assert.AreEqual(Dtype.Bool, ge.dtype)
+
+ []
+ member _.TestActivationFunctionBranches() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+ let t = combo.tensor([-2.0f; -1.0f; 0.0f; 1.0f; 2.0f])
+
+ // Test various activation functions that might have different code paths
+ let sigmoid = t.sigmoid()
+ Assert.AreEqual(5, sigmoid.nelement)
+ Assert.AreEqual(Dtype.Float32, sigmoid.dtype)
+
+ let tanh = t.tanh()
+ Assert.AreEqual(5, tanh.nelement)
+
+ let relu = t.relu()
+ Assert.AreEqual(5, relu.nelement)
+
+ let softplus = t.softplus()
+ Assert.AreEqual(5, softplus.nelement)
+
+ []
+ member _.TestMathematicalFunctionEdgeCases() =
+ let combo = ComboInfo(Backend.Reference, Device.CPU, Dtype.Float32)
+
+ // Test with various special values that might trigger different branches
+ let positive = combo.tensor([1.0f; 2.0f; 3.0f])
+ let negative = combo.tensor([-1.0f; -2.0f; -3.0f])
+ let mixed = combo.tensor([-1.0f; 0.0f; 1.0f])
+
+ // Test exp on different value ranges
+ let expPos = positive.exp()
+ let expMixed = mixed.exp()
+ Assert.AreEqual(3, expPos.nelement)
+ Assert.AreEqual(3, expMixed.nelement)
+
+ // Test log on positive values (negative would be invalid)
+ let logPos = positive.log()
+ Assert.AreEqual(3, logPos.nelement)
+
+ // Test absolute value
+ let absNeg = negative.abs()
+ let absMixed = mixed.abs()
+ Assert.AreEqual(3, absNeg.nelement)
+ Assert.AreEqual(3, absMixed.nelement)
\ No newline at end of file