diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82ff7b89..fdbfe033 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,10 @@ jobs: - windows-latest arch: - x64 + - x86 + exclude: + - os: macOS-latest + arch: x86 steps: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@v1 diff --git a/Project.toml b/Project.toml index aa4a06d1..50cd0b0d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "GeometryBasics" uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" authors = ["SimonDanisch "] -version = "0.5.0" +version = "0.5.1" [deps] EarCut_jll = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" diff --git a/src/basic_types.jl b/src/basic_types.jl index 9d444514..d2623940 100644 --- a/src/basic_types.jl +++ b/src/basic_types.jl @@ -552,12 +552,12 @@ struct Mesh{ vertex_attributes::NamedTuple{Names, VAT} faces::FVT - views::Vector{UnitRange{Int}} + views::Vector{UnitRange{UInt32}} function Mesh( vertex_attributes::NamedTuple{Names, VAT}, fs::FVT, - views::Vector{UnitRange{Int}} = UnitRange{Int}[] + views::Vector{<: UnitRange{<: Integer}} = UnitRange{UInt32}[] ) where { FT <: AbstractFace, FVT <: AbstractVector{FT}, Names, Dim, T, VAT <: Tuple{<: AbstractVector{Point{Dim, T}}, Vararg{VertexAttributeType}} @@ -669,13 +669,14 @@ and have faces of matching length. sub-meshes. This is done by providing ranges for indexing faces which correspond to the sub-meshes. By default this is left empty. """ -function Mesh(faces::AbstractVector{<:AbstractFace}; views::Vector{UnitRange{Int}} = UnitRange{Int}[], attributes...) +function Mesh(faces::AbstractVector{<:AbstractFace}; + views::Vector{<: UnitRange{<: Integer}} = UnitRange{UInt32}[], attributes...) return Mesh(NamedTuple(attributes), faces, views) end function Mesh(points::AbstractVector{Point{Dim, T}}, faces::AbstractVector{<:AbstractFace}; - views = UnitRange{Int}[], kwargs...) where {Dim, T} + views = UnitRange{UInt32}[], kwargs...) where {Dim, T} va = (position = points, kwargs...) return Mesh(va, faces, views) end diff --git a/src/interfaces.jl b/src/interfaces.jl index 8bba548c..cd77348d 100644 --- a/src/interfaces.jl +++ b/src/interfaces.jl @@ -1,9 +1,9 @@ """ coordinates(geometry) -Returns the positions/coordinates of a geometry. +Returns the positions/coordinates of a geometry. -This is allowed to return lazy iterators. Use `decompose(ConcretePointType, geometry)` +This is allowed to return lazy iterators. Use `decompose(ConcretePointType, geometry)` to get a `Vector{ConcretePointType}` with `ConcretePointType` being something like `Point3f`. """ @@ -14,9 +14,9 @@ end """ faces(geometry) -Returns the faces of a geometry. +Returns the faces of a geometry. -This is allowed to return lazy iterators. Use `decompose(ConcreteFaceType, geometry)` +This is allowed to return lazy iterators. Use `decompose(ConcreteFaceType, geometry)` to get a `Vector{ConcreteFaceType}` with `ConcreteFaceType` being something like `GLTriangleFace`. """ function faces(f::AbstractVector{<:AbstractFace}) @@ -26,9 +26,9 @@ end """ normals(primitive) -Returns the normals of a geometry. +Returns the normals of a geometry. -This is allowed to return lazy iterators. Use `decompose_normals(ConcreteVecType, geometry)` +This is allowed to return lazy iterators. Use `decompose_normals(ConcreteVecType, geometry)` to get a `Vector{ConcreteVecType}` with `ConcreteVecType` being something like `Vec3f`. """ function normals(primitive, nvertices=nothing; kw...) @@ -49,10 +49,10 @@ end """ texturecoordinates(primitive) -Returns the texturecoordinates of a geometry. +Returns the texturecoordinates of a geometry. -This is allowed to return lazy iterators. Use `decompose_uv(ConcreteVecType, geometry)` -(or `decompose_uvw`) to get a `Vector{ConcreteVecType}` with `ConcreteVecType` being +This is allowed to return lazy iterators. Use `decompose_uv(ConcreteVecType, geometry)` +(or `decompose_uvw`) to get a `Vector{ConcreteVecType}` with `ConcreteVecType` being something like `Vec2f`. """ texturecoordinates(primitive, nvertices=nothing) = nothing @@ -61,9 +61,9 @@ texturecoordinates(primitive, nvertices=nothing) = nothing Tessellation(primitive, nvertices) When generating a mesh from an abstract geometry, we can typically generate it -at different levels of detail, i.e. with different amounts of vertices. The +at different levels of detail, i.e. with different amounts of vertices. The `Tessellation` wrapper allows you to specify this level of detail. When generating -a mesh from a tessellated geometry, the added information will be passed to +a mesh from a tessellated geometry, the added information will be passed to `coordinates`, `faces`, etc. ```julia @@ -127,7 +127,7 @@ Normal() = Normal(Vec3f) decompose(::Type{TargetType}, primitive) decompose(::Type{TargetType}, data::AbstractVector) -Dependent on the given type, extracts data from the primtive and converts its +Dependent on the given type, extracts data from the primtive and converts its eltype to `TargetType`. Possible `TargetType`s: @@ -149,7 +149,7 @@ function decompose(::Type{F}, primitive::AbstractGeometry) where {F<:AbstractFac end return decompose(F, f) end - + function decompose(::Type{F}, f::AbstractVector) where {F<:AbstractFace} fs = faces(f) isnothing(fs) && error("No faces defined for $(typeof(f))") @@ -157,14 +157,14 @@ function decompose(::Type{F}, f::AbstractVector) where {F<:AbstractFace} end # TODO: Should this be a completely different function? -function decompose(::Type{F}, f::AbstractVector, views::Vector{UnitRange{Int}}) where {F<:AbstractFace} +function decompose(::Type{F}, f::AbstractVector, views::Vector{UnitRange{IT}}) where {F<:AbstractFace, IT<:Integer} fs = faces(f) isnothing(fs) && error("No faces defined for $(typeof(f))") if isempty(views) return collect_with_eltype(F, fs), views else output = F[] - new_views = sizehint!(UnitRange{Int}[], length(views)) + new_views = sizehint!(UnitRange{IT}[], length(views)) for range in views start = length(output) + 1 collect_with_eltype!(output, view(fs, range)) diff --git a/src/meshes.jl b/src/meshes.jl index 93b664c6..dc27e348 100644 --- a/src/meshes.jl +++ b/src/meshes.jl @@ -1,7 +1,7 @@ """ mesh(primitive::GeometryPrimitive[; pointtype = Point, facetype = GLTriangleFace, vertex_attributes...]) -Creates a mesh from a given `primitive` with the given `pointtype` and `facetype`. +Creates a mesh from a given `primitive` with the given `pointtype` and `facetype`. This method only generates positions and faces from the primitive. Additional vertex attributes like normals and texture coordinates can be given as extra @@ -51,8 +51,8 @@ Note that vertex attributes that are `nothing` get removed before creating a mes See also:[`normal_mesh`](@ref) """ function mesh( - positions::AbstractVector{<:Point}, - faces::AbstractVector{FT}; + positions::AbstractVector{<:Point}, + faces::AbstractVector{FT}; facetype=GLTriangleFace, vertex_attributes... ) where {FT <: AbstractFace} @@ -69,17 +69,17 @@ end """ mesh(mesh::Mesh[; pointtype = Point, facetype = GLTriangleFace, vertex_attributes...] -Recreates the given `mesh` with the given `pointtype`, `facetype` and vertex +Recreates the given `mesh` with the given `pointtype`, `facetype` and vertex attributes. If the new mesh would match the old mesh, the old mesh is returned instead. Note that vertex attributes that are `nothing` get removed before creating a mesh. """ function mesh( - mesh::Mesh{D, T, FT}; pointtype = Point{D, Float32}, + mesh::Mesh{D, T, FT}; pointtype = Point{D, Float32}, facetype::Type{<: AbstractFace} = GLTriangleFace, attributes... ) where {D, T, FT <: AbstractFace} - + names = keys(attributes) valid_names = filter(name -> !isnothing(attributes[name]), names) @@ -88,7 +88,7 @@ function mesh( else vals = getindex.(Ref(attributes), valid_names) va = NamedTuple{valid_names}(vals) - + # add vertex attributes va = merge(vertex_attributes(mesh), va) # convert position attribute and facetypes in FaceViews @@ -116,7 +116,7 @@ end triangle_mesh(primitive::GeometryPrimitive[; pointtype = Point, facetype = GLTriangleFace]) Creates a simple triangle mesh from a given `primitive` with the given `pointtype` -and `facetype`. +and `facetype`. See also: [`triangle_mesh`](@ref), [`normal_mesh`](@ref), [`uv_mesh`](@ref), [`uv_normal_mesh`](@ref) """ @@ -133,18 +133,18 @@ facetype(::Mesh{D, T, FT}) where {D, T, FT} = FT """ uv_mesh(primitive::GeometryPrimitive{N}[; pointtype = Point{N, Float32}, facetype = GLTriangleFace, uvtype = Vec2f]) -Creates a triangle mesh with texture coordinates from a given `primitive`. The -`pointtype`, `facetype` and `uvtype` are set by the correspondering keyword arguments. +Creates a triangle mesh with texture coordinates from a given `primitive`. The +`pointtype`, `facetype` and `uvtype` are set by the correspondering keyword arguments. See also: [`triangle_mesh`](@ref), [`normal_mesh`](@ref), [`uv_mesh`](@ref), [`uv_normal_mesh`](@ref) """ function uv_mesh( - primitive::AbstractGeometry{N}; pointtype = Point{N, Float32}, + primitive::AbstractGeometry{N}; pointtype = Point{N, Float32}, uvtype = Vec2f, facetype = GLTriangleFace ) where {N} - + return mesh( - primitive, uv = decompose_uv(uvtype, primitive), + primitive, uv = decompose_uv(uvtype, primitive), pointtype = pointtype, facetype = facetype ) end @@ -152,19 +152,19 @@ end """ uv_normal_mesh(primitive::GeometryPrimitive{N}[; pointtype = Point{N, Float32}, facetype = GLTriangleFace, uvtype = Vec2f, normaltype = Vec3f]) -Creates a triangle mesh with texture coordinates and normals from a given -`primitive`. The `pointtype`, `facetype` and `uvtype` and `normaltype` are set -by the correspondering keyword arguments. +Creates a triangle mesh with texture coordinates and normals from a given +`primitive`. The `pointtype`, `facetype` and `uvtype` and `normaltype` are set +by the correspondering keyword arguments. See also: [`triangle_mesh`](@ref), [`normal_mesh`](@ref), [`uv_mesh`](@ref), [`uv_normal_mesh`](@ref) """ function uv_normal_mesh( - primitive::AbstractGeometry{N}; pointtype = Point{N, Float32}, + primitive::AbstractGeometry{N}; pointtype = Point{N, Float32}, uvtype = Vec2f, normaltype = Vec3f, facetype = GLTriangleFace ) where {N} return mesh( - primitive, uv = decompose_uv(uvtype, primitive), + primitive, uv = decompose_uv(uvtype, primitive), normal = decompose_normals(normaltype, primitive), pointtype = pointtype, facetype = facetype ) @@ -173,14 +173,14 @@ end """ uv_normal_mesh(primitive::GeometryPrimitive{N}[; pointtype = Point{N, Float32}, facetype = GLTriangleFace, uvtype = Vec2f, normaltype = Vec3f]) -Creates a triangle mesh with texture coordinates and normals from a given -`primitive`. The `pointtype`, `facetype` and `uvtype` and `normaltype` are set -by the correspondering keyword arguments. +Creates a triangle mesh with texture coordinates and normals from a given +`primitive`. The `pointtype`, `facetype` and `uvtype` and `normaltype` are set +by the correspondering keyword arguments. See also: [`triangle_mesh`](@ref), [`normal_mesh`](@ref), [`uv_mesh`](@ref), [`uv_normal_mesh`](@ref) """ function normal_mesh( - points::AbstractVector{<:Point}, faces::AbstractVector{<:AbstractFace}; + points::AbstractVector{<:Point}, faces::AbstractVector{<:AbstractFace}; pointtype = Point3f, normaltype = Vec3f, facetype = GLTriangleFace ) _points = decompose(pointtype, points) @@ -191,18 +191,18 @@ end """ normal_mesh(primitive::GeometryPrimitive{N}[; pointtype = Point{N, Float32}, facetype = GLTriangleFace, normaltype = Vec3f]) -Creates a triangle mesh with normals from a given `primitive`. The `pointtype`, `facetype` and `uvtype` and `normaltype` are set -by the correspondering keyword arguments. +Creates a triangle mesh with normals from a given `primitive`. The `pointtype`, `facetype` and `uvtype` and `normaltype` are set +by the correspondering keyword arguments. See also: [`triangle_mesh`](@ref), [`normal_mesh`](@ref), [`uv_mesh`](@ref), [`uv_normal_mesh`](@ref) """ function normal_mesh( - primitive::AbstractGeometry{N}; pointtype = Point{N, Float32}, + primitive::AbstractGeometry{N}; pointtype = Point{N, Float32}, normaltype = Vec3f, facetype = GLTriangleFace ) where {N} return mesh( - primitive, normal = decompose_normals(normaltype, primitive), + primitive, normal = decompose_normals(normaltype, primitive), pointtype = pointtype, facetype = facetype) end @@ -235,10 +235,10 @@ end Generates a new mesh containing all the data of the individual meshes. -If all meshes are consistent in their use of FaceViews they will be preserved. +If all meshes are consistent in their use of FaceViews they will be preserved. Otherwise all of them will be converted with `expand_faceviews(mesh)`. -This function will generate `views` in the new mesh which correspond to the +This function will generate `views` in the new mesh which correspond to the inputs of this function. """ function Base.merge(meshes::AbstractVector{<:Mesh}) @@ -257,8 +257,8 @@ function Base.merge(meshes::AbstractVector{<:Mesh}) if !all(m -> keys(vertex_attributes(m)) == names, meshes) idx = findfirst(m -> keys(vertex_attributes(m)) != names, meshes) error( - "Cannot merge meshes with different vertex attributes. " * - "First missmatch between meshes[1] with $names and " * + "Cannot merge meshes with different vertex attributes. " * + "First missmatch between meshes[1] with $names and " * "meshes[$idx] with $(keys(vertex_attributes(meshes[idx])))." ) end @@ -276,7 +276,7 @@ function Base.merge(meshes::AbstractVector{<:Mesh}) @label DOUBLE_BREAK if consistent_face_views - + # All the same kind of face, can just merge new_attribs = NamedTuple{names}(map(names) do name if name === :position @@ -293,8 +293,8 @@ function Base.merge(meshes::AbstractVector{<:Mesh}) # TODO: is the type difference in offset bad? idx = length(faces(m1)) offset = length(coordinates(m1)) - views = isempty(m1.views) ? UnitRange{Int64}[1:idx] : copy(m1.views) - + views = isempty(m1.views) ? UnitRange{UInt32}[1:idx] : copy(m1.views) + for mesh in Iterators.drop(meshes, 1) N = length(faces(mesh)) @@ -312,7 +312,7 @@ function Base.merge(meshes::AbstractVector{<:Mesh}) push!(views, view .+ idx) end end - + idx += N offset += length(coordinates(mesh)) end @@ -347,7 +347,7 @@ function expand_faceviews(mesh::Mesh) other_fs = faces.(getproperty.((mesh,), names)) names = (:position, names...) all_fs = tuple(main_fs, other_fs...) - + if isempty(mesh.views) new_fs, maps = merge_vertex_indices(all_fs) @@ -363,7 +363,7 @@ function expand_faceviews(mesh::Mesh) else new_fs = sizehint!(eltype(main_fs)[], length(main_fs)) - new_views = sizehint!(UnitRange{Int}[], length(mesh.views)) + new_views = sizehint!(UnitRange{UInt32}[], length(mesh.views)) new_va = NamedTuple{keys(va)}(map(keys(va)) do name sizehint!(similar(values(va[name]), 0), length(va[name])) end) @@ -374,7 +374,7 @@ function expand_faceviews(mesh::Mesh) view_fs, maps = merge_vertex_indices(view.(all_fs, (idxs,)), vertex_index_counter) vertex_index_counter += length(maps[1]) - + for name in keys(new_va) map = maps[something(findfirst(==(name), names), 1)] append!(new_va[name], values(va[name])[map]) @@ -417,7 +417,7 @@ function merge_vertex_indices( # indices that remap attributes attribute_indices = ntuple(n -> sizehint!(UInt32[], N_faces), N_Attrib) - # keep track of the remmaped indices for one vertex so we don't have to + # keep track of the remmaped indices for one vertex so we don't have to # query the dict twice temp = Vector{T}(undef, N) @@ -430,7 +430,7 @@ function merge_vertex_indices( # if the vertex exists, get it's index # otherwise register it with the next available vertex index - temp[i] = get!(vertex_index_map, vertex) do + temp[i] = get!(vertex_index_map, vertex) do vertex_index_counter += 1 push!.(attribute_indices, vertex) return vertex_index_counter - 1 @@ -454,10 +454,10 @@ end Creates a new mesh containing `faces(mesh)[range]` for each range in `views`. This also removes unused vertices. """ -function split_mesh(mesh::Mesh, views::Vector{UnitRange{Int}} = mesh.views) +function split_mesh(mesh::Mesh, views::Vector{<: UnitRange{<: Integer}} = mesh.views) return map(views) do idxs new_fs, maps = merge_vertex_indices((view(faces(mesh), idxs),)) - + names = keys(vertex_attributes(mesh)) new_va = NamedTuple{names}(map(names) do name v = getproperty(mesh, name) diff --git a/test/meshes.jl b/test/meshes.jl index 4c42cb3e..6265231d 100644 --- a/test/meshes.jl +++ b/test/meshes.jl @@ -39,7 +39,7 @@ end position = GeometryBasics.FaceView(Point2f[(0, 0), (1, 0), (1, 1), (0, 1)], [QuadFace(1,2,3,4)]), normal = GeometryBasics.FaceView([Vec3f(0,0,1)], [QuadFace(1)]) ) - + m2 = GeometryBasics.expand_faceviews(m) @test faces(m) == [QuadFace(1,2,3,4)] @@ -60,8 +60,8 @@ end GeometryBasics.Mesh(coordinates(r), faces(r), normal = normals(r)) end dm = merge(direct_meshes) - - @test GeometryBasics.facetype(dm) == QuadFace{Int64} + + @test GeometryBasics.facetype(dm) == QuadFace{Int} @test length(faces(dm)) == 27 * 6 # 27 rects, 6 quad faces @test length(normals(dm)) == 27 * 6 @test length(coordinates(dm)) == 27 * 8 @@ -69,7 +69,7 @@ end @test coordinates(dm) isa Vector @test !allunique([idx for f in faces(dm) for idx in f]) @test !allunique([idx for f in faces(dm.normal) for idx in f]) - + indirect_meshes = map(rects) do r m = GeometryBasics.mesh(coordinates(r), faces(r), normal = normals(r), facetype = QuadFace{Int64}) # Also testing merge of meshes with views @@ -79,6 +79,7 @@ end im = merge(indirect_meshes) @test im == dm + @test GeometryBasics.facetype(im) == QuadFace{Int64} converted_meshes = map(rects) do r m = GeometryBasics.Mesh(coordinates(r), faces(r), normal = normals(r)) @@ -86,14 +87,14 @@ end end cm = merge(converted_meshes) - @test GeometryBasics.facetype(cm) == QuadFace{Int64} + @test GeometryBasics.facetype(cm) == QuadFace{Int} @test length(faces(cm)) == 27 * 6 # 27 rects, 6 quad faces @test length(normals(cm)) == 27 * 6 * 4 # duplicate 4x across face @test length(coordinates(cm)) == 27 * 8 * 3 # duplicate 3x across shared vertex @test normals(cm) isa Vector @test coordinates(cm) isa Vector @test allunique([idx for f in faces(cm) for idx in f]) - + mixed_meshes = map(direct_meshes, indirect_meshes, converted_meshes) do dm, im, cm rand((dm, im, cm)) # (with FaceView, with mesh.views & FaceView, w/o FaceView) @@ -111,7 +112,7 @@ end @testset "Extracting faces from position FaceView" begin # can't extract from array @test_throws TypeError Mesh(position = ps, normal = ns) - + m = Mesh(position = FaceView(ps, fs), normal = ns) @test coordinates(m) == ps @test normals(m) == ns @@ -124,7 +125,7 @@ end m = Mesh(rand(Point2f, 12), fs) @test length(m.position) == 12 @test length(m.faces) == 4 - + @test_throws ErrorException Mesh(ps, fs, normal = rand(Vec3f, 8)) m = Mesh(ps, fs, normal = rand(Vec3f, 12)) @test length(m.position) == 10 @@ -153,7 +154,7 @@ end fs = GLTriangleFace[(1,2,3), (3,4,5), (5,6,7), (8,9,10)] m = Mesh(ps, fs, normal = ns, uv = uvs) - + @test vertex_attributes(m) == getfield(m, :vertex_attributes) @test coordinates(m) == vertex_attributes(m)[:position] @test normals(m) == vertex_attributes(m)[:normal] @@ -199,19 +200,19 @@ end @test ps isa Vector{Point3d} ns = normals(r) @test length(ns) == 6 - @test ns isa GeometryBasics.FaceView{Vec3f, Vector{Vec3f}, Vector{QuadFace{Int64}}} + @test ns isa GeometryBasics.FaceView{Vec3f, Vector{Vec3f}, Vector{QuadFace{Int}}} uvs = texturecoordinates(r) @test length(uvs) == 8 @test_broken uvs isa Vector{Vec2f} fs = faces(r) @test length(fs) == 6 - @test fs isa Vector{QuadFace{Int64}} + @test fs isa Vector{QuadFace{Int}} end @testset "normal_mesh()" begin m = normal_mesh(r, pointtype = Point3f, normaltype = Vec3f) m = GeometryBasics.expand_faceviews(m) - + @test hasproperty(m, :position) @test coordinates(m) isa Vector{Point3f} @test length(coordinates(m)) == 24 @@ -231,7 +232,7 @@ end @testset "normal_uv_mesh()" begin m = uv_normal_mesh( - r, pointtype = Point3d, normaltype = Vec3d, + r, pointtype = Point3d, normaltype = Vec3d, uvtype = Vec3d, facetype = QuadFace{Int32} ) diff --git a/test/runtests.jl b/test/runtests.jl index f8eb0f8a..a12aba7c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -133,7 +133,7 @@ end @test faces(mesh) == [TetrahedronFace{Int64}(1,2,3,4)] @test decompose(LineFace{Int64}, mesh) == LineFace{Int64}[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] @test decompose(GLTriangleFace, mesh) == GLTriangleFace[(2, 3, 4), (1, 3, 4), (1, 2, 4), (1, 2, 3)] - + points = rand(Point3f, 8) tfaces = [GLTriangleFace(1, 2, 3), GLTriangleFace(5, 6, 7)] ns = rand(Vec3f, 8) @@ -268,25 +268,25 @@ end @testset "Offsetintegers" begin x = 1 - @test GeometryBasics.raw(x) isa Int64 + @test GeometryBasics.raw(x) isa Int @test GeometryBasics.value(x) == x x = ZeroIndex(1) - @test eltype(x) == Int64 + @test eltype(x) == Int x = OffsetInteger{0}(1) - @test typeof(x) == OffsetInteger{0,Int64} + @test typeof(x) == OffsetInteger{0,Int} x1 = OffsetInteger{0}(2) x2 = 1 @test Base.to_index(x1) == 2 - @test -(x1) == OffsetInteger{0,Int64}(-2) - @test abs(x1) == OffsetInteger{0,Int64}(2) - @test +(x, x1) == OffsetInteger{0,Int64}(3) - @test *(x, x1) == OffsetInteger{0,Int64}(2) - @test -(x, x1) == OffsetInteger{0,Int64}(-1) + @test -(x1) == OffsetInteger{0,Int}(-2) + @test abs(x1) == OffsetInteger{0,Int}(2) + @test +(x, x1) == OffsetInteger{0,Int}(3) + @test *(x, x1) == OffsetInteger{0,Int}(2) + @test -(x, x1) == OffsetInteger{0,Int}(-1) #test for / - @test div(x, x1) == OffsetInteger{0,Int64}(0) + @test div(x, x1) == OffsetInteger{0,Int}(0) @test !==(x, x1) @test !>=(x, x1) @test <=(x, x1)