From a790958907688af97d62cd259a376ffb1397cd19 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 7 Feb 2023 15:06:10 -0600 Subject: [PATCH 01/91] pr132 run-able --- src/MAT_v4_pr132.jl | 322 +++++++++++++++++++++++++++++++++++++++++ test/runtests_pr132.jl | 37 +++++ 2 files changed, 359 insertions(+) create mode 100644 src/MAT_v4_pr132.jl create mode 100644 test/runtests_pr132.jl diff --git a/src/MAT_v4_pr132.jl b/src/MAT_v4_pr132.jl new file mode 100644 index 0000000..1952779 --- /dev/null +++ b/src/MAT_v4_pr132.jl @@ -0,0 +1,322 @@ +# Copied from pr132 of https://github.com/JuliaIO/MAT.jl/pull/132 + +# MAT_v4.jl +# Tools for reading MATLAB v4 files in Julia +# +# Copyright (C) 2012 Simon Kornblith +# Copyright (C) 2019 Victor Saase (modified from MAT_v5.jl) +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# MATLAB's file format documentation can be found at +# http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf + +module MAT_v4_pr132 +using BufferedStreams, HDF5, SparseArrays +import Base: read, write, close +import HDF5: names + +round_uint8(data) = round.(UInt8, data) +complex_array(a, b) = complex.(a, b) + +mutable struct Matlabv4File <: HDF5.H5DataStore + ios::IOStream + swap_bytes::Bool + varnames::Dict{String, Int64} + + Matlabv4File(ios, swap_bytes) = new(ios, swap_bytes) +end + +const mLITTLE_ENDIAN = 0 +const mBIG_ENDIAN = 1 +const mVAX_DFLOAT = 2 +const mVAX_GFLOAT = 3 +const mGRAY = 4 + +const pTYPE = Dict( + 0 => Float64, + 1 => Float32, + 2 => Int32, + 3 => Int16, + 4 => UInt16, + 5 => UInt8 +) + +const tNUMERIC = 0 +const tTEXT = 1 +const tSPARSE = 2 + +const imagfREAL = 0 +const imagfCOMPLEX = 1 + +read_bswap(f::IO, swap_bytes::Bool, ::Type{T}) where T = swap_bytes ? bswap(read(f, T)) : read(f, T) + +function read_bswap(f::IO, swap_bytes::Bool, ::Type{T}, dim::Union{Int, Tuple{Vararg{Int}}}) where T + d = read!(f, Array{T}(undef, dim)) + if swap_bytes + for i = 1:length(d) + @inbounds d[i] = bswap(d[i]) + end + end + d +end + +function read_bswap(f::IO, swap_bytes::Bool, d::AbstractArray{T}) where T + readbytes!(f, reinterpret(UInt8, d)) + if swap_bytes + for i = 1:length(d) + @inbounds d[i] = bswap(d[i]) + end + end + d +end + +function checkv4(f::IO) + M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, false) + if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 + swap_bytes = false + return (true, swap_bytes) + else + seek(f, 0) + M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, true) + if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 + swap_bytes = true + return (true, swap_bytes) + end + end + return (false, false) +end + +# Read data type and number of bytes at the start of a data element +function read_header(f::IO, swap_bytes::Bool) + dtype = read_bswap(f, swap_bytes, Int32) + + M = div(rem(dtype, 10000), 1000) + O = div(rem(dtype, 1000), 100) + P = div(rem(dtype, 100), 10) + T = div(rem(dtype, 10), 1) + + mrows = read_bswap(f, swap_bytes, Int32) + ncols = read_bswap(f, swap_bytes, Int32) + imagf = read_bswap(f, swap_bytes, Int32) + namlen = read_bswap(f, swap_bytes, Int32) + + M, O, P, T, mrows, ncols, imagf, namlen +end + +# Read matrix data +function read_matrix(f::IO, swap_bytes::Bool) + M, O, P, T, mrows, ncols, imagf, namlen = read_header(f, swap_bytes) + if ncols == 0 || mrows == 0 + # If one creates a cell array using + # y = cell(m, n) + # then MATLAB will save the empty cells as zero-byte matrices. If one creates a + # empty cells using + # a = {[], [], []} + # then MATLAB does not save the empty cells as zero-byte matrices. To avoid + # surprises, we produce an empty array in both cases. + return ("", Matrix{Union{}}(undef, 0, 0)) + end + name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) + if T == tNUMERIC || T == tSPARSE + real_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) + if T == tNUMERIC && imagf == imagfCOMPLEX + imag_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) + data = complex.(real_data, imag_data) + else + data = real_data + end + datamat = reshape(data, Int(mrows), Int(ncols)) + if T == tNUMERIC + return (name, datamat) + elseif T == tSPARSE + if size(datamat,2) == 3 + return (name, sparse(datamat[:,1], datamat[:,2], datamat[:,3])) + else + return (name, sparse(datamat[:,1], datamat[:,2], complex.(datamat[:,3], datamat[:,4]))) + end + end + elseif T == tTEXT + if mrows > 1 + charvec = UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows))) + charmat = reshape(charvec, Int(mrows), Int(ncols)) + data = [String(charmat[i,:]) for i in 1:mrows] + else + data = String(UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols)))) + end + return (name, data) + end +end + +# Open MAT file for reading +matopen(ios::IOStream, endian_indicator::Bool) = Matlabv4File(ios, endian_indicator) + +# Read whole MAT file +function read(matfile::Matlabv4File) + seek(matfile.ios, 0) + vars = Dict{String, Any}() + while !eof(matfile.ios) + (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) + vars[name] = data + end + vars +end + +# Read only variable names +function getvarnames(matfile::Matlabv4File) + if !isdefined(matfile, :varnames) + seek(matfile.ios, 0) + matfile.varnames = varnames = Dict{String, Int64}() + while !eof(matfile.ios) + offset = position(matfile.ios) + M, O, P, T, mrows, ncols, imagf, namlen = read_header(matfile.ios, matfile.swap_bytes) + f = matfile.ios + + name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) + varnames[name] = offset + imag_offset = 0 + skip(f, mrows*ncols*sizeof(pTYPE[P])) + if imagf == imagfCOMPLEX + skip(f, mrows*ncols*sizeof(pTYPE[P])) + end + end + end + matfile.varnames +end + +names(matfile::Matlabv4File) = keys(getvarnames(matfile)) + +# Read a variable from a MAT file +function read(matfile::Matlabv4File, varname::String) + varnames = getvarnames(matfile) + if !haskey(varnames, varname) + error("no variable $varname in file") + end + seek(matfile.ios, varnames[varname]) + (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) + data +end + +function colvals(A::AbstractSparseMatrix) + rows = rowvals(A) + cols = similar(rows) + m,n = size(A) + for i=1:n + for j in nzrange(A,i) + cols[j] = i + end + end + cols +end + +function write(parent::Matlabv4File, name::String, s) + M = Int(parent.swap_bytes) + O = 0 + P = 0 + for p=keys(pTYPE) + if eltype(s) == pTYPE[p] || eltype(s) == Complex{pTYPE[p]} + P = p + end + end + if pTYPE[P] != eltype(s) && Complex{pTYPE[P]} != eltype(s) && + !(s isa AbstractString && pTYPE[P] == Float64) && + !(s isa Vector{String} && pTYPE[P] == Float64) + error("invalid value type when writing v4 file") + end + if s isa AbstractSparseMatrix + T = tSPARSE + elseif s isa AbstractString || s isa Vector{String} + T = tTEXT + else + T = tNUMERIC + end + write(parent.ios, Int32(1000*M + 100*O + 10*P + T)) + + mrows = 1 + ncols = 1 + if s isa AbstractVector && !(s isa Vector{String}) + ncols = length(s) + elseif s isa AbstractMatrix + if s isa AbstractSparseMatrix + mrows = nnz(s) + ncols = 3 + if eltype(s) <: Complex + ncols = 4 + end + else + mrows, ncols = size(s) + end + elseif s isa Vector{String} + ncols = length(s[1]) + mrows = length(s) + elseif s isa AbstractString + ncols = length(s) + end + write(parent.ios, Int32(mrows)) + write(parent.ios, Int32(ncols)) + + imagf = 0 + if eltype(s) <: Complex && T == tNUMERIC + imagf = 1 + end + write(parent.ios, Int32(imagf)) + + namlen = length(name) + 1 + write(parent.ios, Int32(namlen)) + + write(parent.ios, Vector{UInt8}(name)) + write(parent.ios, UInt8(0)) + + if s isa AbstractArray && T == tNUMERIC + write(parent.ios, reshape(real(s), length(s))) + if imagf == 1 + write(parent.ios, reshape(imag(s), length(s))) + end + elseif s isa Number + write(parent.ios, real(s)) + if imagf == 1 + write(parent.ios, imag(s)) + end + elseif s isa AbstractString + floatarray = Float64.(Vector{UInt8}(s)) + write(parent.ios, floatarray) + elseif s isa Vector{String} + floatarray = Matrix{Float64}(undef, mrows, ncols) + for (i,strel) = enumerate(s) + floatarray[i,:] = Float64.(Vector{UInt8}(strel)) + end + write(parent.ios, floatarray) + elseif T == tSPARSE + rows = rowvals(s) + cols = colvals(s) + vals = nonzeros(s) + write(parent.ios, pTYPE[P].(rows)) + write(parent.ios, pTYPE[P].(cols)) + write(parent.ios, pTYPE[P].(real(vals))) + if eltype(s) <: Complex + write(parent.ios, pTYPE[P].(imag(vals))) + end + end +end + +# Close MAT file +close(matfile::Matlabv4File) = close(matfile.ios) + +end diff --git a/test/runtests_pr132.jl b/test/runtests_pr132.jl new file mode 100644 index 0000000..ee4eea2 --- /dev/null +++ b/test/runtests_pr132.jl @@ -0,0 +1,37 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +include("../src/MAT.jl") +include("../src/MAT_v4_pr132.jl") + +mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + +@testset "checkv4" begin + # function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Bool, ff::Bool, compress::Bool) + # function matopen(fname::AbstractString, mode::AbstractString; compress::Bool = false) + # mat = MAT.matopen(mat1s, "r" ) + + rawfid = open(mat1s, "r") + (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) + close(rawfid) + + @test isv4 == true +end + +rawfid = open(mat1s, "r") +(isv4, swap_bytes) = MAT_v4.checkv4(rawfid) +mat = MAT_v4_pr132.matopen(rawfid, swap_bytes ) + +@testset "getvarnames" begin + # function getvarnames(matfile::Matlabv4File) + @show gvn = MAT_v4_pr132.getvarnames(mat) + # gvn = MAT_v4.getvarnames(mat) = Dict("name" => 71, "Aclass" => 0, "dataInfo" => 448328, "data_2" => 501296, "data_1" => 488197, "description" => 117126) +# ...and this is not correct + @test false +end + + +close(rawfid) \ No newline at end of file From f0e13a03e51588a703e72ea85114f186426eea42 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 7 Feb 2023 15:06:36 -0600 Subject: [PATCH 02/91] pr164 run-able --- src/MAT_v4_pr164.jl | 59 ++++++++++++++++++++++++++++++++++++++++++ test/runtests_pr164.jl | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/MAT_v4_pr164.jl create mode 100644 test/runtests_pr164.jl diff --git a/src/MAT_v4_pr164.jl b/src/MAT_v4_pr164.jl new file mode 100644 index 0000000..20f3479 --- /dev/null +++ b/src/MAT_v4_pr164.jl @@ -0,0 +1,59 @@ +module MAT_v4_pr164 + +import Base: read, write, close + +mutable struct Matlabv4File + ios::IOStream + varnames::Dict{String, Int64} + + Matlabv4File(ios) = new(ios) +end + +const V4_ELTYPE = [Float64, Float32, Int32, Int16, UInt16, UInt8] + +matopen(ios::IOStream) = Matlabv4File(ios) + +function unpack_header_type(type::Int32) + T, P, O, M = digits(type; pad=4) + iszero(O) || error("file is not a v4 MAT file (magic digit not 0)") + iszero(M) || error("only little endian v4 MAT currently supported") + iszero(T) || error("only full numeric matrices supported for v4 MAT files") + + P in 0:5 || error("invalid eltype digit in v4 MAT file: $P") + eltype = V4_ELTYPE[P+1] + + return eltype +end + +function read_one_mat!(mat::Matlabv4File) + type, mrows, ncols, imagf, namlen = read!(mat.ios, Vector{Int32}(undef, 5)) + eltype = unpack_header_type(type) + + iszero(imagf) || error("no imaginary matrix support") + + name = String(read!(mat.ios, Vector{UInt8}(undef, namlen-1))) + + # one null byte before start of matrix data + Base.read(mat.ios, UInt8) + + @show mrows, ncols + value = read!(mat.ios, Matrix{eltype}(undef, mrows, ncols)) + + return name => value +end + +function read(mat::Matlabv4File) + seekstart(mat.ios) + + results = Dict{String,Any}() + while !eof(mat.ios) + name, value = read_one_mat!(mat) + results[name] = value + end + + return results +end + +close(mat::Matlabv4File) = close(mat.ios) + +end # module diff --git a/test/runtests_pr164.jl b/test/runtests_pr164.jl new file mode 100644 index 0000000..dea51b4 --- /dev/null +++ b/test/runtests_pr164.jl @@ -0,0 +1,48 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +include("../src/MAT.jl") +include("../src/MAT_v4_pr164.jl") + +mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + + + +@testset "unpack_header" begin + rawfid = open(mat1s, "r") + # (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) + + # From 'Version-4-MAT-File-Format.pdf': + # Each matrix starts with a fixed-length 20-byte header that contains information describing certain attributes of the Matrix. + # The 20-byte header consists of five long (4-byte) integers: + @show type, mrows, ncols, imagf, namlen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + # P indicates which format the data is stored: 5 = 8bit uint + # T indicates matrix type: 1 = text matrix + + @show eltype = MAT_v4_pr164.unpack_header_type(type) + + + close(rawfid) + + # @test isv4 == true +end + +# rawfid = open(mat1s, "r") +# (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) +# mat = MAT_v4.matopen(rawfid, swap_bytes ) + +# @testset "getvarnames" begin +# # function getvarnames(matfile::Matlabv4File) +# @show gvn = MAT_v4.getvarnames(mat) +# # gvn = MAT_v4.getvarnames(mat) = Dict("name" => 71, "Aclass" => 0, "dataInfo" => 448328, "data_2" => 501296, "data_1" => 488197, "description" => 117126) +# # ...and this is not correct +# @test false +# end + + +# close(rawfid) \ No newline at end of file From f6883c05c16d4cdfae2c6c808a1604c07be5d6c6 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 7 Feb 2023 15:07:43 -0600 Subject: [PATCH 03/91] started reader write --- src/MAT.jl | 13 +- src/MAT_v4_Modelica.jl | 18 ++ src/Matlab_MATFileFormatV4.pdf | Bin 0 -> 23557 bytes ...aUserGuide_v1.21.0-dev-211-gcd8969a225.pdf | Bin 0 -> 85780 bytes test/readwrite4.jl | 52 +++++ test/runtests.jl | 9 + test/runtests_modelica_manual.jl | 126 +++++++++++ test/runtests_modelica_readMatrix.jl | 205 ++++++++++++++++++ test/v4/testcomplex_4.2c_SOL2.mat | Bin 0 -> 176 bytes test/v4/testdouble_4.2c_SOL2.mat | Bin 0 -> 103 bytes test/v4/testmatrix_4.2c_SOL2.mat | Bin 0 -> 151 bytes test/v4/testminus_4.2c_SOL2.mat | Bin 0 -> 38 bytes test/v4/testmulti_4.2c_SOL2.mat | Bin 0 -> 240 bytes test/v4/testonechar_4.2c_SOL2.mat | Bin 0 -> 40 bytes test/v4/testsparse_4.2c_SOL2.mat | Bin 0 -> 223 bytes test/v4/testsparsecomplex_4.2c_SOL2.mat | Bin 0 -> 294 bytes test/v4/teststring_4.2c_SOL2.mat | Bin 0 -> 375 bytes test/v4/teststringarray_4.2c_SOL2.mat | Bin 0 -> 156 bytes test/v4/testvec_4_GLNX86.mat | Bin 0 -> 93 bytes 19 files changed, 417 insertions(+), 6 deletions(-) create mode 100644 src/MAT_v4_Modelica.jl create mode 100644 src/Matlab_MATFileFormatV4.pdf create mode 100644 src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf create mode 100644 test/readwrite4.jl create mode 100644 test/runtests_modelica_manual.jl create mode 100644 test/runtests_modelica_readMatrix.jl create mode 100644 test/v4/testcomplex_4.2c_SOL2.mat create mode 100644 test/v4/testdouble_4.2c_SOL2.mat create mode 100644 test/v4/testmatrix_4.2c_SOL2.mat create mode 100644 test/v4/testminus_4.2c_SOL2.mat create mode 100644 test/v4/testmulti_4.2c_SOL2.mat create mode 100644 test/v4/testonechar_4.2c_SOL2.mat create mode 100644 test/v4/testsparse_4.2c_SOL2.mat create mode 100644 test/v4/testsparsecomplex_4.2c_SOL2.mat create mode 100644 test/v4/teststring_4.2c_SOL2.mat create mode 100644 test/v4/teststringarray_4.2c_SOL2.mat create mode 100644 test/v4/testvec_4_GLNX86.mat diff --git a/src/MAT.jl b/src/MAT.jl index e6c5824..4c25d7a 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -59,6 +59,7 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo end end + # Check for MAT v5 file seek(rawfid, 124) version = read(rawfid, UInt16) @@ -165,12 +166,12 @@ end ### v0.10.0 deprecations ### -import HDF5: exists -export exists -@noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) - Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) - return haskey(matfile, varname) -end +# import HDF5: exists +# export exists +# @noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) +# Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) +# return haskey(matfile, varname) +# end @noinline function Base.names(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) Base.depwarn("`names(matfile)` is deprecated, use `keys(matfile)` instead.", :names) return keys(matfile) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl new file mode 100644 index 0000000..f739642 --- /dev/null +++ b/src/MAT_v4_Modelica.jl @@ -0,0 +1,18 @@ +module MAT_v4_Modelica + +import Base: read, write, close + +mutable struct ModelicaV4 + ios::IOStream + varnames::Dict{String, Int64} + ModelicaV4(ios) = new(ios) +end + + +function read(mat::Matlabv4File) + seekstart(mat.ios) + + +end + +end #module \ No newline at end of file diff --git a/src/Matlab_MATFileFormatV4.pdf b/src/Matlab_MATFileFormatV4.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8cbe7e72d5a33ded36139939783ed74ba7646eae GIT binary patch literal 23557 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 M97s}9O-~*4Kg~M4yZ`_I literal 0 HcmV?d00001 diff --git a/src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf b/src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9f0db31aadd2215647a07e05568a3cf7135473da GIT binary patch literal 85780 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 z zWGSE3TTB-GRcTMoDX721icD*OzDtHSQ?;W1Xe}ETlz+LkqD6zV=NKfM1_7j9qtHh2 zvc^~STa8)!B=vgdfS<#Q)B^LAFARIH1BQ?nB&cJRV&(GkuF#!#H|jx1wHkucPw2@hWTd;vs;G13 zO{u@y7e!%+qN!$^bv6nmnI&EXN5g#$9yX0(8sSJG<0&y@Q0wPZtskAUz1I+*Wvz0s z&tsC6vLMyUQ-W;S7gl06F|O*=8sD;-5mscn4ai?#MQJPnJ+{6Y*hDu_$8cPV{bc?; z+kw4tb1e)Rj6__ZRFb#cS*|RFr9+$~brB1FfUT;X4CDIh88@9RN9JOnvQ(r|)XHDf ziB4qU%$t>|2pZIXW3Ctw%$F!T_2f47wNeW`UXNpmP@IBD1`v1mqGLx5^9sbWIPWdb|MnzP%RPtC0S{r!$YAGYdW#Py7RkLJxaPKO$*y2(t#JX~P z>MxjH!@~6EXI_@tWiv)v8i`ZL<@FDk_1~?1(j2{cRXW}kt{Kc8QpB&-mquYoV4do5 zJa-aMJ)BIdNWLjx9G03X9_H}eC?rA?=Wlata9l|+zNM_Zs6&%e<$Jx2aV3%93_ht# z&U9pNWb+6s9{iBF#0uuKm{l^4m|vkFSL>!i9_bmOm=9P`G^fF!$AR8Zrc-+`et4d; z-@KMRwky8`?{fod18c2aINlT!*1)6g#!crJlUmA?AP0+WiM=3&CKx2<-n)FjzV>h5 z+-irvY>tiE2i#Aa=pvCN!SHf&BN?qh>aGeUDhzCs{4exhmFq;}rLRPrZVPcR#H(;P$DJ4XGu zI#nv@yhAQTsw3LkDk_7`m2VV?MA;n$O-&8OrG{6l!+MJIj^&E(?qk^xXBN^t%uJdZ zG>qG)2I|AZd$rW0MfB<#U&KuYy~Lm2NZRWCi_WcB$>$naP~tKcU5;uc>>Qu8k;M-q zWKg;AW?D5XD5gK%)|Pxsbr3qf4}TbcW*Z5-Z4HBp$h=;35irXkoga86-SErCv(LIQ zC(;h0AhOW1XuRUx4H`Za%hwsS?Xu4QnCnta;#_jFb0^T_`y)ewd6-7vLmcu~TwLkr zI#Os~j)*d~{ixV^!>T89&kQHRT@ISZrqTwp(2o?~6J?@d(v&wVz8JmKo29Lk4me`* zI@%;i1(jAiTdLac!U*F~SF00(d1^19nXPSEFG@<{duBMWjcJorf7v_T#DGiE4rvq$dr};W zxCMT*Z=cBx|og0s1Ifwp!P2 z-v$|z)Y5<%s0T>5EGl8S+A!!s*^P{8{RuMg8+1GBhEbL(b}{IhxwLHzO-iih!>pVF z5^HD_iPz^Cl|= zOnRO{h=S0T(!}`JM$x^Giw} zj>TPV6B@-A1S|AAI;RRR#kZHxMoJ^UAY-R1&*C&TdO5Z(Vk^})Ffi?PBZobwaj)QO z`%_CKb5XZ!)D?e!Ox=vDWupJ=4)(6SGaknC`7t(+tcp%d;^d8qH9BN+A+Etwdu+DM4g!|(6qH6%haCK0cR z6JdakL^LK)cGf_!!-uioBz|vJnE9eApx+`AW^~7nPuzuvl(-yx(NQBvE$+8Hc~{=I z(m`KTl_Ab>84niFzWb!+rf^_ojcb z$$N~Zf%3%E#KiUS4r`*Wj#4dFb~bHw(YCqR#&aO`Nl%;wE0M>@6A|1E94|lcjo^VC zZn))YGA~}PI7Um-Dj&?mKc7kX#?vekQJ8bTEp>_co8mfAi8pXJG)OOaKu?(N@>T8z z?M{;pm!HNW@>!D*qHXG@f|7JnzPsQb`DI@tSfj7N#*{E*`i|IWT$(cXM933~BF&p6 zZmVw))y8OR?<%yURdVP*C1qov)RoQA?;gJ}2b4|3AJzy<@_n`OE}9Rcsg^kDI!mXr zH+i1_bS>Bsm5epiN)?Y`Pibie{I0AFJN>hW<%kU*X5&b$3EAK3+p_&hw;+O~%dz~qG3My z**4*ax=8yc$Q|6sS58|un_cX|4t-AtVY?X-@}p{bGo(dTO@`>=dghFTZyFk2gvk5s z?!t0dY~HbC*nT?SBVVjKP@ZV4LQ9n`L$BUX&O`tf0^O3I=1=u|Ix!_OlW5mX(=VeV zd+0uMw7?8QmFMut?ZaOevcH|zFP05}Na{8mbWl0k&ngZ2Ju)ZDn)PVC^grE7g)ns* zzKzKnCN`8N?lReg7yTJywskb7m_Qrkd!>AaHS^v<+L z9uN8hrZNU31i_rf%xi7gc3{^RV%#YwW!BZsG1P%0BTRW6IVLH%o{_ zgvV-{{K#!*LJctO=@#ll82jq{gJLs6b;2vHI^l)I(&O?LN`-N$&7{B#OxaNtW8s-` z3nc@a91?f_fqHptCs`xszFk#Q{`He#8J{4>BwQJb1FiH(bm_VqZh9xFl>S5ddSuBe zhlap)UNc3Dk{Q!4%gG@Wq0Glz>U-r#e&2`Ij>MJ|8hsM*4UPul3=~0`!X}a^(#kQ=+0}+uGkZn~#1lzVpDCLa_tFh{Llo@^Fok#Xk$SOZ-VD6n#+s=I32~zu;?c2eCmp7#yv2Z)CGVToA~*IKWq(s+`ecG?n()CAWuxRq?Zo)Rs+9`l5GQk%XK zuV-P?A+ec+dv zOaHqkic{@3oy2Xd7a&h?oPdp?)$@Q0w@)1(Pe-*+J#J;aOhT^2$j;dtx5Pvlwh`a^ z22w{%9>qW44CM$DKiJwXkpc`Ckw;b<#|d2V@4D~r5jYofKR-}i0E_bgy9{wjFp%rs07fP+*Gy^S{-0t9O5UlUsgtPvZysx$q1(VW~ zf~!9oHrr;Q2EE^aXni-jVO0jwa3g?k5T3lGwR1UV)ejhsU=NbM0&d6H61$;%AVj`d zp!4Ats+jUP_}I(~Gwhyk>z)qQ>L87k(Y#Mi^?ibK$j-ez@l^^dQH^_!^A+vpN~_)G zDGlwU$Sc`(v!kzYwI>##DEW;EP4JR#E8mlzi5q#~+8aCNf?MT2g!h@q7gQ_EzY$l1SR0{}ih*iO>xsWM|Q>qq=h4p;_vSyn(by{H1N`_c5$2&H5_d1qDqQNN_F|%ie0rxbs^|SUC^Ty z2{%x7!LJSEwlViC_Mw>z4gjdH2zBNcJ^Lr))}PCk<`s8LhMXGe&yNfSngU zk_D6_KkY&k%_67VD55odW~LbL0htjn6MZQ?ar7wFN*DOi*kh#Q4bpx~v6Ymk7}0|% zN?X3>-EC`2va3Zsc8i$BS|#-_e0pkFNDeX|SH0<0>A^-m6%#R=h4Pv5tCCtk=95{4 z=7yP=Xd^&=V+aMI+AE>5#@ukTK>T6JKx9|%J2RYLE0(U@NJe#=KgF#}X>a+k4gp&x z3VQqc&Fh>zQ-h||26}3J(chA909AAdhA-K>Yc~QBm(SdXmJ-3Xmzi*rf|5kzPzSL@`31F&jZ>~v@J6$N1ASu% z`>6UA13L1{lP{wh0?LQFaQFH7tj%_IRp(U*=(gtaJ28sZR8nZK;AvJeDNlQ@Toy1{ z*CuIrvizcaMlFN{VcYsZs-exzFS5pHQn2?@66b0Jkg2Sk_12pgyp#~)2f;m`5oe6< z*=DHjiKjJefzvy2+!^~I`O{*=fY)vo%L$6N-r&*~ zvG5DYt|h6F9ljE7zeDFGCOkyn{JbWfl6?7rBckdW3tkqJjOe)<=EVG`+J3h1TUwNPd>Xv+dK?8c?QQ{hiOZ0^e0Y;Gaj;4!-k5?4pAN(Cu= z{~6MT;cpi#sPNMjGwQ*YJmu<5kwUVgywKQ{l zrB777P%U2vyGBPb5HHvJ^bpot>;+$-StGjF&mLI)it=|_dlSKyw~Rn&59nj@Wj+!j zdn*izh-YTUU#1xm`Z^Akg+fOjdmy3?(cVHs=d?qRLv2?hKJEk&vKBoPCz^*a`iucd zr1_mj>t6A{eHVRuD36?t7Mhj^g>;nW-3DTVa-Y$;sl~>qw1ak~it0jiUPu(OP4}Kh zn^bLuqJ=PDKt?SM%hY7}Jesh!TwP+OQIG#ti{15bg+jX*1vc0M6v>jx4A2wfDUZHJm>zPgpPHGxS(wG^2If&KytioYUyeuNMW zfd^3!O#_;o!JRP?tjY5j6-_|3ZMI+XkJ6;PJI@7#fmks!gD#@CW z$E`{LT*5EDZL))j{Uu-qHU7ZjgFfndg3{Bn2v}NVoT38D2)aF1*mO=#Qog^Z>~ozE|rP%A*f$`tBYhF_~i@Nov9AOWe|A zfbnai)INd^3mV8iXhKmKP*Q>ihJSHgzQy}#suOG8 z3#~nORV3tFYbCkH?a)%cKY?#jg3cSf68V+xyYU{aX`YU;!zYBe{zK(NxBLxd0hzpN zls;52Q#o@1%3FK^$lc1+IZ`1_UQ0iix*s$+a!{nbt-kC@p0%7Tjx3Zw-vo{bZ&>3b zgC9Tpy&xGbepvm$kj(|taz&4~xaoDXqtH`Lj>%p}_o@`4_l(7IHOSJfYo}dfFMfSH6lR?VuL{(<*v2Ez0 zr1bm(7sqn=bAb{(3;!FWTpV6km}mqHS)Os9uh83uKPnQgBxYXrrzYZ=JeePsf+h|i zUgFG0PHc=5N45vx`)e}!frFzl{We!1Oc-*3BT)hWo%kw3g8K?I-NBpepW9n}A|n>$ zSM}!m-gv^J6XoTL%pH-o_yHawV##LTop-Z84W!tQHScrgD6260G{S(?b;g#-8zSSF z__;%yaFkimJ)_1L$Xor+!=`!qJC)%EmUF_^Mi-BV=2!>hp-}~f3-5El0%LRXLiKKe z_5^`ueK-%Vf1AV0*Jz6&L*Q+=DD#vZ9?)ElejF$q4(pR%x;|hyv_~);xobF&3g7p2 z73lwoF1u3ohAL}B%18iOT(8r`lqMP@X7vlG||j_9>yZB>nNpB=?gn^gc&_x zUIAWC^Wy;%-wsP@WC+scY?;7#`f)OPn3%TYWqb0kf zxTPGqV>)4LctpBh$wqYm9U|qjNF_f8NPii2<1lfWkEGz*7M)i?eB(-g;}SA!WYRSI zo&+a)y`H)U=H(NE1MSY6CbC`NWwO`P(Tme|xV7QlHQMp;BZ>#?5G?Wy9>Fb{N75nP z^N8`5rr^>9sFfBRr1!X5T_faK7O8%bhg$)7l4;vbG#-6Jc+mR#p^b2b5P5@PEMUXR zz2n;BY2Ab9rM(N`EqxQg6ZNW%-}LP)3n;TL^c-)IP82hFuY*f}w}UQs`+Qk`w8rg$ zYc?G(>iyHtbkuHsAIDDd&>W3Pin#VQT&r9bqE#`O0nw9MwV4vR1BBOnPePZox zF7T1b^?}WkoQtudsTCWbf(4jblAf3%t`}gj= zOB+|6OlvKCtf&z%I6kybn@i0OD@x8kmPn|Z2HRntfTWFi?Te;3;5OA9S{{}@bJ z5CVTb8HJx~+N?VjAolzrGLcLULRRO?i^r)mV_Y*(MsGXHHX&QXL|F=RQfok;edUmi zEpunHhR+AKP6J!~k-W4~NATaV zrM#Mwy^eIBU)bR7K|9H7=_Qqu#n3vjs=?oBGvE_$mk%Kgd*;+dldf?2_(^Fr_5R(0&+rFF#{Bm|ga0veQCvYrN>b_%j_m)>nTx-{Fn^_E=>MQIIXM9-b-yJn z{zB^hE12v*mgPSaWvu^EWkfszzhJH7+3+1f6Z{D1tbvuGbZu(bFlyt1sVRJx&9K9`1dUV zu&TeNBmR2PzvLnQigo-4+V)?>AOc?f&-BCJ&h`II=;41F>wkBgiIEk+k+J^b&;AYR z`fXkP>!bycZU4T|{+h_YQ;B~^4*xYWk&YIShRV$iX=iV1plbo?oT{gzucM=5WUZs~ zjaFn_lCtsE-VziTI0qUSSYE+Z%0P{)LXbp85au(@popY|EcqmyfsRf&(A60I_m8E) z5*oa3pt#yZ1P!@AZaMhrBE3Eh?hg$~OX-8_41T!60eZx23PSz_Z5|rW`v!%p{lS<7 z*oSVLk{B)%>H!UPG%QpSBraBPHVjD+Bo0Y3bY>zqjpEz{}+u~!6#`~t^`<~^; zBVCZ?sbS~fOIROHU>|Y}j+E-?W_0l6^1^fbyZXO} zc7+CoB8NJJ#w3Q*t;x#B)5=uJspJ&iyz?wtL81DfB7VQBlX20_Y$ejbX3+szwh6HL zvW?mA!rKT08S!Bgq92jhg}Aj@xa%iyD663NL*QzzTUw0w4p<3r$9JBW_rV8@9(O}xmq69;|R3!@Lq~@E&jd?mf`Is!w76>&8^Z+RIdM7 zDAirZ2x30Py<*wIFb$RFqLK=2pSy7|_T!ssvUolWEDdwGvp!H~g?^NK;?D~Yw7UXb z$wM$BXf%*x_R53dMwYbo+t5x7_alqL=higHyMl*hgdgU&E)DwGeMBZcxjnuT5L4c^ z<$92^bG&pH~*LXHJ_lWyqFM$q?N0Qxw)=3pM$x%!S7qkuRZ1u4Ey)P z|5I2o0AU4yqky1d08aWp5!ftjzvI<^1{E{@9z^`loMOO9e&6E$i=5)$R-3;rKY%sr zpUcnxa?kv~l;l6}n*X51|CMzIY!ZJ?-KAYOoyxnEb*VAAhT$EE^cQgg&_;=LN$Q`-wIO>!1IEN}eN#?<2X5 z0fj{+E5fIVh~i2kH}U6s-$PJr+q-ipT9~lrc$V6jo#a6kIm6Md5*8*=!zZ8$GF|CI zG+oK#R6NZi$+eoHxOU~SadB}KtuNzlIoTUOua4n=%+5KT7g3SzK0ug5T9kmQi(4AO zvWQf4jZ-6$mNO|ui8J+7Ou0Qe%C@U!&`_mK=^^9L zCm9QmGMJ0ySzUbGjpdqspNZ8QFy_0Us5`xinQkA)w-1rI5?Fw{g1Qon=1o?qc#-Wz z$fS8@;vxW6IG?8p%a)x&=@t|HZmMl5l?ic)vTF-{KUGu^3&w=a^+soAq8=;P`7;bM zr2Quhg`|v_Gvdxeh*oUpB(^PGro#|=7(-Ac-pKfoZjhv?4Y8nD)gJbest@Q04N`gA zn|-C(D3@ExKm~eILv^ms`pyT7hzK=6C_Q($*#7=&mV6EbPhcYfr6SxfU-Jq zjCAXux8WZ7m)vk~*cgH@(#!BKv7SPj3GZS9h3xZMklG#TV*$vT1-rnuk&B|BO(|^- zM&_mI#Gr)}IGYDpndi+pH%Xi4vK*q>qN0~~)r&xJtFASNeC{S{0mZ3I`PTK3VvD%VUm*h}m)3~gG zLW`$hG0AHD5X_>Rg51P{!(tS7C#2G9t@*AlCwN*tEO+Ixg_m}gOEGYaQERtV)@wH* zraV?({dt&83{*vz8}s2KIM0icqqf{RR}rx03*2o4_it~~9!7u6`g(hm<&ade)ne2y zf~i9jiZ{N_7(Y|1)2Pz`6L*z6#^^@c(QxWll)s)`95hC2wQ)$8sJRv4@y=QH#8+bJ z%Bk$QlqD)nv9YBIYmVaM;ILFh$$^shFO96X^o&6Hn-EO6)dj;>+p2u{jKOG^JDe0d z?WP0AoRC;>uqORB$9YVq>NOAG7d@t$jLYI;$3b0ezD@$Kcl*Pk;Q8%2u}vLqN{@(jmIwjk8+?N#p~ zA}+Hp@=5F&huGsKu6Y@{iSt`ucD;6y-MEOh{XBu=8Es)}q+C2-(djd*d=?vhMl=QE zjIMTEVVcc^n_O7cRa1q9T1b^EDI({sFk6^A+64-;%DGK+P;Cm1VHTW84L#Wgi@^Fe zKUcz3&^lPI_vqvz`@I^$lO~v!U>`EzMCX`qje~7vCsJDDjerWKeK%hnC$Fu3$SuR| zpdul@>d~70A=Tb@FNT#qb^@%1@RW~&Colw3MmZ%PdQ4}f7mvBRWqS~8ofSs>(k;q& zfy}D<29>M|Z)ZC|bX4Q@@Z@O91fmTgG~B#^p~9;>@KehkPP}X5s)sySu0dY^op=>7 zbOz_Qt$dE)AOW0Hb}Fp?)=<1QQ9Luz6n1J{S+Cd`%*>*<^xZ`7B8yxF8)(MupQ?r- z_34y01r0`QxnB5Lf5&oQuZlG7V;4Gkgqr`bI0G4AH3O3PRWPZ8~zCE#S%~Gv4NK_%noblebZ8U(R^is5SEf1Q! zc!M^ISwgmRzJcawv|i%ovoYD2aDdSTUz5UMg@cBMeCeKo^ajLK0H*DQOX^}IfVR}? z3YL;Cg*G87=9YKhQ452Iwb~|&qZcp1juk`()sah0gs4N<*Rr=mn%?Wo8NkF$9xIIH zIaIRq<4}VWLk%s)0*w)TobMPjmldjP=yCju!(0eJ6m05#)hoAOScRO#{kW5kHh13c zaNpY46(eL%jX(UT!`5FS+LqoM-L6k&wnjmiLkf5K~t=iH%R?bvcP50ov+Kb32iVEhwj7YvcH?<%b~|2Ob3* zGcy|nUhmb=2-%F+0_Uuu$1*BTnYL4gMsqkfa!Ry=Cf|GP;$rL@#DYOyZ*}{G-#d?w z&0$dcMzM4D#f3`a+uCVRmB*&6mg7KwJ~6gdNjABnX$8S$9(;zc8AoUqS>I}uZ6vGP zr1OchF>q}$0-ReENBaW&iOVK@hI3EYGHgZFGa(*qpkdg2S3Ux!QRyO3Q_m!9w`deh zw!2U?yPfu8{2m#O8P1$moB4TbD!g6BwbTYPg;9bzP2t2ul2T%EyvC3!7q*8O26mpd zSvZB3jm{=jMS0nv_Qf}suF~n7+UdQI^9y8}1&aQW(l*yN#IB4_F|V6aJlyO4S+Vculo7e+oSCzorKd1*iT6y&q)N+rIeW)8X6er@9*tE zZx+O*;+gMv+RV9rHsd(9{$jT&X5%Bb?(2d+wW}yQtqCkuv{d-RVrJM|Fe#f+?rgo7 z&XKi54RoLo;l~6_>uGwK!)1{T7bu~=6sEi}+&DYZDE1SU;da_Rwi>%ebb936!tQ)T zw6r6$#X32c?;BU3#|89DV%qH0tB|nv60%V+!;%g5^DveaR(~LlBKx=U80+22I{0?D zYM*{QW`TefX*75tyWR+`^tj4a>-Q`y858b}4~HVEP5wW9=TaV_+G}go0^Ikm&W#^? z^RxCIzQ*`VYsATO?eUJln5Ec0#Zncs5>YKnh>z?qxKuM(4N9XinBP5^0Gpmyps!Th zD=x8&?r%#3dDx-uDWPYmnu-$3iinyqR6+>G3DsB0D<*H4$?Q#8orpzKm#cNe#TdAE( z(E|QRSKXk8S=Y__B7XF9r=|_lNbcC4=dfp@5aR)VFI}8%nEu`2rg7+FiXm=zMh%wg zmt%FU86Bl|Qx|E zhm0>9_m|0+Az)$VR7IMnhcPZaKcWM0(3Iq3SF2gy+oyAFw#%KExok!4 zqgPT)$SFm0YvjPp6!zRKrk*KoS_})bl;b1fdIh@eX8$Zmcq5uh~CHRu;tvxyxFYd^w89AYpIOK4p_vy-s2 zKS1hH5{yt8P5iKhS#wKgq4wx|RXb4My3^FfjqjzgCw?_#0URtRNdv}i6%)T^qb?zI zcrh+CUoV=tFSOeb{#9%R-0*&;kzi24fiH#xrSeXH^T9KI1FKNTp}VBFM#wsZEM_i? z9yH+unmX#fI9^*VTdf`5VM5-$4u(SHa*m@&$^v{vxS@yCw0R9HFK93Zruu8a*bI`) z4`l;gqMaSeA(d2JgM#(YZPi93a$&ag7<;3>RRtwU(wZv{@Hk0hG3GG3r0QLU4bX&* zryB@%y~i0_&^h~YXes#>YHe6g{5C$L9E+H#bL&u1_9p&Aryt1sx?b)e2C?aw{Za$* zfq|ZA$>FopiQ&1^zB|qDT|U}txQeiBm(Y_ z_>^RWGr%zO&pz$6$_0Ngkd6_(GtHqSwv#)De*R(+JBMU(o_4s{%!jh!)mf=!fIROB zyjD~pCQR?oK~UyXe8r9;S^+~X5yS|WsIl54!u{wfW#TnLRRbCeBL$9qEYZK5Jmfj@ zy@D1IEGhejlQZ-+GOMOy?R&3|mIl2&pN!oxe+ooM%xgLdzHN!t3S0;XoJ5RPMah<%Xzt{|-EF0vVq(hI_k z>IDHC{R6yY#|yBea@k_#rtN5dSS1cOIflDDG^H~}htbi;+4Fiqe?LDn(TIyh5eH$F zSScg=UJZP?fTHfsJ#pg+{pXKN*!0*QINpskn3D*YZWmOIP;|@WC2|_O57mTsr4zR}xc&)y&;= z-%cU^N$A!t!Kfy)*05*7f(fx6OnccH`$fUpNY0yUk+w7Dc(miV^Qbop!d~97FEICI z7tH$0%oN;?$+Wo&sbG`Wh{8I*L!$mkKuAxi*$E1QW=aAAMMqHQ-Bs=Qey-?^<i zv`$Vj-J&;pbnGLk_!XO45$28xOiQ8rc!>1@(!h(scAl_owhb0uD`?UjUw4ogI^3CP zXkZYbxF|}Rh}nU=D~Sg$JE4cAbd}L^z_PYAp@T!!#>k}3R%BNApetO4Cg+E*_oZ_rj2m6`Q&xy8jTI-d>{@_@B!oHHe>CfdYIK@-atf882eM@q# z*+@L~XleGnsY?7sx`pYo_{3FeY2%;Ul^S0?uEs|6-om-AFlyyhZvJhX|4RQ-MY$ZlIrG_8pN-orB(jedW5z zsL=ij@Q6(%p~TFZ$1Ivx>2WHaektd!+BJ*OobtDz_-puV`{b6QF1MXL0DH>xHXuU>LFyqcl9zf%7KWTc2)^46cT{IAyCl-{x0SnG=-_QcJ9 z8(~~uDkGM0oFu6XN`yi`d;Q)>Bnv~u&tfyX$`8bSpPPRvCVzCMq|lk9G*uVR5UHL2 zbZFHsB(EOP*0XyoQGZpZnMz7>#dcpJEqd z-^@wIyZkkJb;s_LIRBeZ@1zO#6t}El8T_fL&D^^l<)B`m z#c6J-xy#h+?w%4g+zvI=ojrsx%xbI~Id(2=s6X6f$`^ENn6-8gkecy4E>He8ybxl- zK04$QBC|AT&qkte71UA?dXo36Z>@oMhq4*&NoS0o-b17E=CV_Sd#V{TMNmf>+64X7Lu4Pv_{ASzaV~dja z6XB@R*&DB7g_Syq#0q@==l39)2IYs)M^-kC9_KPU({}i!zxMRVm8v2Z#C`0Xa%ajAev^{nOS& zEA@(+VuO&rozkmGn$n#f=T149wZq~*;RO8DT5)M^Y3YTNl&W!bvc_1x3{msjGi6%^ zuv;g6zE3ANcd3+YH!kW7qH4>8TiDVmcHX`Tr*#RcL&rvUedG7iQQ=u>ffh~m)a-rr zRAlZhX9?e7E=5ad700X@J)v%5d^i+h3+sZX> zlF7#5I0li(R4w{wGjKk{x~ws@8JcH1pN)_v>T+5NQTSzOYLz`#(zv9KfPwv(Hwl`t z=#4hWMQC;YK?dha0BGNdf!`xf*`81}hTfd1Z%H&o{KJ}T;=n$5t=i>$b5}J4D!GAL z$YiBwZANX5NRh0p8rdz4ZWerPuGC}wftn*V+_{CAD*F3roY=T_3mt9$tLHGC|Eb8K znUW8;$om)u&&?|A?c7?3b|_)#h{3eQ!Cpn_(i!cMk~Hdsfp2*{uM~RT0YN18_kKSZ zOR;XIiOlIe7j0k0_~klJh9!tg5tfw+(c+UX2~S<5jIXKem&L*&9kqyC<(M^lvQ*P;<+7J{gsJcfGH9CE+4ywa3S8tZ&YsB;jS#*## z7?qB1NsJ^SG8F?bRqjd7g%usTDUvU0tUnWEMl4RYq6Wtfvm484HB^_|`(&ZAk zABE}*yN-xQtajzB8(dU%3Lpw4h>3l^H+yX-} z)8T=%#fx{iNoHRkK`m5wn7bY%J&MHrGNL7j;^T)k9!i-`+x_WkTKLV(1iw|)tI{b0 zZdN%0)uQsxQDv~gW}gWR0c!H=wHkCszc0hl7z1Y>4x5^pF`0=&Gk%U(&P4$x(V%jy z$e96l#z%&m?QGM+sz7tbMOj^2M{`>$q0YIDs0Cuzs7K{_1#LYeH6<#o$>w!)%35XR z#Ugjr9Bce9<6kyV>-bL2ZcBC5`6-Ve*9K?La%*!n;Le4r`K@)m2FDURH{r^sP6pEH zypgj)bUbWDM>nTh(0mh{jIBb+*awBoH(P%y3&y?7-puk0ro@&dt5Pd`(Spv|gn^4# zr=9`{cKO>lkz4h}h+y*?v|NTxk?ti=wPT-Jy-%6y8Ks74r3qh>KJFPFz9@6Q6?TQF z1b>lWrul(fK(j?@rCkN*bNZJ5}B0HRf2J zvTN)gF7~nB#~8kcohWmlqZ)zbf_+K}zDPfYY~lH>EKw1l~x>gkQgheaq1Hjq>H7H!MiC3+L8hDn<3)+o@iYe318?)ERb5B zr$Yt`ZBaX?=Mb$-{{GObj|)a2+aD#YYZZ?n56%y}GC#{({WI$2ekzm1Xh)Gs{%tL6 z<2Cg-jV^amx`aGfpk#97^-FmQ5wwFS?oaYR3%aLYChTS4XrbWY*C@o5BNRy(;hj_> z5JiDFdzK`;A>330N}_Mn1E``vti2e(HlmYGgqkQ2bB~YYgJM7^^hq=6>Ko#<& z6Hy!T^b;@~`lK4c6!Me}7!G;Ti|9eP#vu{J*qavhr5#Wq5k%WFB@sm5QxYXW+Y=)Z z1nl{dpdeo70{k)e^hvhR_UuTu(DxDm4hT-l5nLfp!2sBh6{dl6(N4;NO3_Zz0YQ=n zgnFrn000wWy>5iQ=mln>K3osLPV^~cg=8RFw3B$?R+OJ=piz{cY~W9*r%nVYWQB2n zlOz|@q!OST;;9!gDaubdP$X)LZc+q@hpQKf2oLp?j(`pI6ppwTt->&=20S9xOGapt z{Khg#1<-}INkw3ZZZHfulcd4dt4HvKYLO0@kYK~t3r9SQPGIbzkQ~9W=tiuEW>5@N zi%wwn(+wbz*rS27ox)FLLl5i~Y5dfUaNtKvE5kl&IgV*INp9$cBw7hq-wZHP{m zDV8v~d|>d?=3{XYpoO40Ih78~X>2ebx?KBw+=LQ}kC)D|09TMuEuB+*leEr7ty0XG zTr##PNI!;)VMy0~=NY$7QZiDGz*m zF)R1FNlz}3IZaJ%Pi-RgGco@y-r-NWS?D`HKTaz*Po>VOl#h#Xl7_yfC;FH^qfN1( zFB&}`O^Y~>NP9bfOZ(Z|QnN>}!ZZFL)#9lU9WA#Yz%_8*OHs(>Y{xZj zUg?J}%!!nukR$K-DH7-pPtK8R)Vz%1w$lp^$lv)TZJrDG(RT_3s^#$;JpJ?WM3TP` z>^``I2K6}c4xPFv`XAomDCRl6uz{3#2oLYzLGe6(2l4nYFBl*)py0tBIOvgw@aT>R zB;@R!KF%+J7!Qgx4_6LxZ*ia^?p;@{5x>Tu_J!oMzQ+G zIQ2{yP}50g^pr@Ej(c;`ng{fgn_+*U-=7+Y8~51>5B8*?Qbljkr#h~s%5o8S-IR%P94aahxgv_9dyh3Sb)uS* zBK!|ik`rDQ+e(t~0II}XeXMA;#@!nh*4f^@{@hV(@R!YG1M z1^x-}2mcGz4c!g?0S*M~hUtd*Kp}wbhUy0UfFXbsg#AExgMLDKV!NVo#C#%e;J-HA zA=^>vMcWbEf!y)iVZI`|lG@SVA>6UsVb}p&Q91%VA)g=}!5ken;*ZaJzxP)5()9`& zG1bAsfiXe;4Ak#c%qP5pv_or-6f}apLcb!tg1GV(x88Hvu?(~f*oIVsR03ZQ+y-9{ zpbD(+#omG6aWZ1N`T|JMY{w@fh>Q=Lmfe7|g(3py1Ve>H1xJNI1v?LP>($x$s9PG~ z==BR=$>khw9gv%JVsKIo<5=eHqovYerEkafiZ&0IaMrbr9Hcw=R(`{VQ{YNi?ih<~ z^>b|a#kGX%<%HJoM*P7wUE#r6^I7`v0AIi^Wy`SS?HgWRBOHEgtc_{4)Jt7bF#`eLtf@itL3W_j$jyd7w4|h`?3zn4}GkJMh z`?$hdsoCa4T~ZJ6F+Nhm_!NWqH{zRU=~Y4=)}9p(pQ(*i7yX*mM$7ezvhNQYJdKvq zBsG5@h3V$nm^ThFl^~Co?QJ;0H&Cmyjj$c*m@%#(*aCC|PK7ZX z*=FF_!S00#l@S-f7JC(X*^OwKq1({PAozQcd&%?B%fQA0S@SV7z+zyBz)b^Yg;^3% z55Vn-alU{fK{^MJ^coofm;qnF%pkRe{fRL~z+Mtx{^q8>=Dn!DdvNXeKNdYzw=>c` zf312?clsCP=|5>}SeThvxc`Cjeo~+R0bTwNaShl1iMVFcKA0I0^5+M_*Lg9h`aWy& z5cLuqly{YRL9qIC%67~P+=X-U+ z$lb`NZ$;-M)wGUZ(sfic5xVDvCiHe9?< zN{cMXR_YNP_9hv2j0;?@YlSjoHOx0~+~j7Ci?k< zM^#x|QA~qQ%)#Ep!PeO2-x=P2W{Ll?@V}smKMiRA&C{9nlcDAMlyk7K{4a@O_WxJ7 z^8XRVtQ=hbe)9i7ivKOm|2|XvZ=~seVv7GkT>oF_aQ@|w@;|4E|Fx?#*Z;8w=f48M z9G@B+?*HM``ya!9i~83q|1|r*5%N#b|1|uMYxtLw|0(Z(#Qv9S`p3e*Oz|%#|8c~B z$^TEW|Chu6a%KON{lD+S|NGh- zHg=x>VyC>(>E%l>*vzc6_N6Nc^ZIGHNdq@f++0`s-Q6aee+}c z^|5?WV{vg&pc(iMwD~kqmSm67DmHLl9E9w-zvP82Xl50iSd**yDT!=n_9JvA^c`-r zRiC%+w4%}*VjS&E2paX?_H5hvv?njYAIu@3(Q@BBuibNh^oJcn!Fjr<{=B*J?nM%N zTQ_pee!9NfB=WZRz)0VHkz?0S|62+ivg<6C5PL1CH-3S-2wrUk}jkG{xV2?5As1$9F&(pN_fWN2@9WE?7sIEZDdbsw?q{ zO6X6PyMn=5KoJ;Uif%X>?dM-Dqb;`w^LF~lc4bKB&!*C6w}D*|l@UE_%kgz^&s#rY zQuH{}Czl_^FI9XX&W7Nr6UJi2q&{&Rw7rKbITfgIncAkMW>1Dj&L+;! z+)3@MuI5@}663Sxv$eN|i2P$h1OQwD1Rqjx9v$-tDBIbLvH|6gL_5`{{+REIkW-|S z-x>I9se?LZF(SmUD65|-f_Ge5>5wh5V~ep7i!}_C$nk6BMI-79I^0e7(Q$nwdGuw z{QVuKI5Wy|YRendB4$FHGx{+7F#Y_t4d)&Pg?YD*ULqyR)7#%aq<-W9;Wd2rhqkCJ z&pO!OIMo`oBQ!qKQFT__=v93KCDS549a~!~d)hTqjY8rG7OF=(-MNDpTb*lL;&ytj zXT2X98@HSHr=p;Yl;;w)>Dq`^PJh>zBWX8M73Fa5#>G{ClCafqECXIYp#Icv5k^dt zVb>KSUd^i6xuoGkNeg^=h=#cdS*oeCsqb)=)Tq;>xab*yEFeUk#P+83OJ&hg;eJL^ z6>QwZ4N9ft{vUHF0k$v5FHo32oM(0eM7u^q#xPbdc5Pc)`N4z3I$J#;#6H~J`8~{l z!kNOaSk9QsXaFH2qd~5QGe7nyBRsI%LLtm(%O37u*blJl;?*Yd&qQIGPc6 zSl*u>K+go{sRo8$&s@6tZUi~BZu(DWgG%5|Q|pTwi6waHyA%$b4SDtR7so5l5y1N_ zwe~wl8n=FGY9HgUdzUUzho?}nrZdYv(rac8EJmuWEPunKX%_%Jbpyl zX45Qw4-**r%I0qtm#KuWKc|95c3n1tlmwjEgp*$*QUN#|=~4f2O0MB0q!{*o)O=)o z`P}(x0Lh9mc43>eAFbhL{7 z_+0k7K0bB>@5!}7cYXc@Peakk>sLjc7e9-wpQ|T;-)h^BlJND`{o}87zZzYq4(lur z=r-xX*%^Sj79{&YNMzEAVo8E`SWpL#JwdaP#6gsRASe)nsEpYn{AL5GF-kH}?l|!( zqqbMc!r`6yOw6KcYHAqrzlfmMnsZD;<N8A)fY*)ViV~A=-Yz6n#iq!zR|v|{a`VS6umSmN*M4g*zh<$i?Sjoual&iMUsbc zk{EATAFvT!9UVtRA}IUdaDXsZ!2Oq`RMmV|zq5eGfpKSe4B}KdS~H(RbZ8l*-UwJo zSUXNJ_AxwutZOCHcbG(Ejp7|Ep;vg!H5#Nn+!ZmX2R&t&9XSek59go!AsQTwIgwlr z??>=4!!7T+ev`nuP0Z-YP0y+4?XB+J&LfC!$G<(MyE$C-Zc^V^(_Ia5;63c09v%hH z%GO1VW+4j4{cj!%DUVJYxz)=g$cQQMFC`-b@>L>}zlS#1max9Zvq0o3bC;?(YE(f{ zr<$3+DCvZ#lNW`WRA5W-G~x!Nc9ZE;CU`7pmj30Cv?;jS{n8E|I)CS<%w4`Zvfq5? z*Q|ADdnx&VIs$G9F~ts~Q8)UVzZ#VVp3A^lkRqV|E3(e2UGMqwB7~pC>87|yy$~*8 zF*DltX%ObwQL~mONq`>Gz|69JD=R42v6)n17e@`+lg6t=z}!qaM}=5^T>3dfr9cCr zTGcAGu!()2x;k#|?DMVPN-;K3hx*MeW+R|~7#v5-4`7g>C~kl4Vlppk*Rv? zhl(|M?nQ0$iIBr7Ep`k=O|42z9J&+$$(GHiUk4l zZ`N*$gBYwMYX+6*!s`xcI}bahp4l|%Mt4}OgA2(ZYJ-G>G4mkGhf>O3K<%)?0e3v(FbA4Q zjc{bMmB(pw083-432op{PXB`U&cJ|qn~CpgE}J^RiLn#o%Z9V3j0_nz0#rw;wrD+k zmV+oCTcgNaQ-*E@4=W4tnw7H5L`oEgw^yCD1}Sv(*$4*eIFZ9SLuncpJli@4>86k! z1Sp>yo=5(DEWHQz53gvxa zf~CPOzt?ZQILa)q=w_h zv1#GEHcBchYWzB8513A0)njm06Ang0?0ADLSa&@wQ&Zq|`AiGsxl75c{3<#GzSiX7 zh}VZn^6q)kTQq9N8Yt;~TAdNWuM6j|Q*==}p^MB2m$G2i#+h}n`DF$oaRG5hFGlOA z$t*LFuuk-HMN4b*$*dp#63Gxo#bdRF#NWfPFj05lj1|@-io#e3HT5Nc)rEq_&IO${)D_PTZ z{&FtA^g^NLqK9|}E)qQ6P$10T?MP;D zJww{Vpy-q=VwVun`zl`+wfY9{P9*A-(F*8;2oEicqo@GTN76z`6e@;t00s)Ynd*e2 zm|zOMR0!Kx-{&EVoBIj@8^tm8tncCVxJzEPX>Q&1dCrQdMrj_}t}d?MQ(`skV6tYZ zj6{lVzS8fA+&~n_Iu`0ZBhPHc-Hkb%pW*kG=0f;D+Kv%X=JP~LVyE6TQY(!545iU$ z(loW6Lr*Sm&{r))Xf^2?jl`1{OIC_g;ckWNB=B1^513*{@nVM&M}`(+;%3GPAQa%F z;D}C&PTw)toXVeg)5w>t+xj^#sU4jFR_x~PW5>?w2Hm%4%h zbe5_jY1TN4uw@T9OK!ni!Mj%-&!Z1J8QfBDvF92jR3zsjMk#`-?|PYb=~fPssj6j7v+2xa!DP()wfwE}2=r;MCxq3ss?_vyQ+~Ks_%s7TadPHdn?-wNOUtJ|0C>?{hJYiBpjr{h?>qL=19|{ zo>4nhtFu;-+8Rz?q@k{|Xwo^*w$jwrR@`3XcFj|%kS*VeZ_l=6(NKN0ab`?;M=(n0g+AoK}G2r9EV>TAsTK=A_@&-j!LtWFt3SmzRNmBuvn zdR+o+ZS4V&_y&VXy{Y}MW%&mrm|)6f7PmlRDiwS*n1-Mn48Id_JS|=1)DX$OkQWWW&C-7A45LbuqAo}* z0X)E#2#SbG6PG7dK(jP777ik%lroCRcP#SHVkJ<^&%n=Q5|d1TIyzpkjfBNK0m!N% z^`?_ntARc~jw@&dZl36=umAa0f%Jy_Y7T zr8V#b+UnYs(UdO!aAn^ba9s6Q zkNb9Pba8w_qvArHcrh+S)RwQ zil+y;$_g8ub3BCRN=ep4m&R75F5h#4bSvxc&u5)X`&*ob3WnCR-lUDWkiN4pzO%9Z zj-21_Ak^~pX3eqdB4{iJ)O!cin+7ZjPXLABp+YKz)BNL46CncVtPr8wmsHak&ma-c zlpV}O46Xdp(?YzQabzs< zZv(srI@f*2CbFdjELrgNIq6#c`VG9ViAM~3jp00SUC0blsK23EwjpQ)3JywIw2vJ7 zhK8ygI_GTTsk~Y9wcI}bSm>^x5Z8wJ?3m~%sco!`MDhc%2h&R#bGpbNm`JS`vqP-g z@S|kHkjn6QjnaR!eg55f=Wyn{p?HO`MmYm((=+?;6mxP~h%{K>8vtIbki}sEM@F|A zdno+tANK7HlPoj)|jDmsl5HL5c`)666%2aaNOXE)`HwSf`JX} zt*YWav^vi^w`4Q%ZeK+-)2v-8vz`8e+zdss43x1)+4rD`HjD!(a1Y4_&EjH|Vz7Ntqmp(0yR+&pXX zzF61fW*Idwgh zF1fFLE^5mk7UvZhZN_IH(2_xpRh+@(gM-F*bPolA+vswSJTJdbpsd&6;@7RHdXKBZ zU)cE}fp7@<7kb7#IBS%Qt^~=_akEm|yw%Hr&SdNy^;egvH76hx+lU!2JWsLO1-lFf zXMA+!PA@e?U!AYn%}L_Mz%H(@uaDjjG%eP{KDLvSTh-V}F||kSL$CycsDO{vAXEfA ziFI%c&Q*vNzjj0>cjFfq9RT9hdMVfu4c@J~-#(4Wt)jj$mX#6>tJtm~W@gTm@)iAk ztT*~tvXqa{usI;X$C83w%Lp(180=zx^|oOO898FQT57J(_1;B z-bdApw-__&);^O9z!3f7Lh#kYjqkb+gA8=BvH$6b=tks%Fx9=YzixSzXJvJ8BCv_b z4p?CW1dr}RMI6Gnxe=^)Q9ywpJsIljQBm#-NeUwg;fy8^`v?LKw1lv2(GSI1a0S(A zpAX4W6xlmW-Eo?2xzNu|Shbsmqk7}BvYd|b9oS2YTrSI3t~xt777cB>JC?F{bF*It zgGT+&GqYtJ+KQdtDJINSrVpc&^1z?gx&4686eF4U8(5Zoh82lp>9Kf;47l>mRG%+f z#HW&!%mq*(XCX?W4=1S}XTocXw~Fj)A&_i*pMn4PF-zWvaZIJe_Pw#M%$IPCim#8) zux~Nj#-fM6y1FpBx_CxN&fPra%pnSi>507FBi_6p=9$+3%_ZbDYZ&ee*Ya*%Wj#a3 zTK)TU&(qgPd1ctgq1QuK9LAJD#|-AvT|*9`K&I^K`aUM@@=KCKVr|;RmHbMs)nDE} zGCXU&M`^waZs?|=szt)_O89EwipAE3IdPw{tCho;ArCH^8f5zlf5-y4qKG}sUhrV? z!ttSXO`$8-IORUA8`8d2h3|)B^h9-``BxqZ{O(%uxqkdEALp##FlqVSp=LGEgPCu| z__5BPo0(w4;Ahd4$uG{K5xfb}8LeB@#c>vz5pTV?e5!+SC>wV?cf7C4Jp`}L8wVpg za~wGw<9N$lTM6w2M=0k-^;CFDb$_5Iamxi`axMC~gIqHDuz-XTO3NB`hK=9ojJM|l z>xA}(8w^7;$+-NKd`2c|!y(xvFf8R}V^H_M7Io;k3DjX`sQ*3o6AYDZWmJljr@yU$ zG#&mv>rvC1)YAToYL^zfjB~Q`~n-Egf7!5D`=~|0QYw=>NuU451#(hXMq{hDt6rjkSmw zHgSp`HLuTT(LQfj%0B!hfRLn=n|ngo&ni9Oorj900KoCY!WaMDSh6IsRzXNBg2 z3HMDBfm8zpHZkKUSj&>K6%d&E^4ZEkC`G8rKjlbRk5DvN+;h?Q=M|#`&d>2Xx3_}05KS1b>8fVs+qD>fcuYcG!`eJ;5q$w#C{=bRkzCRlPimR$ zAf4E=2rXX3mhhHU6i`{wI|3|BF4A0Tj7jB50a!jT!BV} zN|Oi+AyX`5vj_J$j9Mj{i*M?LJfa5-PD`+a^3^sxUeOkA2f>#Oy#(+s`ZI)#4=Ixw=0kU$Q3 zAQVN)o{NEd_Ueq(ZG$U&{&_t5wNP&v%IOz4V%ROZi4}rzVoeJT z&S@L^X+{(k@J2m#F}y2$pKM!YBH4yn)o4E?a+Fd1=uQH1w#u@<;0cc~52T1O&Q)LQgMO0! zU|wVTa(75ZrWrYdL1FEJfp&Dks!b5CuSk&zPrs`RvIo8xoeeBh_xL^hfffAuzE$}k z@H%~&cUTKtM=g?dkSs&n{l^-O9#xJEsk=bszC-4+hKpU*IQ=4<(o) zN@4M=4WwoZcP@l6#2L3)m>< z@aMoA3eB}!HTT)ju%#?T=^H|D{IWmLf{zzIA;5C&Z!V$lQCXEG z3=a$(tum;Qf8_qMYu&gku|HtGAObmIkI6Z6oVXX|S3IOr*)TL!SyG}to8ipz4<#xz z>g8;N$S8eDyc-2+;h&KWp^?p75^xF{o5g-D`!t55fT&ng&|CYZBCpri@tPk0&@9+v zriqkPUXsOa78#kPwb5xwZ(pThA1s8>Zc{_8Q!Gwmt$2hXQ7fvrmUS-lJLrNqf~}N0W;v7J+Uu57`Q~I zX_uL-(2dP))RXPBAD}ex7x9>9UCGmHGhNlkLD}ND(y~vKc+WqW38t+#m)I-J@R|CHHO_baDf@!vC z_4lctcQezl(`p37!5SpwZ&-IN{9=CcYw+1Y)zUXQerZh%rjV0P?8TR9bbXf({)n!puHW*_F5hfRp&om1U08cAzBaIKUFrVO9h(h<|C5&@ z^c=_G8}V`Y?ju+a$MnEBQavcJrO{5;$@OHQZZOhQ(3$A}&5O&SSpp)=Disg@sYt^u zQJ7a}P#i*)ol*)S*NPqwzF4?YJ%u(zU-}w+hE6urOFKJzy8R}KD^Jat8`kgf#ZQpQ zG=V(i=2=~5tQKh2qF{PVIp;L9vt6aljEI#wT3hv5U84ir2d3U^Z5Pt+~0m`T8TNk!en1l=hXd$HPZROZ}j=m0S2c zhkg>N{wy=}zG4;<@=MP2DAAQ#lX!61C^!m`gr-wK=z!f##mKPO4UK_P+dF_VtwKcJ zq?7%%d7`;Lb_l`Mc7vY^8s08)c~RQG)!jud!Z-^{P0?|8Qx2f_^6x& z^ypn=neb_q@F~5t6EOn-vMgBEotQqqD6i+HW4Wcuyx!HFWkeokA-j*=3!GogJyAS(yT$E z)iOy$?h@7V(#T#h3-k7cg#kGkit1->-%@d9cE935E2i4C*(8j>lI#%#R4Ru+(ONaA zv1y5nITncb6E7{)EsTp4hY$eS9#XDb8^g`d@P*_!XBZuO)ucJ<>Al#w0sT5`OIZ&Tap`6j042 z9%HNbhe0XWxO?gjMMUI*Xg#(#NAPRUZp(i9*|xiZ(S8jqI}CF{8uO~yTuptGwU&6O zENqh=u&G&Xn`s++a{HZlw#nnf??F3WMypyxZZpU3C!f7t%BaSq%Ea+d$yM=lhVh=| zI^oHrnTCc(?Z{nfg=d;j)@v!BF*)8w z>8VQ4WoRI;rbLpkF=%U!zE4E}1{haF_qt(3B6`J$rIb+dSn>4t=OB^sq0$8zK60= zPy?NXw8%MsKdN1y@3Ef`auM|2h3>37fA#Jim2&X+F`gm9_r>kuama7FXh{EYLniqB`E}AQ!i3e(L>Bpci4%IX+uNxeqFo{$bt0LhL;uN}9mYpb; zr77j}k3Ds1aVXAJcqgN2(k_t>h}4uI1vhM};6?A9&0s@UE@UwpM+(jqUo$D2QZM~$ z%GrI;wfdCwXt5_3a6JYST%0MF1cMNrlKv&KFmrscDup^rCPg7(4U!#hNXdt{Rxjn& zlemYkhsvLR`jCv41FMpkQq{gtlZQYz$FmWG&&{PYb&cnj`qty2qQB*m$p#{lL8;{a z?SX-EBpmC5Dh6vc`G`n+*VIBfu*ck?5h@@lrZo0}HbM4FUz#+-reeUiu zrgs2l5_jYZ)uM5!(~>!Xsef2Jhr8wGu~O&D&&Qjyw;IXP#yZm9hzy%ftCvE55K23q z)JbR2aDi|4l|6qSxVmRy&}a~1lij?Rx8DFmM{F1Wo$LRyJpmbK?=cj^2Pf5 zxgU6QES82uEaw*H=B1$bxmQT8o;K%r2VEqtmKBfK@~ecLFt|5hqu~@3q1gn5t;KU- z*hQFYUO(kRKvd+D&nTQXEF3K?VP?xlM`c3S_>;rB+KA>7{B>ZFApa2ga8)*p>12eT zw&%2k-o!D;hnrE8MY&8dOA`Euu24tYt0r|##zaZ-NtAXw!q8>y{CYSh6?c~#35H;m zts&XW!HmoqJ8NsWBVLVHTRVhSh^n#5X8eb+UR~BsDNOA)VOHJFqn!09#TY$`IJ>?* z+&r<&Gxi+13SO%$T7BQfEMAHf4GXoBj5b(Zd$TC6QcA<%a-83HNKS62L4k-@und-D z1Vap?%iU&@Hg!h34^Aw2p^o)T#dkr!;LH3`)WL=aMK*SMFI*OKAhXB3tvZc{hG1kb z$<%OTj7zCIhf$c7!!y8)jo`X>YUXg0fXFoWC4%K8+Zknq+1c*Z7918>cZ8xwaqmaXv+Ypq6Wloc* zXs)_QqsdIQ53jo*2wRD^%4iYWJc1r9u37DpKtcg_DW_Gg0w=wJG+R}Q=i!hh?wUk= z+81C^RaZc)#E6!G>fU$r zVLzl2np`G$j|m#xX{dpvB&KmsRk&jnC!! z$Yi!z6K^o~M2_jglu8M|XgE4tg$kMaAq)f0$`4tLv@)vAGM~9}2mO0B+Nc3O^uOFTvJ6fX|(zl6HhuCow}>^l>b_ZIv|^6a9Q;)G z!L6>4LL`+BY@{Ad>tsjfs7SEE^o)rx84sXoHJH;MU=|WUfIv!%4NyT><6E!-%0j@t zVni9SYl~rx@~=584oc-OB3t^tc9e0SH$ax*M9<<%5$TmgT~H8HN~$iE3oRRX%)iz2 zWf|R)ZfvPd88jC+rM-_GFnOqeUqA1<`25Bl;#GJ1VVD(P5JXi%|UGNqRrnE?8&S79>^k6KCU2!(XO8_pOS*fMLO^jQHYN z|MTSLQ|lv^%3|}4&`ILH{6kZ0bmfdVtU}Wt)U?7p&257vvD+n8WfD+N2tdK>Xm{hCQgg^f?GxcTvF#4wVJ+P2r5;?ec+8BTTd0h@QHnH@e^R?z7D- z$mwUphhlmr+A^XOYKTHNgc?E$cXg{Zbm6JcMF)-vek)n7!1 zg}%G&xSk77l2~csa8tTzd3u1?yC5^Z-wLElf{w`81A=xX8>WauDsD|uS@33}S71O> zBB_-*KF&0JJg(IbiZ!1v?vqeWIq7%u-#sN#rh?GIQTt5WWnHVRw~x^@hp0c>3~ieh z&N_(szu)qQ6Klv2x8C8Z#N@+?ob?F~!P}!kukrwoXt5;~lBPA{lufBXDCS>Z)Y)0g zFaiiU!8;}ETh;rH^l^H|)04pf1TdGiJL4n!`Y@kfQZ#mdT9=<-wJ(?-|xF zqzXztO<-tMZ_FyC6bolM^{sVzlf^0@QzRFglHf&(7&LeYWONCLJNRe~fvijrV>;qL>$?Z9)- zhTQD5P=zb&?$TQWq+nz4hP@+nDL3G>EJCY0CqMb!@Ez_qPJZbWX zF>T7HkG?)E6I$b1K(mB_qn32WKAx(T%-IEwsOzTkMYHGa$3l^Gb0qU%4D5oF0SN5y@~x+JSlC-*KWnxJ(}E@~m%*2~SBxj?~jM5+A_T&ezoIo5j{< zqBqphaAI#D)^JSP)HlSUbDrj3ppE0iK2`FI8z%hX7)l)T=KBR3?2_ z?;K;{`?$->viIJS5fz1joa-0kk7bz}I~mqW2~%Rl^8D}=6SEmn{j_1qekMikS;>1b zwfr~{^3I8Ad!XCi74ujegYuyDa(ljhbx|J>qT}ghG*fVXlEpPgys&V@H#>2NdLpRq zgWW%EdvG=qL8kZ6Lpj+hX;S?_vsl3COG#Q!nn(5=*BU<U7u>2* z7_t~_Df_aI8>7%DAt!6To?;Rci)y8Z8rojcdJL6cV5ug*uApDOPJ2h}Mh4Al*s&_P zCam@=u$b4DNw>&?^P2L=XWVPmHk_xi^V;n*0C{{yRYLZYAk!+K7k0PvWscjnaz9;f zK)0Rv-)S?*@Y*{Zb-kYR-QO3Zemn1UJAi6(+VU&Co_0)>_ln<`b>JB*({BT@rJGrA@8M@}osBc*sQ!)JS_Zq%Zx zo%Q7y6P`^0ue?!g?<Gflh^+QzzEa)3?7Xd>&64=<@WUl&-c2zC{*exV#0Tx*Iz+U zKWe)JE5F-99z2&`^?$+VZu?XTFs$`$01e8D9H!TN!2hwXhNS(=3>4H`lsIm{oY_oa z6^8qln}!E~H z`3{sa6shqp?qOfF?dkK9E{6XOgr~wIH8f(-Z9S9p1PeFV)o070l ziW$0XhP_3;L%g9q0EZ3y$kCR;=qE4h*{M2dwo-0(X9HAaE+g%SqQ**s|i!7s`JnB$km|dW$0l>%L z>WxGRuxE2k_bqdBIXk8T-r(oui@ScXo@GN0Jqv3ntWjWI8h`?%31KY94Ez_9asoZXR;e^b=b9ZIZf zb=+2Y#7(8KN>Gdb$nyg$*4!+_Q1O~OEngT=QYm8j*SvZKDH zhux%i5%;Xh&PpYazJOCOQv+TsDst z;*b={3BmUtZD#m$Jpz~A&~!#iv#eXo@G_#p#P-;~S=mDsf{^cR*tg%1y|zYI&t6L z*n7j>%;99nj~$c_n=RTHTZDQIhr>yVZv6@i)k6)<)u7*ZSc~IvM|&HX{H2MQvvUJ& zO|aUF@l!vORfRGo?nYS>ha;hYVf{;h8tN zM2?S`anpqObV-rok2wsP6UrnL<7nDr+z1s3B&7;O9NReS;pqCcw+zhnJq1f>R(~`r z;U$Kg>dLI7bg-~!-NQf9{IuVqdQ{j_cv@;nLSe{XmumaLr->=ZLjhwmVJQ(xC9FAw z){gHG(w^2Spp~!neaYrG(R#7fy>-lLN)rpG*8ZwVQ*SHXuqGVugU6!>HiM>+30OGoZS{IEU$^XV^(;H@a6>>l=@Ohv>|&;Uf}51&2{KL)v6-qKPi1Q43#;E@ ztnS>3Hw;cw;yjd8s3XIhilmm>-n=Ph6~;R7c4KY-`cq$F6`Lo$+o-c5b(;Ab^L9!e zHq(sSH*y@$PfZiqll)BXcy41GkuLBm-bKF4Blhoso~h}mEX(e%fzE!Bh)XBr^zA%3 zgB>U@xgJgEH+JT#L`f4B!*B8>k3RCE8W!=tZ}c!%N^L(+e5xnLp2 zfPlD~-jo@0ST4##8al4|a8Aed5RT@TvuhUza9sU#lQPDlD2moVScq5_y~ly%GT~5= z0U!d?0x56yO=@A?l>y z>^h24CqjLlpTT5MKy2#pdl&!8{9b)c82IrIL;Cw2-Ai%Vl6cCN#8Lh?Ukx7j@5UJH znod6c_(VjASH~7|T;C>r5bL4tr(ToRunU()@*g#dspMA_L>`FPQlli8A8D65Foozw@6J9LE;P85x&v&bnr}O@lQzMNhcz@b)-Jj2e{7ckRkd+~gX(BpW z)eb+3W*O>cBxG8p;MXk@z1;!Q$2H@pHzgT5^$aRugo=#;mFDCwo?lkWaBs{vD{pKZ zuT%4l_+g8+xLT#*VcxS{lju)192PKV=!K8)_nub zN;rX+{&fe%lp_mWWF1cV5ms08d>Kt41B1;+O`Tb*@st*;HnR0dsMLE_mhqnx zGxaUJ9Zu#yOIstb&u|V66p~i@osfI;MwJeZzRoD=&!0u`wV_K~4J_9#UCWtHs_)3h zpRx?zmYwpf3QK_TkKA-d#0{k2gP4}-wzy__ru?L~Y0yBaHd?`hHz+M1N#%GRB^3@0 z+?o0AyiINIEj+QmiAkSpRIH<6CUaqpKHOBf-ghiF51AowxXx=f-zk5^@0o8jFqfK3 zjy(N5Oy9vWx22KA@HmTcDYnu+t@ur2yCiKT?M%DIjq#`&D{Ugh!f_(ZBT5TUU*fj= zCD^`^KDk6!llgKw)mXNFx1R56GteO*%Z6g1Gu_T1k4~fA{uN$2vCOL7{HR5xgaRez zTk-o{g&$Y>2cK7lrVzh9O3X5YZ;J<=@(jSm}F}&FU(3eKI-}*mD?u zT$7$Vx1NaFRp!L&IqVQ^kT7wC4FL$uzCAq#ZtZ7IUZ=CW)8!%U;Oj)Y@k@8 zqM^W{wPW6jLHxRUS%mr@6sP%XD<=y=C{#%lDRva7nNCRgT9<&#uq0x^FJ_^{<2tr& z#F?{jubWl)^HcO{eTWD*rYD~mu#1K@QkTZ06hdTb&K|f>%cKPORjo)t{X76b=Rn0a zUQg8YA8*UsSvj(WTJ@Qa2f9Sq=Zp>PaHPWM+I9XK^m=rDhSb z?m%y_UngJzs$G1ZjgNthoIU%<`p)rw_pqmV1dv`=n9Rbsx$3udp@EF;6U?Sha$y%% z#wIh%r!#$4vSgHqFO>W=8wbv1EUBf%5C)bUhFU%v6fcHL@tvd4jTLzpoKIhtJn9HC zecOV4qY7S1QON=fh;tP90*O~07>a)#Dd=Z4*BSJL9y|qro@A^#goAhY57WCwALg7# zR6;5V^}#(&qRaC1mUkUVhi+V`e?CaTzsjtwb3;L7OPXf?r90np-*8^RcR!pK0(F-5 zr1wnLTr_Fh*_~F@abwBkL;5^-J!{;GvHI)2%UEmw?zvLE;f@Azp73EM^D99{63m>0 zrv@YMVyBg)H03G*P{(PxM)oTqq#I!U!ZTDCrsaVNpP=c`@HySDna7^XIAKP8XoP79Wq~NFnj-Y30m(M!qIso=naYq)Xd0)Skgq%*!Azd*B*gQ?0+O!7<9+{i5?g@RHB$nB%oW zA~AB|7*n2@T*5sp)XeBFU}>c`=}ZzR>M~`A#}S!MuxmQ{n#7vXYCbr-&8EWLl7SI0 zI`vz=Ds~}vrm*VcyOu&P9Ixn z+H{N*Gy5E436~L$aMv}&A$-If0Sec_EMA z2SZ%$T8K>9x}szS60eT9EY-Sb`lVp@!!~jwVS3Dok?Og)3PWP)@F)q&N^W2Xi$ulD zq(Fd^DkD4mlwiAGyFo7To0m@~r6zJksBQEuNd%jWp?%p4MHBS#Mv$`|>!kO9koXtK zL~!=Qobp)t^MFqx%WFC} zWT!cK6msozm?ObUhVV2O%$Qv*U`eJNDt_i42?TpZi{E`1(GXwflYy_R?-g zFN(|&aH6KyKXr4y$%C*TRy=VS^GKs;brgPRNUIS|v z1A;{<+lhFH;6x>|OYj1L@dR37{RvL~uHpNt8+YeA)myUd1LZxvd|vjmWx)`1E8pVP}B*JMI%yMez3h z*nUd+OlcuuhBqd1TUa)R6Q5=TFz&HepuKB-C@ZxCaSs?a z5!AsDFh9Dv>e@>kl?#Js+eLY*9;8^eaqW9%)x&+E=7-r9v~B=SJ_5(=V{l(pS=G6l z>gL^o{=w-s76p<}CYIC<<4Iuh%!hTJcK~t2?6OT-5ro_hI_WbsroY#HslCUx=X!E_ zf^cT|7F}$pL@49M>V>J`?+)T>sRtws(0G8o6Ch-MfbnE|g?(0drQFGBG~sHXV_!dBgFj3q&=CPEJ%`xuiP@sCe!!*iK8qq4#J=GiB%i= z71!7B^J<`tpli-IP@Oa3*6D4=Gt=qOgQNp%ClpnXP0E``hlkrzkRzMH`<`mlcM$#% z-7bdh^=-uAZsqP+lhkD9nQmG=AX8)SCHfxaHKe0Id`(i-*Cl-p0wcsKTl>)MCi4!^ zXwjF|7m^+t15GVbo9{ZWZU!|THKtdtN@gSc^)^SPN|Um$_8xOL7zaL|=5o0X4uJ=| za=zUm8*XrbOArs__Hb{}=O?u>JpzCZXlxp{M%h#I+uEF*C;caY*7#TUJNG;1yDYrkIXljZ zsIUFjTQ<8o+uj51!(M&j505X#F99#tLIRYZX@Y11D`Dk7Qv}8cIviuLhq8xNM$CNq zR5TZ(zw*p4dgah#`CzfuzuMIX=9W_h_5y4}F_FM*!|e*cFmSND z()fG|tkBI--812tXzN-VS>t+Twb&)>JHJ3=<`<@RjkM2o1u1Q0>N|f|+H=^msSBpL zAj}!nU+bJ)2f5}=YDQJ7t#m}pbzQ=`&U!$5qgIF_X^M|v!DJ$P;ZRtS#1lhxL-K12 zXMI8Q##KWah^dL$$dANUwL~=R9tQ~E>E}H-&xd+Q5ZcGtZ`jY7784T|`j2DAjAo0%8Ny1PlO)>2K5gZt|Vyt@qCztsUPwT`L>JZD;6YOjNMAw+G!@kXbgq*MacSeX_kmxZaNT;D33= z+?ThHvG!hQL)<=LS`sf;7ESxPP>kcajmcr?MDch?I#qrGy z)gWfdm2NU^J%>D4yIzv_ol@-a*@^CqlQ8#%6HVY1IAusqXi?t7axM@qK2ezqnBYEi zCZD#Mb%)q0bYMnnhUGkcZ^`dQ!ZO|)xVSPED zuu84SZ5^{W&sef-`NVgpawohJ<2`WKZCjapzY)Vz7U&&xU8(SnzK2O={zTeMpp~F5!anPu8Wv=Pz9hERLKl<7voY6h<`m7POJ2QWBd(l>SP&D@e zKq7QKF3#j~p8}LtDLNNQ9i}E)HRGdwSAH9teJ`wlqQ_5)mLAOC{$+pIlbc70wa8dW2b#p4%FRSDNk z83Xob{FCSN-jM_EvaD#mmR#51lmfShJ^PYV+QZLDnF7`9@-)>5*aA36D(|!O2)F-7dTB_hRafB3bh}4N*=63 zX_>A>>z)EjlE|EtQW`8&AYv>tkeqZ#B@U=XZsV<;*oekvhZ6#&@dvW8^#beqkUA*J`c+;Hkvqf!5yL;IDwANLXC;Q`t4eAEai))5ZDhqz z^o^3Jhbm`|Jg#Xx6Gj*Wsz8Xm66doFYE>DLznh4bD4{H!6eaM9!;0T5X(lfSB=N>h z5VSXwVL)?hSGWbqn>w#*Cdp}*>+GPDD^r9|(>b@OpykZ5mpP9NOAG^le5B5_D!)odpBR)!%(&rw z0CyDf#g>V8KTiiu1JR8ozilyPoC^9;+d|pKPq*mM^^k>~mSh&rS$vIOMr~qrjN__b zJCT}9LB$)*%;!XATOydz@`zH3a1GusC{U`o-J4z#biOl^4Pw|$ws5kvkW!>qFze)) z;?6-O(w`8Xo)7ajZWGf43l6AoDJ_ze5l%~V_|=*aw<@h4OfvCZ>b(1Qv0#6WEH2t& z2%o3j=8l7v?U8Kr$&&Ehq~<3JMYS8AE(+q6A~VxeHX-7%PGyG~G9h4|OG|OqJ}6XI zV{TDFe7#7*mHJ`S@fUwfxfn~6 zvfb2J`*D%0z+n5AbhlEEU>lk=H*`)Dxd^NB`K7UU!BxwHJZsH5V_}&nQq<;-s0R6P z?5BLx7OhZh7^fzitL#D?R=Dv7@Cn?^4S=2RN0RS9LWBCY2+z(eJ% z-=h`R8#X3EWu8q>(@sDhVZrIR6M~($35}`%>@ZN$T0C~Yl2O}V${?KuO@7*xZnXzn zhMOp~6Y4FSS1tU>rqi9WF+8XF6AA;bNmwgPvw1d?qw&|n!G_vHCz}B#wtXiBvpiQ~ z%%D1b@Mj%KuhE@@`7kqHD4U|p(MV|jOWJ_~>GiYO%9w8yGF-Q@^lV{*TI1pl^%8Ay zBz|G-UkDG+b{z5IJH7_!v4LBZjUI-R2z%>FUM+jNQpBZ@G?k#F4~OIu3#Znrj?{(g;rPVUYBF!&m6X-(J1KW9W7 zBw^~D5i{ih_3C|b+=#!hw}Tn&Fv<*ExFIV4ex4(iy(VG=1(R=#CeQ}@VgVhzrzRon z*1{9xVU_TTb*Vx=;%%KFZnG%v%V-4gi^a&4yQqIE9$W@uf+(gF-T zyTN6YITgkobw_LA^D}PuSBG0q5RGxN8ZAE>XPmhJTzW21Ww^&NA5v=f-mY|H;*x}DBs_tb+Lim;M3GR+e(ZwV*#aP=n)#caH}O%4fVFmQfin0nMX zM?mF|p+5FVZuW!!lnevwrW& z=bI|BiN1-vVY!6A*#BmhA0Fz&{((uwmVSx6M9Q}gFP+m zs8xe*59`P-7e2*q(p>2(F{R%7zj(@BzZ@hKd&9u%u`+SNKVf_TT03!e{dz@|3fdvxp>{#h zz3;yskHaUUk1g#O6jc$snaU4lJ%TYSgw z#grJd zJS%QnbfrPf?3?&COmm&9(*#dI?@`MXT65YP-1iN|mSVJ~PoKfao4K_$i_;Sprr?1v za*DzE`tFOYRzQ^?jZ5j8tklg{H0neeO1(VHglLejHTx1~F)RK1L`7f+PzayM>l(lu zR5~yY1Uj9vzbpvJWd|mMxxsGMZkY6i>#2da5=%q?=!oCcFNkx@AyEW;Q%l1i)%ADxh9LtpfV{`@bQuW}0~xUyh3@i5u!}+vT+d z`sA^mmS4f1-Pyf)dfef?$K_5r9D-ZSgL+_bfDQfhRsF;!K&Tfd=SzKI^FsiB3fu|A zvf ziMP=2G}+tcMzO)q(eRulAZ!9qQ;5ljvhKVuXC9gSMW1fKC_v)dK=(b|+iz$mKCYLS zFza;rgmY3}p85KftfRjRQ}>D1*A_x}2~4#@lq1)%av0aYmd@P~mNl$5|Ly<*Elw9w zz(biur~SN?1-q0cbQeU#?T{u)Un9)|UT-(zO?QAJ=+%8l|GYr|yifmZO8Ih*QnfxyRe@qHrJST?ZtS%Ls>T?ZtnzP3g6_g9 zVnuiEdGYk+U%#z~`mfUWNp{!47_Ne6lFBGwi>@}Q>TL0I-$!tM4JKqoUY<$O*gi3j zUHe}do{H{@=VLd}m#5zuTPd{dj7prilLg7m>(f+YCZKlEjnt-25zJ-x=kwteDGK9y)&yF?3&m#vAlZh7b^ciW2UC+hfVur*8CgAk@PtllIwMy`K%FtbyHW?gFR zVV{tDTK|M%%W62@a3tNXxH|t*t{z@}-iJJs-@f>@agb4@*(@f$GlpF(Qsu09aQLx; z_r7nNwj}GUz$~f6XD&E#r_pL^T+=y%hI)`&EXXCE6ZEFESqzJDJ}^x1v3UU-(Tg7qMDJe^(J5+YCH3k!g9yS&ZBWVOo!p-L)NPgg1A+7HN3rH{xQy^^3p63yB-r7Ol zbyL?$=v{po`q8p zAxC|D7FKf<+(}fcpQ(Y*a9n)AUX-WxipD@QG8%qWlL^B|HWJ=2Ut{J-Yn-R~_7k~V zJm23_CIbBKzMcjDm8K6*26~~ge zuSu=x6iYLlKftz%HR;@oNo(kME^)B1u<$mF%!XjOIFyM41CrY+8j5r#*9qYc{6Yz2 z0=KtH#~33aDvzePc<^`e#bv!TvkEKQ&WnmAEw5OAu;a7MnXzT2UZv(0NR|Im($I0A z{b?31PNf!|8r+9m83BRM0I>!q;pMm4<=3^@73RBygxR^{JwxZ*^t{r026onahH3+= zW)<0v1c&G1bCejuh_VETmQYuA{fx&zfRDEYk1cR2h3fw5PU6n&L;bNah_RA$a)knM z6v`-te>hLfCXU;P<$bQP-h=7ATe3ahP2y+q(ux~~abH&(pxZKJfcQKX57JeVZ=of|en@zgvZ)iw4ci9q zhFaG-NbO0|EAcgvA;JKZS=ZUgqBpL9H`FN>sDXcjJO2kX^?%^bGqW)LgHM3%-{4sP z0mA;}FHm`ND<@+I0E3v7zLT+tv7xPzF$^CcjH8o-vA#8oTe^0ftW7TfDd;}k-?$~z zDgZ3XNVMcv6y^t?06~|Zy+!c=hCN2J7q(72G)k0ERmU^EfW09B)1=>6#V#yf)Z2_ zjR#Z+o1|*G&tMG*N$zhsu|34n)ytCdUQ+kJ#w%APYKFRx=L@g(n=6zSv$n3e;*}p; z{mIzfxh6C67K2d33TRzx4xcl!H{LT2>4c)&pU-m5fX;D=#+o9Az?7Ld^#HbOl84xo zuD&)1+Y}nR{*x~HYHF53QC2pKIjvEDYm?a;ElW$((weQ;mXHop_^ZYALBn-NIx{#+ zI0u~Q)$AnrdjoU>^ylgeLr%mdlZL6~GIqUq^*n}gFRGi0CfBI+R7VT9nG@OWw2`&% z>vrC&f-}{SnJQhXq~-A0H_gyZ*lISXZKO{V?#WtSM{o8q1A^!)sjhh7yc zWCN^U+>@_qFVW!r&aV07S;TkO8DO$UY5&dO{|%}S003AS+5S-Y!xcU-|NjMHuIz4S z{4vb7Hco$F`#YH1IoUe=C#`^-zV*jJRSjt|1wk5Rb8BNqIb+u^w$}PK3QDrd|EVUX zZ*6Yn4xs!~1Mr~&_*3WqAR%A_a{NI;00b~Iva)@!5U_CmUn~S1oPRM4{ELNv3HWCt z{>ehX!ue;Bl9REuDu9FWKLv@ug+KWc%&m->0e@n-{}bGu`QKaSuR5F!oc^?xvV*hn zA6EObR7l^^_>T(zFDw1;bOdaF_36Kk0N~Ft`fDnR+I-ADa~o3tgPOUGppB#X-vxg* zK+W99$;=V(6~N5)H=g}ppG+K#^i1q*>>PiPg#7jRLxqKfg`Sb|qm<>(Gnm=g={ZXcm`~P_TbJm4%*zl^ytJUH=gNXzoA6fAse6uKl-e{6l2_ z_qO;iHe>l$AO3OX-*x|+PW{(r|NlxK)Gv&G%?1F2vaO1Z`5yro!2U9efC2Q)jV?g)$uF*SgO=x3GJt1B4GfA>P zYw8b$M3$&u)F#>G#ux=CW&Y)?BnVsSEW}9m{Sv<5jgh1hae6-m!r}5zlwtVi0dy~m zzL7VEurOzlp|VFU|_#5Z|y8K}nH=9V)lN9S@>p`@F^bu=qoNY8aUS?rh@ zq2+vbSZB^P26SHLobn$N(!X1|57`z~jI3R*;Nxjb+wO5*e&k7~Xel_$Sif6(wQ+65 zA^ctOxaa-m-01v#++xev+JU4cLEp6b<9oi>=q>KVex-9u$+O3PL%hHnX@HZ*gLv|kRB{=M>W{F6N6ua!sri?PW+y_K1j^?&urk7bNO$=t&j z!10%k8sIAv;3JOx$=bp2#n#pd@MpC9D{e~KnAm>A{J*Ene_p!KP4P+#>0=FCK67^9 z>1O5OFI!miN()aRiAsUXjn{Jr3F`_lQb+m+7d*jXqv*3W+)=R>+af{l-8SwHj*mm! zN2DxSVI-58II5LN;p@T7BzMyWC-?Gv07{b4<_&(PQFq>37)#e6ZBOE6zjC02%JE!E zh%arnu}#^Td=G}jhfMBaHDQb(+GC7?pf!LVJz(kyuWia{em+mdB^)WIUmHCIW)43! zx0&gS-<{b3Dm#s`zCJ-+*+(ip-cV=pe}A(>U)cRB8*Otb=uNO){IWI`+={BfzJ#^O z@GkivZy=3jINrP$%RG)+%OBjv_UV6)kE4^mgOi)Xhv_-kSb%IWWMrc9Vle*?K&(j_ literal 0 HcmV?d00001 diff --git a/test/readwrite4.jl b/test/readwrite4.jl new file mode 100644 index 0000000..c751125 --- /dev/null +++ b/test/readwrite4.jl @@ -0,0 +1,52 @@ +using MAT, Test + +function check(filename, result) + matfile = matopen(filename) + for (k, v) in result + @test exists(matfile, k) + got = read(matfile, k) + if !isequal(got, v) || (typeof(got) != typeof(v) && (!isa(got, String) || !(isa(v, String)))) + close(matfile) + error(""" + Data mismatch reading $k from $filename ($format) + + Got $(typeof(got)): + + $(repr(got)) + + Expected $(typeof(v)): + + $(repr(v)) + """) + end + end + @test union!(Set(), names(matfile)) == union!(Set(), keys(result)) + close(matfile) + + mat = matread(filename) + if !isequal(mat, result) + error(""" + Data mismatch reading $filename ($format) + + Got: + + $(repr(mat)) + + Expected: + + $(repr(result)) + """) + close(matfile) + return false + end + + return true +end +cd(dirname(@__FILE__)) +for filename in readdir("v4") + #println("testing $filename") + d = matread("v4/$filename") + matwrite4("v4/tmp.mat", d) + check("v4/tmp.mat", d) + rm("v4/tmp.mat") +end diff --git a/test/runtests.jl b/test/runtests.jl index 5b9bec1..222deaa 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,13 @@ + +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + + using SparseArrays, LinearAlgebra include("read.jl") include("write.jl") + + + diff --git a/test/runtests_modelica_manual.jl b/test/runtests_modelica_manual.jl new file mode 100644 index 0000000..d3ee86c --- /dev/null +++ b/test/runtests_modelica_manual.jl @@ -0,0 +1,126 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +# include("../src/MAT.jl") +# include("../src/MAT_v4_Modelica.jl") + +mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + + +@testset "manual" begin + rawfid = open(mat1s, "r") + + # This reader is collected from two references: + # (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) + # Matlab_MATFileFormatV4.pdf from https://www.eiscat.se/wp-content/uploads/2016/03/Version-4-MAT-File-Format.pdf + # OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf from https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/technical_details.html + + # Start by reading the header, following Matlab: + # Each matrix starts with a fixed-length 20-byte header that contains information describing certain attributes of the Matrix. + # The 20-byte header consists of five long (4-byte) integers: + @show type, nrows, ncols, imagf, namelen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + + # M indicates the numeric format of binary numbers on the machine that wrote the file. + # Use this table to determine the number to use for your machine: + # 0 IEEE Little Endian (PC, 386, 486, DEC Risc) + # 1 IEEE Big Endian (Macintosh, SPARC, Apollo,SGI, HP 9000/300, other Motorola) + # 2 VAX D-float + # 3 VAX G-float + # 4 Cray + @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 + @show numberFormat(M) + + # O is always 0 (zero) and is reserved for future use. + # @show O + + # P indicates which format the data is stored: + # 0 double-precision (64-bit) floating point numbers + # 1 single-precision (32-bit) floating point numbers + # 2 32-bit signed integers + # 3 16-bit signed integers + # 4 16-bit unsigned integers + # 5 8-bit unsigned integers + @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 + @show dataFormat(P) + @test dataFormat(P) == uint8 + + # T indicates matrix type: 1 = text matrix + @enum matrixFormat numericFull=0 text=1 spars=2 + @show matrixFormat(T) + + # nrows The row dimension contains an integer with the number of rows in the matrix. + @show nrows + # ncols The column dimension contains an integer with the number of columns in the matrix. + @show ncols + # imagf The imaginary flag is an integer whose value is either 0 or 1. If 1, then the matrix has an imaginary part. If 0, there is only real data. + @show imagf + # namelen The name length contains an integer with 1 plus the length of the matrix name. + @show namelen + + # Immediately following the fixed length header is the data whose length is dependent on the variables in the fixed length header: + # name The matrix name consists of namlen ASCII bytes, the last one of which must be a null character (’\0’ ). + @show nameuint = read!(rawfid, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + # pop!(nameuint) #trim \0 + # @show name = String(nameuint) + @show name = replace(String(nameuint), '\0'=>"") + + # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. + # @show realint = read!(rawfid, Vector{UInt8}(undef, 1*nrows*ncols)) # UInt8 from P is 8 bytes long + # @show realint = read!(rawfid, Vector{UInt16}(undef, 8*nrows*ncols)) # UInt8 from P is 8 bytes long + # @show realint = read(rawfid, 32) # UInt8 from P is 8 bytes long + @show realint = read!(rawfid, Matrix{UInt8}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + + # imag Imaginary part of the matrix, if any. If the imaginary flag imagf is nonzero, the imaginary part of a matrix is placed here. It is stored in the same manner as the real data. + if imagf==1 + @show imagint = read!(rawfid, Vector{UInt8}(undef, nrows*ncols)) # read the full namelen to make the pointer ready to read the data + end + + # The variables stored in the MAT-file are (in the order required by OpenModelica): + # Aclass + # • Aclass(1,:) is always Atrajectory + # • Aclass(2,:) is 1.1 in OpenModelica + # • Aclass(3,:) is empty + # • Aclass(4,:) is either binTrans or binNormal + + Aclass1 = replace(String(realint[1,:]), '\0'=>"") + Aclass2 = replace(String(realint[2,:]), '\0'=>"") + Aclass3 = replace(String(realint[3,:]), '\0'=>"") + Aclass4 = replace(String(realint[4,:]), '\0'=>"") + @test Aclass1 == "Atrajectory" + @test Aclass2 == "1.1" + @test isempty(Aclass3) + @test Aclass4 == "binNormal" || Aclass4 == "binTrans" + + #now to read the next matrix header... + # name: + # Is an n x m character (int8) matrix, where n is the number of variables stored in the result-file (including time). m is the length of the longest variable. OpenModelica stores the trailing part of the name as NIL bytes (0) whereas other tools use spaces for the trailing part. + @show type, nrows, ncols, imagf, namelen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + T, P, O, M = digits(type; pad=4) + @show numberFormat(M) + @show matrixFormat(T) + @show dataFormat(P) + + @show nrows + @show ncols + @show imagf + @show namelen + nameuint = read!(rawfid, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + @show name = replace(String(nameuint), '\0'=>"") + + realint = read!(rawfid, Matrix{UInt8}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + @show varname1 = replace(String(realint[:,1]), '\0'=>"") # note :,1 implicit transpose + @show varname2 = replace(String(realint[:,2]), '\0'=>"") # note :,1 implicit transpose + @show varname3 = replace(String(realint[:,3]), '\0'=>"") # note :,1 implicit transpose + @show varname4 = replace(String(realint[:,4]), '\0'=>"") # note :,1 implicit transpose + @show varname5 = replace(String(realint[:,5]), '\0'=>"") # note :,1 implicit transpose + + close(rawfid) +end + + diff --git a/test/runtests_modelica_readMatrix.jl b/test/runtests_modelica_readMatrix.jl new file mode 100644 index 0000000..95f652a --- /dev/null +++ b/test/runtests_modelica_readMatrix.jl @@ -0,0 +1,205 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +# include("../src/MAT.jl") +# include("../src/MAT_v4_Modelica.jl") + +mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + +struct MATrix + name::String + # data::T where T<:AbstractMatrix + data::Any + # type +end + +@enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 +@enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 +@enum matrixFormat numericFull=0 text=1 spars=2 + +""" +Read a single data matrix from the opened MAT file +""" +function readMATMatrix(ios::IOStream) + # The 20-byte header consists of five long (4-byte) integers: + type, nrows, ncols, imagf, namelen = read!(ios, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + + nameuint = read!(ios, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + + # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. + realint = [] + if dataFormat(P) == uint8 + realint = read!(ios, Matrix{UInt8}(undef, nrows,ncols)) + elseif dataFormat(P) == uint16 + realint = read!(ios, Matrix{UInt16}(undef, nrows,ncols)) + elseif dataFormat(P) == int32 + realint = read!(ios, Matrix{Int32}(undef, nrows,ncols)) + elseif dataFormat(P) == single + realint = read!(ios, Matrix{Float32}(undef, nrows,ncols)) + elseif dataFormat(P) == double + realint = read!(ios, Matrix{Float64}(undef, nrows,ncols)) + else + error("Unknown data format for P=$P") + end + + # imag Imaginary part of the matrix, if any. If the imaginary flag imagf is nonzero, the imaginary part of a matrix is placed here. It is stored in the same manner as the real data. + if imagf==1 + error("imaginary not implemented") + # if dataFormat(P) + # @show imagint = read!(ios, Vector{UInt8}(undef, nrows*ncols)) # read the full namelen to make the pointer ready to read the data + end + + @show matrixName + + return MATrix(matrixName, realint) +end + +""" + # The variables stored in the MAT-file are (in the order required by OpenModelica): + # Aclass + # • Aclass(1,:) is always Atrajectory + # • Aclass(2,:) is 1.1 in OpenModelica + # • Aclass(3,:) is empty + # • Aclass(4,:) is either binTrans or binNormal + + +""" +function parseAclass(mat::MATrix) + # @show mat + # Aclass1 = replace(String(mat.data[1,:]), '\0'=>"") + # Aclass2 = replace(String(data[2,:]), '\0'=>"") + # Aclass3 = replace(String(data[3,:]), '\0'=>"") + # Aclass4 = replace(String(data[4,:]), '\0'=>"") + Aclass = [ replace(String(mat.data[1,:]), '\0'=>""), + replace(String(mat.data[2,:]), '\0'=>""), + replace(String(mat.data[3,:]), '\0'=>""), + replace(String(mat.data[4,:]), '\0'=>"") ] + return Aclass +end + + +""" +name +Is an n x m character (int8) matrix, where n is the number of variables stored in the result-file (including time). +m is the length of the longest variable. +OpenModelica stores the trailing part of the name as NIL bytes (0) whereas other tools use spaces for the trailing part. +""" +function parseVariableNames(mat::MATrix) + varNames = [] + m,n = size(mat.data) + for i in 1:n + push!(varNames, replace(String(mat.data[:,i]), '\0'=>"")) + end + return varNames +end + +""" +Find the index of the given variable, returning -1 if not found. +""" +function getVariableIndex(variableNames, name) + vecAll = findall( x->x==name, variableNames) + n = length(vecAll) + + if isempty(vecAll) == true + return -1 + else + if n>1 + error("Found $n instances of variable [$name], but variables should be unique.") + end + return vecAll[1] + end +end + + +""" +description +Is an n x m character (int8) matrix containing the comment-string corresponding to the variable in the name matrix +""" +function parseVariableDescriptions(mat::MATrix) + varDesc = [] + m,n = size(mat.data) + for i in 1:n + push!(varDesc, replace(String(mat.data[:,i]), '\0'=>"")) + end + return varDesc +end + +""" +dataInfo provides indicies to access variable data + +dataInfo +Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). +• dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). +• dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. +• dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. +• dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. +""" +function parseDataInfo(mat::MATrix, varIndex::Int) + # @show mat.data[:,varIndex] + return Dict("locatedInData"=>mat.data[1,varIndex], "indexInData"=>mat.data[2,varIndex], "isInterpolated"=>mat.data[3,varIndex], "isWithinTimeRange"=>mat.data[4,varIndex] ) +end + +""" +data_1 +If it is an n x 1 matrix it contains the values of parameters. If it is an n x 2 matrix, the first and second column +signify start and stop-values. +""" +function parseData1(mat::MATrix, varIndex::Int) + @show size(mat.data) + @show mat.data[varIndex,:] +end + + +# using Profile + +@testset "matrix reader" begin + matio = open(mat1s, "r") + seekstart(matio) + ret = readMATMatrix(matio) + Aclass = parseAclass(ret) + @test Aclass[1] == "Atrajectory" + @test Aclass[2] == "1.1" + @test isempty(Aclass[3]) == true + @test Aclass[4] == "binTrans" || Aclass[4] == "binNormal" + + ret = readMATMatrix(matio) # name + variableNames = parseVariableNames(ret) + @test variableNames[1] == "time" + @test variableNames[2490] == "world.z_label.color[3]" + @test getVariableIndex(variableNames, "time") == 1 + + + ret = readMATMatrix(matio) # description + variableDescriptions = parseVariableDescriptions(ret) + @test variableDescriptions[2490] == "Color of cylinders" + + # @allocated readMATMatrix(matio) # dataInfo + ret = readMATMatrix(matio) # dataInfo + @show dataInfo = parseDataInfo(ret, 1) + + ret = readMATMatrix(matio) # data_1 + + + # itime = getVariableIndex(variableNames, "time") + # # ditime = parse + # parseData1(ret, itime) + + # parseData1(ret, iphi) + # # parseData1(ret, ihfaf1) + + # iphi = getVariableIndex(variableNames, "revolute.phi") + # ihfaf1 = getVariableIndex(variableNames, "head.frame_a.f[1]") + + ret = readMATMatrix(matio) # data_2 + close(matio) + + @test true + +end diff --git a/test/v4/testcomplex_4.2c_SOL2.mat b/test/v4/testcomplex_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..36621b25c08f18e4545100c6eaec015123c3bf9f GIT binary patch literal 176 zcmZQzV1B{Cz`zK^oKTvlB(=CCIX|}`C$)mX{sT}H2<)FNn3q;>eb#2;GBsn7820@T p{+azyc_{zfo>i5WKJ&V`pz2S<^g~R6n{x&x4mWop0dqG(`$IPYCV6bhD=3Sn-kr dCzQ{hRiznf5$7NT6&L-x`{nNCr4Eu1c>u#Y9lihn literal 0 HcmV?d00001 diff --git a/test/v4/testmatrix_4.2c_SOL2.mat b/test/v4/testmatrix_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..3698c8853b46d4a42194002523b57fddfb225908 GIT binary patch literal 151 zcmZQzV1B{Cz`zW|tUwF`+$E{SCAo0_8%Z(4iJjLfdiEf6^2tVdAI64fzvu`z literal 0 HcmV?d00001 diff --git a/test/v4/testminus_4.2c_SOL2.mat b/test/v4/testminus_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..cc207ed9f32095f39b7690e2dc1e2dc0d55ee8e0 GIT binary patch literal 38 kcmZQzV1B{Cz`zK_K#GB@B(=CCH#4uam|_11kN^V%0A!m6lK=n! literal 0 HcmV?d00001 diff --git a/test/v4/testmulti_4.2c_SOL2.mat b/test/v4/testmulti_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..9c6ba793cf41bf36447ab7a1890447fe5e939614 GIT binary patch literal 240 zcmZQzV1B{Cz`zW|tUwF`Oo>TXrytD# literal 0 HcmV?d00001 diff --git a/test/v4/testonechar_4.2c_SOL2.mat b/test/v4/testonechar_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..cdb4191c7d2eb0ac66d4f6add250e1f6a604d892 GIT binary patch literal 40 mcmZQzV1CKKz`zK_K#GBoBe96VA*KN&#sC0t%m%jr literal 0 HcmV?d00001 diff --git a/test/v4/testsparse_4.2c_SOL2.mat b/test/v4/testsparse_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..55cbd3c1b3d65630beae47832ffbcc7a6fd43354 GIT binary patch literal 223 zcmZQzV1C8Gz`y~-%s>nR+$E{SCB+4aMa8KM_8%Z(4iJjL0i+NJVB)xFLh2mArZB+G Wa}aKwqPFu=`o5P%3ch@jFi^D)#D&<~Y{yA#GIl?DK-2^cH@ literal 0 HcmV?d00001 diff --git a/test/v4/teststring_4.2c_SOL2.mat b/test/v4/teststring_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..137561e1f636d7b08959e43e969a6984eb7a3b37 GIT binary patch literal 375 zcmZ{gO%6an423I}j^YxvVJTQKEJbWAm;*SP>$ruVzPB0Cq-nqQwbP79e2PePdwTn0 zi61w=`E_0<(adUEA-dyDRLQ$>X9acO7HmP(fmx@H{cwJe*OdBxH||jjyl4?yTB eFvZ{y=>Xxw^u;tl_z+ Date: Wed, 8 Feb 2023 16:53:55 -0600 Subject: [PATCH 04/91] restore readMatrix dev file --- test/runtests_modelica_readMatrix.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/runtests_modelica_readMatrix.jl b/test/runtests_modelica_readMatrix.jl index 95f652a..d0fb41c 100644 --- a/test/runtests_modelica_readMatrix.jl +++ b/test/runtests_modelica_readMatrix.jl @@ -24,8 +24,9 @@ end Read a single data matrix from the opened MAT file """ function readMATMatrix(ios::IOStream) + @show startP = position(ios) # The 20-byte header consists of five long (4-byte) integers: - type, nrows, ncols, imagf, namelen = read!(ios, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + type, nrows, ncols, imagf, namelen = read!(ios, Vector{Int32}(undef, 5)) # 32bits=4byte, * 5 qty #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... T, P, O, M = digits(type; pad=4) @@ -35,6 +36,7 @@ function readMATMatrix(ios::IOStream) # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. realint = [] + @show dataFormat(P) if dataFormat(P) == uint8 realint = read!(ios, Matrix{UInt8}(undef, nrows,ncols)) elseif dataFormat(P) == uint16 @@ -57,6 +59,7 @@ function readMATMatrix(ios::IOStream) end @show matrixName + @show endP = position(ios) return MATrix(matrixName, realint) end @@ -157,8 +160,6 @@ function parseData1(mat::MATrix, varIndex::Int) end -# using Profile - @testset "matrix reader" begin matio = open(mat1s, "r") seekstart(matio) @@ -176,6 +177,7 @@ end @test getVariableIndex(variableNames, "time") == 1 + @show position(matio) ret = readMATMatrix(matio) # description variableDescriptions = parseVariableDescriptions(ret) @test variableDescriptions[2490] == "Color of cylinders" From 42ac57533108be483f2cbb9f6f3bf6a19c6bd524 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Wed, 8 Feb 2023 16:54:40 -0600 Subject: [PATCH 05/91] temp commit, need to get a smaller reference model --- test/runtests_modelica.jl | 479 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100644 test/runtests_modelica.jl diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl new file mode 100644 index 0000000..909ebf7 --- /dev/null +++ b/test/runtests_modelica.jl @@ -0,0 +1,479 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +# include("../src/MAT.jl") +# include("../src/MAT_v4_Modelica.jl") + +const mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + + + +# The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices +# The first matrix, Aclass is narrowly defined + +function isLittleEndian(dtype) :: Bool + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(dtype; pad=4) + # @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 + return M == 0 +end + +@testset "isLittleEndian" begin + @test isLittleEndian(0) == true + @test isLittleEndian(1000) == false + @test isLittleEndian(2000) == false + @test isLittleEndian(3000) == false +end + +function dataFormat(type) :: DataType + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + # @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 + if P == 0 + return Float64 + end + if P == 1 + return Float32 + end + if P == 2 + return Int32 + end + if P == 3 + return Int16 + end + if P == 4 + return UInt16 + end + if P == 5 + return UInt8 + end +end +@testset "dataFormat" begin + @test dataFormat(0000) <: Float64 + @test dataFormat(0010) <: Float32 + @test dataFormat(0020) <: Int32 + @test dataFormat(0030) <: Int16 + @test dataFormat(0040) <: UInt16 + @test dataFormat(0050) <: UInt8 +end + +function typeBytes(type::T)::Int where T<:DataType + if type == Float64 + return 8 + end + if type == Float32 + return 4 + end + if type == Int32 + return 4 + end + if type == Int16 + return 2 + end + if type == UInt16 + return 2 + end + if type == UInt8 + return 1 + end +end +@testset "typeBits" begin + @test typeBits(Int32) == 4 +end + +struct Aclass + filepath::String + isTranspose::Bool + positionStart::Int + positionEnd::Int +end + +""" +Reads the Aclass matrix, returing if the data is stored binNormal or binTranspose +""" +function readAclass( filepath::String ) + open(filepath, "r", lock=false) do matio + seekstart(matio) # always start from the start, don't assume the position + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # 32bit=4byte * 5 qty + catch e + error("caught error $e while reading $filepath") + end + + if !isLittleEndian(dtype) + error("Only the little-endian encoding is implemented, cannot read $filepath") + end + + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + name = replace(String(nameuint), '\0'=>"") + if name != "Aclass" + error("First matrix must be named Aclass, is instead [$name]. This likely means that [$filepath] is not a Modelica MAT v4 file.") + end + + # real: the Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. + fmt = dataFormat(dtype) # read the format type before reading + realint = read!(matio, Matrix{UInt8}(undef, nrows,ncols)) + + Aclass1 = replace(String(realint[1,:]), '\0'=>"") + Aclass2 = replace(String(realint[2,:]), '\0'=>"") + Aclass3 = replace(String(realint[3,:]), '\0'=>"") + Aclass4 = replace(String(realint[4,:]), '\0'=>"") + if Aclass1 == "Atrajectory" && Aclass2 == "1.1" && isempty(Aclass3) && Aclass4 == "binNormal" || Aclass4 == "binTrans" + return Aclass( filepath, Aclass4 == "binTranspose", startP, position(matio) ) + end + end #open +end + +@testset "Aclass" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + @test ac.positionStart == 0 + @test ac.positionEnd == 71 +end + + +##### the NAME matrix######################################################################################################################## +struct VariableNames + # names::Vector{T}(undef,undef) where T<:AbstractString + names::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableNames(ac::Aclass) + open(ac.filepath, "r", lock=false) do matio + seek(matio, ac.positionEnd) #skip over Aclass + startP = position(matio) + + #read the matrix header + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "name" + error("trying to read matrix [name] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realint = [] + try + realint = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + #pull the names out of the matrix + vnames = [] + for i in 1:ncols + push!(vnames, replace(String(realint[:,i]), '\0'=>"")) # note :,1 = implicit transpose + end + + return VariableNames(vnames, startP, position(matio)) + + end #open +end + +@testset "readVariableNames" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + # @show vn + @test length(vn.names) == 2490 + @test vn.names[1] == "time" + @test vn.names[3] == "revolute.w" + @test vn.names[30] == "der(alignElastoBacklash.frame_a.r_0[1])" + @test vn.names[2490] == "world.z_label.color[3]" + @test vn.positionStart == 71 + @test vn.positionEnd == 117126 +end + +function getVariableIndex(vn::VariableNames, name::String) + vecAll = findall( x->x==name, vn.names) + n = length(vecAll) + + if isempty(vecAll) == true + return -1 + else + if n>1 + error("Found $n instances of variable [$name], but variables should be unique.") + end + return vecAll[1] + end +end + +@testset "getVariableIndex" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + @test getVariableIndex(vn, vn.names[3]) == 3 + @test getVariableIndex(vn, vn.names[30]) == 30 +end + +struct VariableDescriptions + names::Vector{String} + descriptions::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableDescriptions(ac::Aclass, vn::VariableNames) + open(ac.filepath, "r", lock=false) do matio + seek(matio, vn.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "description" + error("trying to read matrix [description] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + vdesc = [] + for i in 1:ncols + push!(vdesc, replace(String(realread[:,i]), '\0'=>"")) # note :,1 = implicit transpose + end + + return VariableDescriptions(vn.names, vdesc, startP, position(matio)) + end #open +end + +@testset "readVariableDescriptions" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + @test length(vd.descriptions) == 2490 + @test vd.descriptions[1] == "Simulation time [s]" + @test vd.descriptions[3] == "First derivative of angle phi (relative angular velocity) [rad/s]" + @test vd.descriptions[30] == "Position vector from world frame to the connector frame origin, resolved in world frame" + @test vd.descriptions[2490] == "Color of cylinders" +end + +struct DataInfo + info + positionStart::Int + positionEnd::Int +end + +""" +dataInfo provides indicies to access variable data + +dataInfo +Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). + dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). + dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. + dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. + dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. +""" +function readDataInfo(ac::Aclass, vd::VariableDescriptions) + open(ac.filepath, "r", lock=false) do matio + seek(matio, vd.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "dataInfo" + error("trying to read variable [dataInfo] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + dinfo = [] + for i in 1:ncols + push!(dinfo, Dict("name"=>vd.names[i], "description"=>vd.descriptions[i], "locatedInData"=>realread[1,i], "indexInData"=>realread[2,i], "isInterpolated"=>realread[3,i], "isWithinTimeRange"=>realread[4,i] ) ) + end + return DataInfo( dinfo, startP, position(matio)) + end #open +end + + +@testset "readDataInfo" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + # @show di.info[3] + @test di.info[1]["isWithinTimeRange"] == -1 + @test di.info[3]["locatedInData"] == 2 + @test di.info[30]["isInterpolated"] == 0 + @test di.info[2490]["isWithinTimeRange"] == 0 +end + + +""" +read one variable from the thing +to read a variable, we need its index, then to look up whether it is in data_1 or data_2 +""" +function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) + open(ac.filepath, "r", lock=false) do matio + seek(matio, di.positionEnd) #this follows the VariableNames matrix + # @show startP = position(matio) + + println("\ndata_1:") + @show data1HeaderStart = mark(matio) + # read data1 header: + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + @show dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + @show data1MatrixName = mark(matio) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + @show matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "data_1" + error("trying to read matrix [data_1] but read $matrixName") + end + + @show data1MatrixStart = mark(matio) + #read the matrix data_1 + @show fmt1 = dataFormat(dtype) # read the format type before reading + # realread = [] + try + # realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + @show position(matio) + @show toskip = nrows*ncols*typeBytes(fmt1) #817*2*8 = 13072, 488197+20+7+13072 = 501296 + skip(matio, toskip ) + @show position(matio) + catch e + error("caught error $e while reading $ac.filepath") + end + + # read data2 header: + println("\ndata_2:") + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + @show dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + @show data2MatrixName = mark(matio) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + @show matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "data_2" + error("trying to read matrix [data_2] but read $matrixName") + end + + #read the matrix data_2 + @show data2MatrixStart = mark(matio) + @show fmt2 = dataFormat(dtype) # read the format type before reading + + # so, having found both headers and matrix starts, need to calculate the variable's position and length + println("\nlocate variable [$name]:") + @show varInd = getVariableIndex(vn, name) + @show di.info[varInd] + if di.info[varInd]["locatedInData"] == 1 #data_1 + @show pstart = data1MatrixStart + di.info[varInd]["indexInData"] * typeBytes(fmt1) + else #data_2 + @show pstart = data2MatrixStart + di.info[varInd]["indexInData"] * ncols * typeBytes(fmt2) + @show pstop = data2MatrixStart + di.info[varInd]["indexInData"] * ncols * typeBytes(fmt2) *2 + @show pstop-pstart + seek(matio, pstart) + # readreal = read!(matio, Vector{fmt2}(undef, pstop-pstart) ) + # @show readreal[1:100] + + read100 = read!(matio, Vector{fmt2}(undef, 100) ) + + seek(matio, pstart) + readns = [] + for i = 1:100 + append!(readns, read!(matio, Vector{fmt2}(undef,1))) + skip(matio, typeBytes(fmt2)*ncols ) + println( "$i] " , vn.names[i] , ": " , read100[i], " : ", readns[i]) + end + end + + end #open +end + +@testset "readVariable" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + readVariable(ac, vn, vd, di, "time") + # readVariable(ac, vn, vd, di, "revolute.w") +end + + +; + From 0c1f9c9b831e831b528a35fa52b2c6e1dd1885fe Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 10:27:09 -0600 Subject: [PATCH 06/91] transpose read working --- test/Modelica/BouncingBall/BouncingBall.mo | 29 ++++++++++++ .../BouncingBall/BouncingBall_res.csv | 32 +++++++++++++ .../BouncingBall/BouncingBall_res.mat | Bin 0 -> 1761 bytes .../Modelica/FallingBodyBox/FallingBodyBox.mo | 16 +++++++ test/runtests_modelica.jl | 43 +++++++++++------- 5 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 test/Modelica/BouncingBall/BouncingBall.mo create mode 100644 test/Modelica/BouncingBall/BouncingBall_res.csv create mode 100644 test/Modelica/BouncingBall/BouncingBall_res.mat create mode 100644 test/Modelica/FallingBodyBox/FallingBodyBox.mo diff --git a/test/Modelica/BouncingBall/BouncingBall.mo b/test/Modelica/BouncingBall/BouncingBall.mo new file mode 100644 index 0000000..3e5b213 --- /dev/null +++ b/test/Modelica/BouncingBall/BouncingBall.mo @@ -0,0 +1,29 @@ +model BouncingBall + parameter Real eff=0.77 "coefficient of restitution"; + parameter Real grav=9.81 "gravity acceleration"; + Real height(fixed=true, start=111) "height of ball"; + Real vel(fixed=true) "velocity of ball"; + Boolean flying(fixed=true, start=true) "true, if ball is flying"; + Boolean impact; + Real v_new(fixed=true); + Integer foo; + +equation + impact = height <= 0.0; + foo = if impact then 1 else 2; + der(vel) = if flying then -grav else 0; + der(height) = vel; + + when {height <= 0.0 and vel <= 0.0, impact} then + //when {impact} then + v_new = if edge(impact) then -eff*pre(vel) else 0; + flying = v_new > 0; + reinit(vel, v_new); + end when; + + //copy files from C:/Users/BenConrad/AppData/Local/Temp/OpenModelica/OMEdit/BouncingBall + annotation( + experiment(StartTime=0, StopTime=1, Tolerance=1e-3, Interval=0.1) + ); + +end BouncingBall; diff --git a/test/Modelica/BouncingBall/BouncingBall_res.csv b/test/Modelica/BouncingBall/BouncingBall_res.csv new file mode 100644 index 0000000..1864caf --- /dev/null +++ b/test/Modelica/BouncingBall/BouncingBall_res.csv @@ -0,0 +1,32 @@ +name,time,eff,grav,der(height),der(vel),flying,foo,height,impact,v_new,vel,,,, +data,0,1,1,2,2,2,2,2,2,2,2,,,, +,0,0.77,9.81,0,-9.81,1,2,111,0,0,0,,,, +,0.1,0.77,9.81,-0.981,-9.81,1,2,110.9509495,0,0,-0.981,,,time,0 +,0.2,0.77,9.81,-1.962,-9.81,1,2,110.8037994,0,0,-1.962,,,height,111 +,0.3,0.77,9.81,-2.943,-9.81,1,2,110.5585494,0,0,-2.943,,,,0 +,0.4,0.77,9.81,-3.924,-9.81,1,2,110.2151994,0,0,-3.924,,,,0 +,0.5,0.77,9.81,-4.905,-9.81,1,2,109.7737494,0,0,-4.905,,,der(vel),-9.81 +,0.6,0.77,9.81,-5.886,-9.81,1,2,109.2341993,0,0,-5.886,,,,0 +,0.7,0.77,9.81,-6.867,-9.81,1,2,108.5965493,0,0,-6.867,,,foo,2 +,0.8,0.77,9.81,-7.848,-9.81,1,2,107.8607993,0,0,-7.848,,,flying,1 +,0.9,0.77,9.81,-8.829,-9.81,1,2,107.0269493,0,0,-8.829,,,,0 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,time,0.1 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,height,110.9509495 +,,,,,,,,,,,,,,der(height)/vel,-0.981 +,,,,,,,,,,,,,,der(height)/vel,-0.981 +,,,,,,,,,,,,,,der(vel),-9.81 +,,,,,,,,,,,,,,,0 +,,,,,,,,,,,,,,foo,2 +,,,,,,,,,,,,,,flying,1 +,,,,,,,,,,,,,,,0 +,,,,,,,,,,,,,,time,0.2 +,,,,,,,,,,,,,,height,110.8037994 +,,,,,,,,,,,,,,der(height)/vel,-1.962 +,,,,,,,,,,,,,,der(height)/vel,-1.962 +,,,,,,,,,,,,,,der(vel),-9.81 +,,,,,,,,,,,,,,impact/vel,0 +,,,,,,,,,,,,,,foo,2 +,,,,,,,,,,,,,,flying,1 +,,,,,,,,,,,,,,impact/vel,0 +,,,,,,,,,,,,,,,0.3 +,,,,,,,,,,,,,,,110.5585494 diff --git a/test/Modelica/BouncingBall/BouncingBall_res.mat b/test/Modelica/BouncingBall/BouncingBall_res.mat new file mode 100644 index 0000000000000000000000000000000000000000..8e7f76e1823f2de740c26dd2869522b7fbdec7d2 GIT binary patch literal 1761 zcmcgqJ!lj`6dr%_;}51V7$oAYoCJeW%w1t6vq2DS1W^lb(POhWdl|XxUUv5qQz%$O z5wWx|h)pa6EreJY5N-u60>oB4s-pOu;u)+(p6<9eleAgJZD^1gS1$gxx z8BMM|-@F;kCl}LuarW>1t@AA=)|o~@O$-=PSvF*7w6^RYT8pc4VLQT=n}l=(Q_=jgVsyFyBN4FxDtWz#88b z0qb@ufU(BsOTfC53Sg|!5COg57y;|n^Kl$&T7%knnc^VMBzRCgw)Nf2SDu;re)`${ ztf@wq`C8+6G`>8p_m7lISt&Zx`C%z-M(mOR~nQGroy`MN6Q$IDEikp2__xjAA*7KR)r!2ECxmQn2 zoYs$<;0x!o10N6dS?0kTyB6O}^v1+#byR=8@y5c^xt?DI{l7B0Kbh41S$K{3!Tqi) lH|Jgy%zH15C&9ygF>zWy)UWQ#ZzsC17j&Pqt3Lj>&L8j$T&Ms5 literal 0 HcmV?d00001 diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox.mo b/test/Modelica/FallingBodyBox/FallingBodyBox.mo new file mode 100644 index 0000000..6e74314 --- /dev/null +++ b/test/Modelica/FallingBodyBox/FallingBodyBox.mo @@ -0,0 +1,16 @@ +model FallingBodyBox + inner Modelica.Mechanics.MultiBody.World world annotation( + Placement(visible = true, transformation(origin = {-70, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); + Modelica.Mechanics.MultiBody.Parts.BodyBox bodyBox(r = {1, 0, 0}) annotation( + Placement(visible = true, transformation(origin = {10, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); + Modelica.Mechanics.MultiBody.Joints.FreeMotion freeMotion annotation( + Placement(visible = true, transformation(origin = {-30, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); +equation + connect(world.frame_b, freeMotion.frame_a) annotation( + Line(points = {{-60, 10}, {-40, 10}})); + connect(freeMotion.frame_b, bodyBox.frame_a) annotation( + Line(points = {{-20, 10}, {0, 10}}, color = {95, 95, 95})); + +annotation( + uses(Modelica(version = "4.0.0"))); +end FallingBodyBox; diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 909ebf7..37bc920 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -443,35 +443,46 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d if di.info[varInd]["locatedInData"] == 1 #data_1 @show pstart = data1MatrixStart + di.info[varInd]["indexInData"] * typeBytes(fmt1) else #data_2 - @show pstart = data2MatrixStart + di.info[varInd]["indexInData"] * ncols * typeBytes(fmt2) - @show pstop = data2MatrixStart + di.info[varInd]["indexInData"] * ncols * typeBytes(fmt2) *2 - @show pstop-pstart + @show pstart = data2MatrixStart + (di.info[varInd]["indexInData"]-1) * ncols * typeBytes(fmt2) + pstop = pstart + ncols*typeBytes(fmt2) + # @show pstop-pstart seek(matio, pstart) - # readreal = read!(matio, Vector{fmt2}(undef, pstop-pstart) ) - # @show readreal[1:100] - - read100 = read!(matio, Vector{fmt2}(undef, 100) ) + # readreal = read!(matio, Vector{fmt2}(undef, 30) ) + for i = 1:30 + ps = position(matio) + readreal = read!(matio, Vector{fmt2}(undef,1)) + println("$i [$ps] = $(readreal[1])") + end - seek(matio, pstart) + # 'transpose' reading, that is the data is saved time, var1(time), var2(time),... time2, var1(time2),... readns = [] - for i = 1:100 - append!(readns, read!(matio, Vector{fmt2}(undef,1))) - skip(matio, typeBytes(fmt2)*ncols ) - println( "$i] " , vn.names[i] , ": " , read100[i], " : ", readns[i]) + nvar = 9 # == nrows == number of variables listing data2 as their location + bytesPerBar = 8 # typeBytes(fmt2) == Float64 + for ivar = 1:nrows + for ind = 1:ncols + seek(matio, pstart + (ivar-1)*bytesPerBar + ((ind-1)*nvar*bytesPerBar) ) + readns = read!(matio, Vector{fmt2}(undef, 1) ) + println( "$ind] : " , readns[1]) + end end end end #open end +using JSON @testset "readVariable" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) + # mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + matbb = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = readAclass(matbb) vn = readVariableNames(ac) vd = readVariableDescriptions(ac,vn) di = readDataInfo(ac,vd) - readVariable(ac, vn, vd, di, "time") - # readVariable(ac, vn, vd, di, "revolute.w") + # println(JSON.json(di.info, 2)) #get the data 1/2 info + # readVariable(ac, vn, vd, di, "eff") #data1 + # readVariable(ac, vn, vd, di, "grav") #data1 + readVariable(ac, vn, vd, di, "time") # data0 + # readVariable(ac, vn, vd, di, "height") #data2 end From 46565c8dc277e8cc802754b71636c3c5ea20a7c9 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 11:46:48 -0600 Subject: [PATCH 07/91] can read all data in BouncingBall --- .../BouncingBall/BouncingBall_res.csv | 31 ++-- test/runtests_modelica.jl | 138 +++++++++++------- test/runtests_modelica_readMatrix.jl | 2 + 3 files changed, 101 insertions(+), 70 deletions(-) diff --git a/test/Modelica/BouncingBall/BouncingBall_res.csv b/test/Modelica/BouncingBall/BouncingBall_res.csv index 1864caf..d4a1a70 100644 --- a/test/Modelica/BouncingBall/BouncingBall_res.csv +++ b/test/Modelica/BouncingBall/BouncingBall_res.csv @@ -1,25 +1,22 @@ -name,time,eff,grav,der(height),der(vel),flying,foo,height,impact,v_new,vel,,,, -data,0,1,1,2,2,2,2,2,2,2,2,,,, -,0,0.77,9.81,0,-9.81,1,2,111,0,0,0,,,, -,0.1,0.77,9.81,-0.981,-9.81,1,2,110.9509495,0,0,-0.981,,,time,0 -,0.2,0.77,9.81,-1.962,-9.81,1,2,110.8037994,0,0,-1.962,,,height,111 +name,time,eff,grav,der(height),der(vel),flying,foo,height,impact,v_new,vel,,t=0,time,0 +data,0,1,1,2,2,2,2,2,2,2,2,,,height,111 +,0,0.77,9.81,0,-9.81,1,2,111,0,0,0,,,,0 +,0.1,0.77,9.81,-0.981,-9.81,1,2,110.9509495,0,0,-0.981,,,,0 +,0.2,0.77,9.81,-1.962,-9.81,1,2,110.8037994,0,0,-1.962,,,der(vel),-9.81 ,0.3,0.77,9.81,-2.943,-9.81,1,2,110.5585494,0,0,-2.943,,,,0 -,0.4,0.77,9.81,-3.924,-9.81,1,2,110.2151994,0,0,-3.924,,,,0 -,0.5,0.77,9.81,-4.905,-9.81,1,2,109.7737494,0,0,-4.905,,,der(vel),-9.81 +,0.4,0.77,9.81,-3.924,-9.81,1,2,110.2151994,0,0,-3.924,,,foo,2 +,0.5,0.77,9.81,-4.905,-9.81,1,2,109.7737494,0,0,-4.905,,,flying,1 ,0.6,0.77,9.81,-5.886,-9.81,1,2,109.2341993,0,0,-5.886,,,,0 -,0.7,0.77,9.81,-6.867,-9.81,1,2,108.5965493,0,0,-6.867,,,foo,2 -,0.8,0.77,9.81,-7.848,-9.81,1,2,107.8607993,0,0,-7.848,,,flying,1 -,0.9,0.77,9.81,-8.829,-9.81,1,2,107.0269493,0,0,-8.829,,,,0 -,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,time,0.1 -,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,height,110.9509495 -,,,,,,,,,,,,,,der(height)/vel,-0.981 -,,,,,,,,,,,,,,der(height)/vel,-0.981 -,,,,,,,,,,,,,,der(vel),-9.81 +,0.7,0.77,9.81,-6.867,-9.81,1,2,108.5965493,0,0,-6.867,,t=0.1,time,0.1 +,0.8,0.77,9.81,-7.848,-9.81,1,2,107.8607993,0,0,-7.848,,,height,110.9509495 +,0.9,0.77,9.81,-8.829,-9.81,1,2,107.0269493,0,0,-8.829,,,der(height)/vel,-0.981 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,der(height)/vel,-0.981 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,der(vel),-9.81 ,,,,,,,,,,,,,,,0 ,,,,,,,,,,,,,,foo,2 ,,,,,,,,,,,,,,flying,1 ,,,,,,,,,,,,,,,0 -,,,,,,,,,,,,,,time,0.2 +,,,,,,,,,,,,,t=0.2,time,0.2 ,,,,,,,,,,,,,,height,110.8037994 ,,,,,,,,,,,,,,der(height)/vel,-1.962 ,,,,,,,,,,,,,,der(height)/vel,-1.962 @@ -28,5 +25,3 @@ data,0,1,1,2,2,2,2,2,2,2,2,,,, ,,,,,,,,,,,,,,foo,2 ,,,,,,,,,,,,,,flying,1 ,,,,,,,,,,,,,,impact/vel,0 -,,,,,,,,,,,,,,,0.3 -,,,,,,,,,,,,,,,110.5585494 diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 37bc920..2db8577 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -80,8 +80,8 @@ function typeBytes(type::T)::Int where T<:DataType return 1 end end -@testset "typeBits" begin - @test typeBits(Int32) == 4 +@testset "typeBytes" begin + @test typeBytes(Int32) == 4 end struct Aclass @@ -364,109 +364,126 @@ end @test di.info[2490]["isWithinTimeRange"] == 0 end +struct MatrixHeader + type::Int + nRows::Int + nCols::Int + hasImaginary::Bool + lName::Int +end """ read one variable from the thing to read a variable, we need its index, then to look up whether it is in data_1 or data_2 """ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) - open(ac.filepath, "r", lock=false) do matio + display(ac) + + + + open(ac.filepath, "r", lock=false) do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix - # @show startP = position(matio) println("\ndata_1:") - @show data1HeaderStart = mark(matio) # read data1 header: - # The 20-byte header consists of five long (4-byte) integers: + data1HeaderStart = mark(matio) dtype = 0 nrows = 0 ncols = 0 namelen = 0 includesImaginary = 0 try - @show dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte catch e error("caught error $e while reading $ac.filepath") end + mh1 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - @show data1MatrixName = mark(matio) + data1MatrixName = mark(matio) nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - @show matrixName = replace(String(nameuint), '\0'=>"") + matrixName = replace(String(nameuint), '\0'=>"") if matrixName != "data_1" error("trying to read matrix [data_1] but read $matrixName") end - @show data1MatrixStart = mark(matio) - #read the matrix data_1 - @show fmt1 = dataFormat(dtype) # read the format type before reading - # realread = [] + #skip dataMatrix1 + data1MatrixStart = mark(matio) + fmt1 = dataFormat(dtype) # read the format type before reading try - # realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - @show position(matio) - @show toskip = nrows*ncols*typeBytes(fmt1) #817*2*8 = 13072, 488197+20+7+13072 = 501296 + toskip = nrows*ncols*typeBytes(fmt1) #817*2*8 = 13072, 488197+20+7+13072 = 501296 skip(matio, toskip ) - @show position(matio) catch e error("caught error $e while reading $ac.filepath") end # read data2 header: println("\ndata_2:") - # The 20-byte header consists of five long (4-byte) integers: dtype = 0 nrows = 0 ncols = 0 namelen = 0 includesImaginary = 0 try - @show dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte catch e error("caught error $e while reading $ac.filepath") end + mh2 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) #read the matrix name - @show data2MatrixName = mark(matio) + data2MatrixName = mark(matio) nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - @show matrixName = replace(String(nameuint), '\0'=>"") + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "data_2" error("trying to read matrix [data_2] but read $matrixName") end + data2MatrixStart = mark(matio) - #read the matrix data_2 - @show data2MatrixStart = mark(matio) - @show fmt2 = dataFormat(dtype) # read the format type before reading + #with the positions marked, read the desired variable + # println("\nlocate variable [$name]:") + varInd = getVariableIndex(vn, name) - # so, having found both headers and matrix starts, need to calculate the variable's position and length - println("\nlocate variable [$name]:") - @show varInd = getVariableIndex(vn, name) - @show di.info[varInd] if di.info[varInd]["locatedInData"] == 1 #data_1 - @show pstart = data1MatrixStart + di.info[varInd]["indexInData"] * typeBytes(fmt1) - else #data_2 - @show pstart = data2MatrixStart + (di.info[varInd]["indexInData"]-1) * ncols * typeBytes(fmt2) - pstop = pstart + ncols*typeBytes(fmt2) - # @show pstop-pstart - seek(matio, pstart) - # readreal = read!(matio, Vector{fmt2}(undef, 30) ) - for i = 1:30 - ps = position(matio) - readreal = read!(matio, Vector{fmt2}(undef,1)) - println("$i [$ps] = $(readreal[1])") + #read the matrix data_1 + fmt1 = dataFormat(dtype) # read the format type before reading + # @show di.info[varInd] + + # dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. + if di.info[varInd]["isWithinTimeRange"]== 0 #linear interpolation + seek(matio, data1MatrixStart) + realread = read!(matio, Vector{fmt1}(undef,10)) + display(realread) + #data format is: time(tInitial), var1(tI), ... varN(tI), time(tFinal), var1(tF), ... varN(tF) + # seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt1) + ((ind-1)*nrows*typeBytes(fmt1)) ) + # readns[ind] = read(matio, fmt1) + + readns = Vector{fmt1}(undef, mh1.nCols) + for ind = 1:mh1.nCols + seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt1) + ((ind-1)*mh1.nRows*typeBytes(fmt1)) ) + readns[ind] = read(matio, fmt1) + end + return readns end - # 'transpose' reading, that is the data is saved time, var1(time), var2(time),... time2, var1(time2),... - readns = [] - nvar = 9 # == nrows == number of variables listing data2 as their location - bytesPerBar = 8 # typeBytes(fmt2) == Float64 - for ivar = 1:nrows + elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 + #read the matrix data_2 + fmt2 = dataFormat(dtype) # read the format type before reading + + if ac.isTranspose == false + # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... + readns = Vector{fmt2}(undef, ncols) for ind = 1:ncols - seek(matio, pstart + (ivar-1)*bytesPerBar + ((ind-1)*nvar*bytesPerBar) ) - readns = read!(matio, Vector{fmt2}(undef, 1) ) - println( "$ind] : " , readns[1]) + seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt2) + ((ind-1)*nrows*typeBytes(fmt2)) ) + readns[ind] = read(matio, fmt2) end + return readns + else + error("reading binTranspose not implemented, lack test data") end + else + error("variable [$name] is located in an unknown location") end - end #open end @@ -478,11 +495,28 @@ using JSON vn = readVariableNames(ac) vd = readVariableDescriptions(ac,vn) di = readDataInfo(ac,vd) + # println(JSON.json(di.info, 2)) #get the data 1/2 info - # readVariable(ac, vn, vd, di, "eff") #data1 - # readVariable(ac, vn, vd, di, "grav") #data1 - readVariable(ac, vn, vd, di, "time") # data0 - # readVariable(ac, vn, vd, di, "height") #data2 + + eff = readVariable(ac, vn, vd, di, "eff") #data1 + @test length(eff) == 2 + @test eff[1] ≈ 0.77 + @test eff[2] ≈ 0.77 + + grav = readVariable(ac, vn, vd, di, "grav") #data1 + @test length(grav) == 2 + @test grav[1] ≈ 9.81 + @test grav[2] ≈ 9.81 + + time = readVariable(ac, vn, vd, di, "time") # data0 + @test all(isapprox.(time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) + + height = readVariable(ac, vn, vd, di, "height") #data2 + @test isapprox(height[1], 111, rtol=1e-3) + @test isapprox(height[2], 110.9509, rtol=1e-3) + + vel = readVariable(ac, vn, vd, di, "vel") #data2 + @test isapprox(vel[2], -0.981, rtol=1e-3) end diff --git a/test/runtests_modelica_readMatrix.jl b/test/runtests_modelica_readMatrix.jl index d0fb41c..742e9e5 100644 --- a/test/runtests_modelica_readMatrix.jl +++ b/test/runtests_modelica_readMatrix.jl @@ -205,3 +205,5 @@ end @test true end + + From e83178416c4c3c71632a4fa9323d8e3582f868d1 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 12:39:17 -0600 Subject: [PATCH 08/91] readMatrixHeader() --- test/runtests_modelica.jl | 134 +++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 53 deletions(-) diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 2db8577..95cbbe0 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -370,6 +370,31 @@ struct MatrixHeader nCols::Int hasImaginary::Bool lName::Int + name::String + format::DataType +end +""" +Reads the matix header, assuming matio's position is correct to read the header +""" +function readMatrixHeader!(matio::IOStream) :: MatrixHeader + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading matrix header") + end + + # data1MatrixName = mark(matio) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + + fmt = dataFormat(dtype) # read the format type before reading + + return MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen,matrixName, fmt) end """ @@ -379,38 +404,40 @@ to read a variable, we need its index, then to look up whether it is in data_1 o function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) display(ac) - - open(ac.filepath, "r", lock=false) do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix println("\ndata_1:") # read data1 header: data1HeaderStart = mark(matio) - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - mh1 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - - data1MatrixName = mark(matio) - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - if matrixName != "data_1" + # dtype = 0 + # nrows = 0 + # ncols = 0 + # namelen = 0 + # includesImaginary = 0 + # try + # dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + # catch e + # error("caught error $e while reading $ac.filepath") + # end + # mh1 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) + mh1 = readMatrixHeader!(matio) + if mh1.name != "data_1" error("trying to read matrix [data_1] but read $matrixName") end + # data1MatrixName = mark(matio) + # nameuint = read!(matio, Vector{UInt8}(undef, mh1.lName)) # read the full namelen to make the pointer ready to read the data + # matrixName = replace(String(nameuint), '\0'=>"") + # if matrixName != "data_1" + # error("trying to read matrix [data_1] but read $matrixName") + # end + #skip dataMatrix1 data1MatrixStart = mark(matio) - fmt1 = dataFormat(dtype) # read the format type before reading + # fmt1 = dataFormat(mh1.type) # read the format type before reading try - toskip = nrows*ncols*typeBytes(fmt1) #817*2*8 = 13072, 488197+20+7+13072 = 501296 + toskip = mh1.nRows*mh1.nCols*typeBytes(mh1.format) #817*2*8 = 13072, 488197+20+7+13072 = 501296 skip(matio, toskip ) catch e error("caught error $e while reading $ac.filepath") @@ -418,25 +445,26 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d # read data2 header: println("\ndata_2:") - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - mh2 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - - #read the matrix name - data2MatrixName = mark(matio) - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - - if matrixName != "data_2" - error("trying to read matrix [data_2] but read $matrixName") + # dtype = 0 + # nrows = 0 + # ncols = 0 + # namelen = 0 + # includesImaginary = 0 + # try + # dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + # catch e + # error("caught error $e while reading $ac.filepath") + # end + # # mh2 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) + + # #read the matrix name + # data2MatrixName = mark(matio) + # nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + # matrixName = replace(String(nameuint), '\0'=>"") + mh2 = readMatrixHeader!(matio) + + if mh2.name != "data_2" + error("trying to read matrix [data_2] but read $(mh2.name)") end data2MatrixStart = mark(matio) @@ -446,36 +474,36 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d if di.info[varInd]["locatedInData"] == 1 #data_1 #read the matrix data_1 - fmt1 = dataFormat(dtype) # read the format type before reading + # fmt1 = dataFormat(dtype) # read the format type before reading # @show di.info[varInd] # dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. if di.info[varInd]["isWithinTimeRange"]== 0 #linear interpolation - seek(matio, data1MatrixStart) - realread = read!(matio, Vector{fmt1}(undef,10)) - display(realread) + # seek(matio, data1MatrixStart) + # realread = read!(matio, Vector{mh1.format}(undef,10)) + # display(realread) #data format is: time(tInitial), var1(tI), ... varN(tI), time(tFinal), var1(tF), ... varN(tF) - # seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt1) + ((ind-1)*nrows*typeBytes(fmt1)) ) - # readns[ind] = read(matio, fmt1) + # seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*nrows*typeBytes(mh1.format)) ) + # readns[ind] = read(matio, mh1.format) - readns = Vector{fmt1}(undef, mh1.nCols) + readns = Vector{mh1.format}(undef, mh1.nCols) for ind = 1:mh1.nCols - seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt1) + ((ind-1)*mh1.nRows*typeBytes(fmt1)) ) - readns[ind] = read(matio, fmt1) + seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*mh1.nRows*typeBytes(mh1.format)) ) + readns[ind] = read(matio, mh1.format) end return readns end elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 #read the matrix data_2 - fmt2 = dataFormat(dtype) # read the format type before reading + # fmt2 = dataFormat(dtype) # read the format type before reading if ac.isTranspose == false # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... - readns = Vector{fmt2}(undef, ncols) - for ind = 1:ncols - seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt2) + ((ind-1)*nrows*typeBytes(fmt2)) ) - readns[ind] = read(matio, fmt2) + readns = Vector{mh2.format}(undef, mh2.nCols) + for ind = 1:mh2.nCols + seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh2.format) + ((ind-1)*mh2.nRows*typeBytes(mh2.format)) ) + readns[ind] = read(matio, mh2.format) end return readns else From 4d6e5684f942409589574a584ee426e2ba2a4ab8 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 13:00:19 -0600 Subject: [PATCH 09/91] moved into MAT_v4_Modelica --- src/MAT_v4_Modelica.jl | 388 +++++++++++++++++++++++++++++- test/runtests_modelica.jl | 491 +++----------------------------------- 2 files changed, 416 insertions(+), 463 deletions(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index f739642..f883311 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -1,18 +1,392 @@ module MAT_v4_Modelica +# The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices +# The first matrix, Aclass is narrowly defined -import Base: read, write, close +function isLittleEndian(dtype) :: Bool + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(dtype; pad=4) + # @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 + return M == 0 +end + + +function dataFormat(type) :: DataType + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + # @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 + if P == 0 + return Float64 + end + if P == 1 + return Float32 + end + if P == 2 + return Int32 + end + if P == 3 + return Int16 + end + if P == 4 + return UInt16 + end + if P == 5 + return UInt8 + end +end -mutable struct ModelicaV4 - ios::IOStream - varnames::Dict{String, Int64} - ModelicaV4(ios) = new(ios) +function typeBytes(type::T)::Int where T<:DataType + if type == Float64 + return 8 + end + if type == Float32 + return 4 + end + if type == Int32 + return 4 + end + if type == Int16 + return 2 + end + if type == UInt16 + return 2 + end + if type == UInt8 + return 1 + end end +struct Aclass + filepath::String + isTranspose::Bool + positionStart::Int + positionEnd::Int +end + +""" +Reads the Aclass matrix, returing if the data is stored binNormal or binTranspose +""" +function readAclass( filepath::String ) + open(filepath, "r", lock=false) do matio + seekstart(matio) # always start from the start, don't assume the position + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # 32bit=4byte * 5 qty + catch e + error("caught error $e while reading $filepath") + end + + if !isLittleEndian(dtype) + error("Only the little-endian encoding is implemented, cannot read $filepath") + end -function read(mat::Matlabv4File) - seekstart(mat.ios) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + name = replace(String(nameuint), '\0'=>"") + if name != "Aclass" + error("First matrix must be named Aclass, is instead [$name]. This likely means that [$filepath] is not a Modelica MAT v4 file.") + end + # real: the Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. + fmt = dataFormat(dtype) # read the format type before reading + realint = read!(matio, Matrix{UInt8}(undef, nrows,ncols)) + Aclass1 = replace(String(realint[1,:]), '\0'=>"") + Aclass2 = replace(String(realint[2,:]), '\0'=>"") + Aclass3 = replace(String(realint[3,:]), '\0'=>"") + Aclass4 = replace(String(realint[4,:]), '\0'=>"") + if Aclass1 == "Atrajectory" && Aclass2 == "1.1" && isempty(Aclass3) && Aclass4 == "binNormal" || Aclass4 == "binTrans" + return Aclass( filepath, Aclass4 == "binTranspose", startP, position(matio) ) + end + end #open end + +##### the NAME matrix######################################################################################################################## +struct VariableNames + # names::Vector{T}(undef,undef) where T<:AbstractString + names::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableNames(ac::Aclass) + open(ac.filepath, "r", lock=false) do matio + seek(matio, ac.positionEnd) #skip over Aclass + startP = position(matio) + + #read the matrix header + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "name" + error("trying to read matrix [name] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realint = [] + try + realint = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + #pull the names out of the matrix + vnames = [] + for i in 1:ncols + push!(vnames, replace(String(realint[:,i]), '\0'=>"")) # note :,1 = implicit transpose + end + + return VariableNames(vnames, startP, position(matio)) + + end #open +end + + +function getVariableIndex(vn::VariableNames, name::String) + vecAll = findall( x->x==name, vn.names) + n = length(vecAll) + + if isempty(vecAll) == true + return -1 + else + if n>1 + error("Found $n instances of variable [$name], but variables should be unique.") + end + return vecAll[1] + end +end + + +struct VariableDescriptions + names::Vector{String} + descriptions::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableDescriptions(ac::Aclass, vn::VariableNames) + open(ac.filepath, "r", lock=false) do matio + seek(matio, vn.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "description" + error("trying to read matrix [description] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + vdesc = [] + for i in 1:ncols + push!(vdesc, replace(String(realread[:,i]), '\0'=>"")) # note :,1 = implicit transpose + end + + return VariableDescriptions(vn.names, vdesc, startP, position(matio)) + end #open +end + + +struct DataInfo + info + positionStart::Int + positionEnd::Int +end + +""" +dataInfo provides indicies to access variable data + +dataInfo +Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). + dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). + dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. + dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. + dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. +""" +function readDataInfo(ac::Aclass, vd::VariableDescriptions) + open(ac.filepath, "r", lock=false) do matio + seek(matio, vd.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "dataInfo" + error("trying to read variable [dataInfo] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + dinfo = [] + for i in 1:ncols + push!(dinfo, Dict("name"=>vd.names[i], "description"=>vd.descriptions[i], "locatedInData"=>realread[1,i], "indexInData"=>realread[2,i], "isInterpolated"=>realread[3,i], "isWithinTimeRange"=>realread[4,i] ) ) + end + return DataInfo( dinfo, startP, position(matio)) + end #open +end + + + +struct MatrixHeader + type::Int + nRows::Int + nCols::Int + hasImaginary::Bool + lName::Int + name::String + format::DataType +end +""" +Reads the matix header, assuming matio's position is correct to read the header +""" +function readMatrixHeader!(matio::IOStream) :: MatrixHeader + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading matrix header") + end + + # data1MatrixName = mark(matio) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + + fmt = dataFormat(dtype) # read the format type before reading + + return MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen,matrixName, fmt) +end + +""" +read one variable from the thing +to read a variable, we need its index, then to look up whether it is in data_1 or data_2 +""" +function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) + display(ac) + + open(ac.filepath, "r", lock=false) do matio + seek(matio, di.positionEnd) #this follows the VariableNames matrix + + # read data1 header: + # data1HeaderStart = mark(matio) + mh1 = readMatrixHeader!(matio) + if mh1.name != "data_1" + error("trying to read matrix [data_1] but read $matrixName") + end + + data1MatrixStart = mark(matio) + try + toskip = mh1.nRows*mh1.nCols*typeBytes(mh1.format) #817*2*8 = 13072, 488197+20+7+13072 = 501296 + skip(matio, toskip ) + catch e + error("caught error $e while reading $ac.filepath") + end + + # read data2 header: + mh2 = readMatrixHeader!(matio) + + if mh2.name != "data_2" + error("trying to read matrix [data_2] but read $(mh2.name)") + end + data2MatrixStart = mark(matio) + + #with the positions marked, read the desired variable + # println("\nlocate variable [$name]:") + varInd = getVariableIndex(vn, name) + + if di.info[varInd]["locatedInData"] == 1 #data_1 + #read the matrix data_1 + # dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. + if di.info[varInd]["isWithinTimeRange"]== 0 #linear interpolation + #data format is: time(tInitial), var1(tI), ... varN(tI), time(tFinal), var1(tF), ... varN(tF) + readns = Vector{mh1.format}(undef, mh1.nCols) + for ind = 1:mh1.nCols + seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*mh1.nRows*typeBytes(mh1.format)) ) + readns[ind] = read(matio, mh1.format) + end + return readns + end + + elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 + #read the matrix data_2 + if ac.isTranspose == false + # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... + readns = Vector{mh2.format}(undef, mh2.nCols) + for ind = 1:mh2.nCols + seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh2.format) + ((ind-1)*mh2.nRows*typeBytes(mh2.format)) ) + readns[ind] = read(matio, mh2.format) + end + return readns + else + error("reading binTranspose not implemented, lack test data") + end + else + error("variable [$name] is located in an unknown location") + end + end #open +end + + + end #module \ No newline at end of file diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 95cbbe0..76d90b5 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -5,7 +5,7 @@ Pkg.activate(joinpath(@__DIR__, "..")) # test only MAT_v4 using Test # include("../src/MAT.jl") -# include("../src/MAT_v4_Modelica.jl") +include("../src/MAT_v4_Modelica.jl") const mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" @@ -14,192 +14,39 @@ const mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer # The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices # The first matrix, Aclass is narrowly defined -function isLittleEndian(dtype) :: Bool - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(dtype; pad=4) - # @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 - return M == 0 -end - @testset "isLittleEndian" begin - @test isLittleEndian(0) == true - @test isLittleEndian(1000) == false - @test isLittleEndian(2000) == false - @test isLittleEndian(3000) == false + @test MAT_v4_Modelica.isLittleEndian(0) == true + @test MAT_v4_Modelica.isLittleEndian(1000) == false + @test MAT_v4_Modelica.isLittleEndian(2000) == false + @test MAT_v4_Modelica.isLittleEndian(3000) == false end -function dataFormat(type) :: DataType - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(type; pad=4) - # @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 - if P == 0 - return Float64 - end - if P == 1 - return Float32 - end - if P == 2 - return Int32 - end - if P == 3 - return Int16 - end - if P == 4 - return UInt16 - end - if P == 5 - return UInt8 - end -end @testset "dataFormat" begin - @test dataFormat(0000) <: Float64 - @test dataFormat(0010) <: Float32 - @test dataFormat(0020) <: Int32 - @test dataFormat(0030) <: Int16 - @test dataFormat(0040) <: UInt16 - @test dataFormat(0050) <: UInt8 + @test MAT_v4_Modelica.dataFormat(0000) <: Float64 + @test MAT_v4_Modelica.dataFormat(0010) <: Float32 + @test MAT_v4_Modelica.dataFormat(0020) <: Int32 + @test MAT_v4_Modelica.dataFormat(0030) <: Int16 + @test MAT_v4_Modelica.dataFormat(0040) <: UInt16 + @test MAT_v4_Modelica.dataFormat(0050) <: UInt8 end -function typeBytes(type::T)::Int where T<:DataType - if type == Float64 - return 8 - end - if type == Float32 - return 4 - end - if type == Int32 - return 4 - end - if type == Int16 - return 2 - end - if type == UInt16 - return 2 - end - if type == UInt8 - return 1 - end -end @testset "typeBytes" begin - @test typeBytes(Int32) == 4 + @test MAT_v4_Modelica.typeBytes(Int32) == 4 end -struct Aclass - filepath::String - isTranspose::Bool - positionStart::Int - positionEnd::Int -end - -""" -Reads the Aclass matrix, returing if the data is stored binNormal or binTranspose -""" -function readAclass( filepath::String ) - open(filepath, "r", lock=false) do matio - seekstart(matio) # always start from the start, don't assume the position - startP = position(matio) - - # The 20-byte header consists of five long (4-byte) integers: - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # 32bit=4byte * 5 qty - catch e - error("caught error $e while reading $filepath") - end - - if !isLittleEndian(dtype) - error("Only the little-endian encoding is implemented, cannot read $filepath") - end - - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - name = replace(String(nameuint), '\0'=>"") - if name != "Aclass" - error("First matrix must be named Aclass, is instead [$name]. This likely means that [$filepath] is not a Modelica MAT v4 file.") - end - - # real: the Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. - fmt = dataFormat(dtype) # read the format type before reading - realint = read!(matio, Matrix{UInt8}(undef, nrows,ncols)) - - Aclass1 = replace(String(realint[1,:]), '\0'=>"") - Aclass2 = replace(String(realint[2,:]), '\0'=>"") - Aclass3 = replace(String(realint[3,:]), '\0'=>"") - Aclass4 = replace(String(realint[4,:]), '\0'=>"") - if Aclass1 == "Atrajectory" && Aclass2 == "1.1" && isempty(Aclass3) && Aclass4 == "binNormal" || Aclass4 == "binTrans" - return Aclass( filepath, Aclass4 == "binTranspose", startP, position(matio) ) - end - end #open -end @testset "Aclass" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) + ac = MAT_v4_Modelica.readAclass(mat1s) @test ac.positionStart == 0 @test ac.positionEnd == 71 end -##### the NAME matrix######################################################################################################################## -struct VariableNames - # names::Vector{T}(undef,undef) where T<:AbstractString - names::Vector{String} - positionStart::Int - positionEnd::Int -end - -function readVariableNames(ac::Aclass) - open(ac.filepath, "r", lock=false) do matio - seek(matio, ac.positionEnd) #skip over Aclass - startP = position(matio) - - #read the matrix header - # The 20-byte header consists of five long (4-byte) integers: - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - - #read the matrix name - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - if matrixName != "name" - error("trying to read matrix [name] but read [$matrixName]") - end - - #read the matrix data - fmt = dataFormat(dtype) # read the format type before reading - realint = [] - try - realint = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - catch e - error("caught error $e while reading $ac.filepath") - end - - #pull the names out of the matrix - vnames = [] - for i in 1:ncols - push!(vnames, replace(String(realint[:,i]), '\0'=>"")) # note :,1 = implicit transpose - end - - return VariableNames(vnames, startP, position(matio)) - - end #open -end - @testset "readVariableNames" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) - vn = readVariableNames(ac) + ac = MAT_v4_Modelica.readAclass(mat1s) + vn = MAT_v4_Modelica.readVariableNames(ac) # @show vn @test length(vn.names) == 2490 @test vn.names[1] == "time" @@ -210,82 +57,21 @@ end @test vn.positionEnd == 117126 end -function getVariableIndex(vn::VariableNames, name::String) - vecAll = findall( x->x==name, vn.names) - n = length(vecAll) - - if isempty(vecAll) == true - return -1 - else - if n>1 - error("Found $n instances of variable [$name], but variables should be unique.") - end - return vecAll[1] - end -end @testset "getVariableIndex" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) - vn = readVariableNames(ac) - @test getVariableIndex(vn, vn.names[3]) == 3 - @test getVariableIndex(vn, vn.names[30]) == 30 + ac = MAT_v4_Modelica.readAclass(mat1s) + vn = MAT_v4_Modelica.readVariableNames(ac) + @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 + @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[30]) == 30 end -struct VariableDescriptions - names::Vector{String} - descriptions::Vector{String} - positionStart::Int - positionEnd::Int -end - -function readVariableDescriptions(ac::Aclass, vn::VariableNames) - open(ac.filepath, "r", lock=false) do matio - seek(matio, vn.positionEnd) #this follows the VariableNames matrix - startP = position(matio) - - # The 20-byte header consists of five long (4-byte) integers: - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - - #read the matrix name - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - if matrixName != "description" - error("trying to read matrix [description] but read [$matrixName]") - end - - #read the matrix data - fmt = dataFormat(dtype) # read the format type before reading - realread = [] - try - realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - catch e - error("caught error $e while reading $ac.filepath") - end - - vdesc = [] - for i in 1:ncols - push!(vdesc, replace(String(realread[:,i]), '\0'=>"")) # note :,1 = implicit transpose - end - - return VariableDescriptions(vn.names, vdesc, startP, position(matio)) - end #open -end @testset "readVariableDescriptions" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) + ac = MAT_v4_Modelica.readAclass(mat1s) + vn = MAT_v4_Modelica.readVariableNames(ac) + vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) @test length(vd.descriptions) == 2490 @test vd.descriptions[1] == "Simulation time [s]" @test vd.descriptions[3] == "First derivative of angle phi (relative angular velocity) [rad/s]" @@ -293,70 +79,14 @@ end @test vd.descriptions[2490] == "Color of cylinders" end -struct DataInfo - info - positionStart::Int - positionEnd::Int -end - -""" -dataInfo provides indicies to access variable data - -dataInfo -Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). - dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). - dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. - dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. - dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. -""" -function readDataInfo(ac::Aclass, vd::VariableDescriptions) - open(ac.filepath, "r", lock=false) do matio - seek(matio, vd.positionEnd) #this follows the VariableNames matrix - startP = position(matio) - - # The 20-byte header consists of five long (4-byte) integers: - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - - #read the matrix name - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - if matrixName != "dataInfo" - error("trying to read variable [dataInfo] but read [$matrixName]") - end - - #read the matrix data - fmt = dataFormat(dtype) # read the format type before reading - realread = [] - try - realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - catch e - error("caught error $e while reading $ac.filepath") - end - - dinfo = [] - for i in 1:ncols - push!(dinfo, Dict("name"=>vd.names[i], "description"=>vd.descriptions[i], "locatedInData"=>realread[1,i], "indexInData"=>realread[2,i], "isInterpolated"=>realread[3,i], "isWithinTimeRange"=>realread[4,i] ) ) - end - return DataInfo( dinfo, startP, position(matio)) - end #open -end @testset "readDataInfo" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) - di = readDataInfo(ac,vd) + ac = MAT_v4_Modelica.readAclass(mat1s) + vn = MAT_v4_Modelica.readVariableNames(ac) + vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT_v4_Modelica.readDataInfo(ac,vd) # @show di.info[3] @test di.info[1]["isWithinTimeRange"] == -1 @test di.info[3]["locatedInData"] == 2 @@ -364,186 +94,35 @@ end @test di.info[2490]["isWithinTimeRange"] == 0 end -struct MatrixHeader - type::Int - nRows::Int - nCols::Int - hasImaginary::Bool - lName::Int - name::String - format::DataType -end -""" -Reads the matix header, assuming matio's position is correct to read the header -""" -function readMatrixHeader!(matio::IOStream) :: MatrixHeader - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading matrix header") - end - - # data1MatrixName = mark(matio) - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - - fmt = dataFormat(dtype) # read the format type before reading - - return MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen,matrixName, fmt) -end - -""" -read one variable from the thing -to read a variable, we need its index, then to look up whether it is in data_1 or data_2 -""" -function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) - display(ac) - - open(ac.filepath, "r", lock=false) do matio - seek(matio, di.positionEnd) #this follows the VariableNames matrix - - println("\ndata_1:") - # read data1 header: - data1HeaderStart = mark(matio) - # dtype = 0 - # nrows = 0 - # ncols = 0 - # namelen = 0 - # includesImaginary = 0 - # try - # dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - # catch e - # error("caught error $e while reading $ac.filepath") - # end - # mh1 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - mh1 = readMatrixHeader!(matio) - if mh1.name != "data_1" - error("trying to read matrix [data_1] but read $matrixName") - end - - # data1MatrixName = mark(matio) - # nameuint = read!(matio, Vector{UInt8}(undef, mh1.lName)) # read the full namelen to make the pointer ready to read the data - # matrixName = replace(String(nameuint), '\0'=>"") - # if matrixName != "data_1" - # error("trying to read matrix [data_1] but read $matrixName") - # end - - #skip dataMatrix1 - data1MatrixStart = mark(matio) - # fmt1 = dataFormat(mh1.type) # read the format type before reading - try - toskip = mh1.nRows*mh1.nCols*typeBytes(mh1.format) #817*2*8 = 13072, 488197+20+7+13072 = 501296 - skip(matio, toskip ) - catch e - error("caught error $e while reading $ac.filepath") - end - - # read data2 header: - println("\ndata_2:") - # dtype = 0 - # nrows = 0 - # ncols = 0 - # namelen = 0 - # includesImaginary = 0 - # try - # dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - # catch e - # error("caught error $e while reading $ac.filepath") - # end - # # mh2 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - - # #read the matrix name - # data2MatrixName = mark(matio) - # nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - # matrixName = replace(String(nameuint), '\0'=>"") - mh2 = readMatrixHeader!(matio) - - if mh2.name != "data_2" - error("trying to read matrix [data_2] but read $(mh2.name)") - end - data2MatrixStart = mark(matio) - - #with the positions marked, read the desired variable - # println("\nlocate variable [$name]:") - varInd = getVariableIndex(vn, name) - - if di.info[varInd]["locatedInData"] == 1 #data_1 - #read the matrix data_1 - # fmt1 = dataFormat(dtype) # read the format type before reading - # @show di.info[varInd] - - # dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. - if di.info[varInd]["isWithinTimeRange"]== 0 #linear interpolation - # seek(matio, data1MatrixStart) - # realread = read!(matio, Vector{mh1.format}(undef,10)) - # display(realread) - #data format is: time(tInitial), var1(tI), ... varN(tI), time(tFinal), var1(tF), ... varN(tF) - # seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*nrows*typeBytes(mh1.format)) ) - # readns[ind] = read(matio, mh1.format) - - readns = Vector{mh1.format}(undef, mh1.nCols) - for ind = 1:mh1.nCols - seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*mh1.nRows*typeBytes(mh1.format)) ) - readns[ind] = read(matio, mh1.format) - end - return readns - end - - elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 - #read the matrix data_2 - # fmt2 = dataFormat(dtype) # read the format type before reading - - if ac.isTranspose == false - # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... - readns = Vector{mh2.format}(undef, mh2.nCols) - for ind = 1:mh2.nCols - seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh2.format) + ((ind-1)*mh2.nRows*typeBytes(mh2.format)) ) - readns[ind] = read(matio, mh2.format) - end - return readns - else - error("reading binTranspose not implemented, lack test data") - end - else - error("variable [$name] is located in an unknown location") - end - end #open -end - using JSON @testset "readVariable" begin # mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" matbb = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = readAclass(matbb) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) - di = readDataInfo(ac,vd) + ac = MAT_v4_Modelica.readAclass(matbb) + vn = MAT_v4_Modelica.readVariableNames(ac) + vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT_v4_Modelica.readDataInfo(ac,vd) # println(JSON.json(di.info, 2)) #get the data 1/2 info - eff = readVariable(ac, vn, vd, di, "eff") #data1 + eff = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 @test length(eff) == 2 @test eff[1] ≈ 0.77 @test eff[2] ≈ 0.77 - grav = readVariable(ac, vn, vd, di, "grav") #data1 + grav = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 @test length(grav) == 2 @test grav[1] ≈ 9.81 @test grav[2] ≈ 9.81 - time = readVariable(ac, vn, vd, di, "time") # data0 + time = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # data0 @test all(isapprox.(time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) - height = readVariable(ac, vn, vd, di, "height") #data2 + height = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 @test isapprox(height[1], 111, rtol=1e-3) @test isapprox(height[2], 110.9509, rtol=1e-3) - vel = readVariable(ac, vn, vd, di, "vel") #data2 + vel = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 @test isapprox(vel[2], -0.981, rtol=1e-3) end From 082d19c52b558ebc353276314d5df786e520442b Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 16:10:02 -0600 Subject: [PATCH 10/91] initially useable --- src/MAT_v4_Modelica.jl | 5 +- .../Modelica/FallingBodyBox/FallingBodyBox.mo | 2 +- .../FallingBodyBox/FallingBodyBox_res.csv | 103 ++++++++++++++++++ .../FallingBodyBox/FallingBodyBox_res.mat | Bin 0 -> 776747 bytes test/runtests_modelica.jl | 86 ++++++++++----- 5 files changed, 163 insertions(+), 33 deletions(-) create mode 100644 test/Modelica/FallingBodyBox/FallingBodyBox_res.csv create mode 100644 test/Modelica/FallingBodyBox/FallingBodyBox_res.mat diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index f883311..0e40e35 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -323,8 +323,6 @@ read one variable from the thing to read a variable, we need its index, then to look up whether it is in data_1 or data_2 """ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) - display(ac) - open(ac.filepath, "r", lock=false) do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix @@ -354,6 +352,9 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d #with the positions marked, read the desired variable # println("\nlocate variable [$name]:") varInd = getVariableIndex(vn, name) + if varInd < 1 + throw( ErrorException("Variable [$name] not found in file [$(ac.filepath)]") ) + end if di.info[varInd]["locatedInData"] == 1 #data_1 #read the matrix data_1 diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox.mo b/test/Modelica/FallingBodyBox/FallingBodyBox.mo index 6e74314..3340533 100644 --- a/test/Modelica/FallingBodyBox/FallingBodyBox.mo +++ b/test/Modelica/FallingBodyBox/FallingBodyBox.mo @@ -1,7 +1,7 @@ model FallingBodyBox inner Modelica.Mechanics.MultiBody.World world annotation( Placement(visible = true, transformation(origin = {-70, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); - Modelica.Mechanics.MultiBody.Parts.BodyBox bodyBox(r = {1, 0, 0}) annotation( + Modelica.Mechanics.MultiBody.Parts.BodyBox bodyBox(r = {1, 0, 0}, w_0_fixed = true, w_0_start = {1, 2, 3}) annotation( Placement(visible = true, transformation(origin = {10, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Mechanics.MultiBody.Joints.FreeMotion freeMotion annotation( Placement(visible = true, transformation(origin = {-30, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox_res.csv b/test/Modelica/FallingBodyBox/FallingBodyBox_res.csv new file mode 100644 index 0000000..0bf3c2a --- /dev/null +++ b/test/Modelica/FallingBodyBox/FallingBodyBox_res.csv @@ -0,0 +1,103 @@ +"time","bodyBox.r[1]","bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]","bodyBox.v_0[2]","bodyBox.frame_b.r_0[1]" +0,1,0,1,0,1 +0.002,1,1.417825012934035e-05,0.9999740006763724,-0.0195807445407832,0.9999881789265018 +0.004,1,5.313923834174793e-05,0.9998960030304396,-0.03908714168556562,0.9999491422687813 +0.006,1,0.0001181362617060314,0.9997660114590055,-0.05851542917791717,0.9998841477207114 +0.008,1,0.0002091234527678357,0.9995840332464712,-0.0778657434873069,0.999793156699239 +0.01,1,0.000326099054897985,0.9993500776948991,-0.09713808926719215,0.9996761767497971 +0.012,1,0.0004690560147366672,0.9990641573526586,-0.1163324869886311,0.9995332133673953 +0.014,1,0.0006379874421543658,0.9987262866361137,-0.1354489568144222,0.9993642740782681 +0.016,1,0.000832885617011653,0.9983364820850886,-0.1544875215725002,0.9991693677021003 +0.018,1,0.001053739093578769,0.9978947644247245,-0.1734482153767369,0.9989485035183032 +0.02,1,0.001300538971511691,0.9974011552321307,-0.1923310647500386,0.9987016942036424 +0.022,1,0.00157327094282644,0.9968556808437448,-0.2111361125177071,0.9984289517865712 +0.024,1,0.001871924983280909,0.9962583689451037,-0.2298633885555008,0.9981302939283846 +0.026,1,0.002196481817791961,0.9956092513011041,-0.2485129501878651,0.9978057331188961 +0.028,1,0.002546927054133541,0.9949083620349535,-0.2670848402930672,0.997455289089087 +0.03,1,0.002923239445405512,0.994155737557995,-0.2855791220502004,0.9970789770034005 +0.032,1,0.003325388624709284,0.9933514166140098,-0.3039958856428557,0.9966768052387192 +0.034,1,0.003753358061751959,0.9924954413939879,-0.3223351802805359,0.9962487994557399 +0.036,1,0.004207126617115786,0.9915878565784442,-0.340597068821661,0.9957949831955599 +0.038,1,0.004686673151383015,0.9906287095709687,-0.3587816141246513,0.9953153827223518 +0.04,1,0.005191967386675178,0.9896180499664131,-0.3768889067903736,0.9948100173530883 +0.042,1,0.005722986459759737,0.9885559304269864,-0.3949190149103882,0.9942789168867462 +0.044,1,0.006279709994901068,0.9874424064724534,-0.4128719990247363,0.9937221164673544 +0.046,1,0.006862115072954707,0.9862775361787828,-0.4307479273947147,0.9931396512517375 +0.048,1,0.007470178774776187,0.9850613803166175,-0.4485468682816198,0.9925315590913937 +0.05,1,0.008103821130588527,0.9837940009841819,-0.4662690606761235,0.9918978221147704 +0.052,1,0.00876304148364556,0.9824754647342051,-0.4839145062172048,0.9912385062178507 +0.054,1,0.009447812213827225,0.9811058401953279,-0.5014832872342715,0.990553652409155 +0.056,1,0.01015810452490512,0.97968519863496,-0.5189754895763452,0.9898433031598651 +0.058,1,0.01089387776305806,0.9782136138791815,-0.5363912346772112,0.9891074916422395 +0.06,1,0.01165505381920325,0.9766911620635708,-0.553730756374293,0.988346215882774 +0.062,1,0.01244162634538108,0.975117922605218,-0.5709940731491192,0.9875595489505991 +0.064,1,0.0132535600626235,0.9734939773231635,-0.5881812903054828,0.9867475373857869 +0.066,1,0.01409081969196248,0.9718194106847828,-0.6052925131471765,0.9859102303767453 +0.068,1,0.01495334316027893,0.9700943096511918,-0.6223279274395663,0.9850476528114708 +0.07,1,0.01584106743243119,0.9683187638112045,-0.6392877218369272,0.9841598312436357 +0.072,1,0.01675397185431944,0.966492865632629,-0.6561719577252614,0.9832468374869485 +0.074,1,0.01769201473536401,0.9646167101017287,-0.67298075966213,0.9823087248370926 +0.076,1,0.01865515438498519,0.9626903948376431,-0.6897142522050934,0.9813455492226283 +0.078,1,0.01964331707618472,0.9607140197251685,-0.7063726561112313,0.9803573368013532 +0.08,1,0.02065645250112456,0.9586876875208576,-0.7229561218141588,0.9793441400219822 +0.082,1,0.02169451650984585,0.9566115036712002,-0.7394647812565036,0.9783060201810461 +0.084,1,0.02275745575203531,0.9544855761318363,-0.7558987940078782,0.9772430318838716 +0.086,1,0.02384521563088566,0.952310015452082,-0.7722583233808898,0.9761552310829678 +0.088,1,0.02495774030309547,0.9500849347706286,-0.7885435364311402,0.9750426750737241 +0.09,1,0.02609497267886922,0.9478104498111921,-0.8047546039572244,0.9739054224900613 +0.092,1,0.02725685442191735,0.9454866788781227,-0.820891700500733,0.97274353330004 +0.094,1,0.02844332594945613,0.94311374285196,-0.8369550043462501,0.9715570688014161 +0.096,1,0.02965432300661144,0.9406917650407978,-0.8529447077563223,0.9703460880474092 +0.098,1,0.03088977204827575,0.9382208709649862,-0.8688610307695054,0.9691106430132619 +0.1,1,0.03214961678515921,0.9357011894795664,-0.884704141886301,0.9678508062647256 +0.102,1,0.03343379109670443,0.9331328516383901,-0.9004742389997172,0.9665666427350945 +0.104,1,0.0347422274909309,0.9305159910386045,-0.9161715241190189,0.9652582185295354 +0.106,1,0.03607485710443521,0.9278507438155497,-0.9317962033697258,0.9639256009199849 +0.108,1,0.03743160970239083,0.925137248637621,-0.9473484869936142,0.9625688583400118 +0.11,1,0.03881241367854828,0.9223756467010917,-0.9628285893487162,0.96118806037964 +0.112,1,0.04021719605523505,0.919566081724903,-0.9782367289093201,0.9597832777801381 +0.114,1,0.04164588248335553,0.9167086999454117,-0.9935731282659692,0.9583545824287673 +0.116,1,0.0430984131729114,0.9138036485978986,-1.008837966181234,0.9569020617708101 +0.118,1,0.04457470783647349,0.910851079164642,-1.024031483095255,0.9554257870011156 +0.12,1,0.04607468513507586,0.9078511456196503,-1.039153922601708,0.9539258307547261 +0.122,1,0.04759826680889059,0.904804004006921,-1.054205519016186,0.9524022708158115 +0.124,1,0.04914537332958941,0.9017098128396142,-1.069186510460787,0.9508551861692036 +0.126,1,0.0507159239003439,0.8985687330936636,-1.084097138864106,0.9492846569940074 +0.128,1,0.05230983645582534,0.8953809282013514,-1.098937649961244,0.9476907646571767 +0.13,1,0.05392702766220477,0.8921465640448423,-1.113708293293798,0.9460735917070471 +0.132,1,0.055567412917153,0.8888658089496781,-1.128409322209871,0.9444332218668312 +0.134,1,0.05723090846134186,0.8855388331121092,-1.143040987506299,0.9427697415734511 +0.136,1,0.05891743315753914,0.882165808066411,-1.157603532071766,0.9410832412239502 +0.138,1,0.06062689417026532,0.8787469105544052,-1.172097234005607,0.9393738047246706 +0.14,1,0.06235920240475686,0.8752823184025715,-1.186522360130819,0.9376415208073284 +0.142,1,0.0641142675341053,0.8717722118281508,-1.200879180967436,0.9358864793622561 +0.144,1,0.06589199799925725,0.8682167734316997,-1.215167970732527,0.9341087714309569 +0.146,1,0.06769230100901449,0.8646161881896115,-1.229389007340194,0.932308489198626 +0.148,1,0.0695150825400338,0.8609706434466029,-1.243542572401574,0.9304857259866367 +0.15,1,0.07136024733682711,0.8572803289081588,-1.257628951224839,0.9286405762449859 +0.152,1,0.07322769891176151,0.8535454366329454,-1.271648432815197,0.9267731355447069 +0.154,1,0.07511734742166784,0.849766158927348,-1.285601286354159,0.9248835063490158 +0.156,1,0.07702909193293221,0.8459426929871825,-1.299487814158628,0.9229717849201147 +0.158,1,0.07896283092029514,0.8420752381907177,-1.313308320329912,0.9210380691110128 +0.16,1,0.08091846365462857,0.8381639956960949,-1.327063106609619,0.9190824593507234 +0.162,1,0.0828958882162892,0.8342091689551816,-1.340752478312082,0.9171050571714708 +0.164,1,0.08489500149511885,0.8302109637050734,-1.354376744324351,0.9151059652001923 +0.166,1,0.08691569919044412,0.8261695879595679,-1.367936217106201,0.9130852871500119 +0.168,1,0.08895787581107682,0.8220852520006039,-1.381431212690126,0.9110431278116807 +0.17,1,0.09102142467531353,0.8179581683696659,-1.394862050681345,0.9089795930449794 +0.172,1,0.0931062404511159,0.813788551136279,-1.408229046683594,0.9068947915873949 +0.174,1,0.09521222061667139,0.8095766154277348,-1.421532507944097,0.9047888360444063 +0.176,1,0.09733925001502079,0.8053225818512313,-1.43477277940791,0.902661831866252 +0.178,1,0.09948721786057077,0.8010266716656287,-1.447950193008633,0.9005138895261995 +0.18,1,0.1016560122183165,0.7966891083251946,-1.461065084130027,0.8983451205435111 +0.182,1,0.1038455200038418,0.7923101174701734,-1.474117791606015,0.8961556374740152 +0.184,1,0.1060556269833189,0.7878899269173223,-1.487108657720677,0.8939455539006412 +0.186,1,0.1082862177735087,0.7834287666504196,-1.500038028208257,0.8917149844239283 +0.188,1,0.1105371758417606,0.7789268688107436,-1.512906252253159,0.8894640446525042 +0.19,1,0.1128083835060125,0.774384467687522,-1.525713682489948,0.8871928511935345 +0.192,1,0.1150997299043263,0.7698017976709137,-1.538460651046364,0.88490152757524 +0.194,1,0.1174110933557253,0.7651790976937483,-1.551147522567888,0.8825901910494736 +0.196,1,0.1197423519115858,0.7605166085958401,-1.563774662499517,0.880258960507426 +0.198,1,0.122093384374056,0.7558145728344028,-1.576342434024323,0.8779079572084588 +0.2,1,0.1244640685114575,0.7510732349244313,-1.588851203428083,0.8755373034358889 +0.2,1,0.1244640685114575,0.7510732349244313,-1.588851203428083,0.8755373034358889 diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat b/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat new file mode 100644 index 0000000000000000000000000000000000000000..3cc5fa57da47eb6dc4bd07d71df6cbbe5ea9a676 GIT binary patch literal 776747 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 literal 0 HcmV?d00001 diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 76d90b5..dd37e2f 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -4,10 +4,10 @@ Pkg.activate(joinpath(@__DIR__, "..")) # test only MAT_v4 using Test +using JSON # include("../src/MAT.jl") include("../src/MAT_v4_Modelica.jl") -const mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" @@ -36,69 +36,63 @@ end @testset "Aclass" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) @test ac.positionStart == 0 @test ac.positionEnd == 71 end @testset "readVariableNames" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) # @show vn - @test length(vn.names) == 2490 + @test length(vn.names) == 11 @test vn.names[1] == "time" - @test vn.names[3] == "revolute.w" - @test vn.names[30] == "der(alignElastoBacklash.frame_a.r_0[1])" - @test vn.names[2490] == "world.z_label.color[3]" + @test vn.names[3] == "vel" + @test vn.names[11] == "grav" @test vn.positionStart == 71 - @test vn.positionEnd == 117126 + @test vn.positionEnd == 228 end @testset "getVariableIndex" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 - @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[30]) == 30 + @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[10]) == 10 end @testset "readVariableDescriptions" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) - @test length(vd.descriptions) == 2490 + @test length(vd.descriptions) == 11 @test vd.descriptions[1] == "Simulation time [s]" - @test vd.descriptions[3] == "First derivative of angle phi (relative angular velocity) [rad/s]" - @test vd.descriptions[30] == "Position vector from world frame to the connector frame origin, resolved in world frame" - @test vd.descriptions[2490] == "Color of cylinders" + @test vd.descriptions[3] == "velocity of ball" + @test vd.descriptions[11] == "gravity acceleration" end - - @testset "readDataInfo" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT_v4_Modelica.readDataInfo(ac,vd) # @show di.info[3] @test di.info[1]["isWithinTimeRange"] == -1 @test di.info[3]["locatedInData"] == 2 - @test di.info[30]["isInterpolated"] == 0 - @test di.info[2490]["isWithinTimeRange"] == 0 + @test di.info[4]["isInterpolated"] == 0 + @test di.info[11]["isWithinTimeRange"] == 0 end -using JSON -@testset "readVariable" begin - # mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - matbb = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(matbb) +@testset "readVariable: BouncingBall" begin + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT_v4_Modelica.readDataInfo(ac,vd) @@ -126,6 +120,38 @@ using JSON @test isapprox(vel[2], -0.981, rtol=1e-3) end +@testset "readVariable: FallingBodyBox" begin + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) + vn = MAT_v4_Modelica.readVariableNames(ac) + vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT_v4_Modelica.readDataInfo(ac,vd) + + # println(JSON.json(di.info, 2)) + + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") + # display(var) + ret = true + for i = 2:length(var)-1 #last time is duplicated + ret &= isapprox(var[i]-var[i-1], 0.002, rtol=1e-4) + end + @test ret == true + + #point-check values read from FallingBodyBox_res.csv + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") + @test isapprox(var[16], 0.002923239, rtol=1e-3) + + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1,1]") + @test isapprox(var[26], 0.983794001, rtol=1e-3) + + @test_throws ErrorException MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") + @test isapprox(var[33], -0.58818129, rtol=1e-3) + + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") + @test isapprox(var[72], 0.935886479, rtol=1e-3) +end ; From ed30247faf4abd8a7150c5dc739e4ce7fa908f34 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Mon, 27 Feb 2023 17:09:24 -0600 Subject: [PATCH 11/91] move test files --- .../BouncingBall/BouncingBall.mo | 0 .../BouncingBall/BouncingBall_dymola2021.mat | Bin 0 -> 1426 bytes .../BouncingBall/BouncingBall_res.csv | 0 .../BouncingBall/BouncingBall_res.mat | Bin .../FallingBodyBox/FallingBodyBox.mo | 0 .../FallingBodyBox/FallingBodyBox_res.csv | 0 .../FallingBodyBox/FallingBodyBox_res.mat | Bin 7 files changed, 0 insertions(+), 0 deletions(-) rename test/{Modelica => v4_Modelica}/BouncingBall/BouncingBall.mo (100%) create mode 100644 test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat rename test/{Modelica => v4_Modelica}/BouncingBall/BouncingBall_res.csv (100%) rename test/{Modelica => v4_Modelica}/BouncingBall/BouncingBall_res.mat (100%) rename test/{Modelica => v4_Modelica}/FallingBodyBox/FallingBodyBox.mo (100%) rename test/{Modelica => v4_Modelica}/FallingBodyBox/FallingBodyBox_res.csv (100%) rename test/{Modelica => v4_Modelica}/FallingBodyBox/FallingBodyBox_res.mat (100%) diff --git a/test/Modelica/BouncingBall/BouncingBall.mo b/test/v4_Modelica/BouncingBall/BouncingBall.mo similarity index 100% rename from test/Modelica/BouncingBall/BouncingBall.mo rename to test/v4_Modelica/BouncingBall/BouncingBall.mo diff --git a/test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat b/test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat new file mode 100644 index 0000000000000000000000000000000000000000..dca4f2e2722d74d4e2fdd678f340777238096b2d GIT binary patch literal 1426 zcmbVLO=}cE5N*FdbMqo3(&!~CqONlbX2%`z5XdnnLkOKo&!%OzXPKGovUu?a&;A$%cg^9L2cKYdiARMV;#oWY4S_tuyf>fKNLyA z>Q%my-exMUaxEBNc*1!sITt?XI^{fxIA<~Eyi4v-86=F%8H%-NO17YyRFjzuf+FIL zShS0HO{&Hk$&szfcshrbwhW7W_LX3Ta1eHtZe$77YzaRvYcFfr$=N|fL*<5vZjMZJ znc`KM__1oGD$@C2O7q2J$&?(k`Vmd8d?jhuQ9x&qq$*v9$an#^rP3}PkuO6T3mYe| zDRxbAD_qm<*e^1~Cku=P z_|ynS&g8*IZ1M)98O&fjc$mRwb68^huV5yBK8Gd7z%b-5(u#g&2Gn9R%T)>(+q(A1 z-94PFGxp3iF-jnTR}L#n?&X+$^@@N|=?+Q_ruD(zU@-gb`+@iF`qQ^?J7>PO@dBJ7 zafVjTX0KQKId$MoJ-*+E+jR!DJNgTH_2D)S&E!@#_PyQ1mz0Meoq4l&)&0@=Oue?L z*S3}8IF1`tKY5)u`+c}gXKyg?zTWzXebGEaD<=p7cQEn6d;V}@2X52BnZCOFo@SoR I^8d^E4JY?na{vGU literal 0 HcmV?d00001 diff --git a/test/Modelica/BouncingBall/BouncingBall_res.csv b/test/v4_Modelica/BouncingBall/BouncingBall_res.csv similarity index 100% rename from test/Modelica/BouncingBall/BouncingBall_res.csv rename to test/v4_Modelica/BouncingBall/BouncingBall_res.csv diff --git a/test/Modelica/BouncingBall/BouncingBall_res.mat b/test/v4_Modelica/BouncingBall/BouncingBall_res.mat similarity index 100% rename from test/Modelica/BouncingBall/BouncingBall_res.mat rename to test/v4_Modelica/BouncingBall/BouncingBall_res.mat diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox.mo b/test/v4_Modelica/FallingBodyBox/FallingBodyBox.mo similarity index 100% rename from test/Modelica/FallingBodyBox/FallingBodyBox.mo rename to test/v4_Modelica/FallingBodyBox/FallingBodyBox.mo diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox_res.csv b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.csv similarity index 100% rename from test/Modelica/FallingBodyBox/FallingBodyBox_res.csv rename to test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.csv diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.mat similarity index 100% rename from test/Modelica/FallingBodyBox/FallingBodyBox_res.mat rename to test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.mat From 38cdd9ccfd3b7542fa7546d032fd466a0a8821f4 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 08:20:19 -0600 Subject: [PATCH 12/91] basic bones for reading OpenModelica MAT result files and tests --- src/MAT.jl | 3 +- src/MAT_v4_Modelica.jl | 69 +++- src/MAT_v4_pr132.jl | 322 ------------------ src/MAT_v4_pr164.jl | 59 ---- src/Matlab_MATFileFormatV4.pdf | Bin 23557 -> 0 bytes ...aUserGuide_v1.21.0-dev-211-gcd8969a225.pdf | Bin 85780 -> 0 bytes test/runtests.jl | 2 +- test/runtests_modelica.jl | 121 ++++--- test/runtests_pr132.jl | 37 -- test/runtests_pr164.jl | 48 --- test/v4/testcomplex_4.2c_SOL2.mat | Bin 176 -> 0 bytes test/v4/testdouble_4.2c_SOL2.mat | Bin 103 -> 0 bytes test/v4/testmatrix_4.2c_SOL2.mat | Bin 151 -> 0 bytes test/v4/testminus_4.2c_SOL2.mat | Bin 38 -> 0 bytes test/v4/testmulti_4.2c_SOL2.mat | Bin 240 -> 0 bytes test/v4/testonechar_4.2c_SOL2.mat | Bin 40 -> 0 bytes test/v4/testsparse_4.2c_SOL2.mat | Bin 223 -> 0 bytes test/v4/testsparsecomplex_4.2c_SOL2.mat | Bin 294 -> 0 bytes test/v4/teststring_4.2c_SOL2.mat | Bin 375 -> 0 bytes test/v4/teststringarray_4.2c_SOL2.mat | Bin 156 -> 0 bytes test/v4/testvec_4_GLNX86.mat | Bin 93 -> 0 bytes 21 files changed, 119 insertions(+), 542 deletions(-) delete mode 100644 src/MAT_v4_pr132.jl delete mode 100644 src/MAT_v4_pr164.jl delete mode 100644 src/Matlab_MATFileFormatV4.pdf delete mode 100644 src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf delete mode 100644 test/runtests_pr132.jl delete mode 100644 test/runtests_pr164.jl delete mode 100644 test/v4/testcomplex_4.2c_SOL2.mat delete mode 100644 test/v4/testdouble_4.2c_SOL2.mat delete mode 100644 test/v4/testmatrix_4.2c_SOL2.mat delete mode 100644 test/v4/testminus_4.2c_SOL2.mat delete mode 100644 test/v4/testmulti_4.2c_SOL2.mat delete mode 100644 test/v4/testonechar_4.2c_SOL2.mat delete mode 100644 test/v4/testsparse_4.2c_SOL2.mat delete mode 100644 test/v4/testsparsecomplex_4.2c_SOL2.mat delete mode 100644 test/v4/teststring_4.2c_SOL2.mat delete mode 100644 test/v4/teststringarray_4.2c_SOL2.mat delete mode 100644 test/v4/testvec_4_GLNX86.mat diff --git a/src/MAT.jl b/src/MAT.jl index 4c25d7a..2349282 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -28,8 +28,9 @@ using HDF5, SparseArrays include("MAT_HDF5.jl") include("MAT_v5.jl") +include("MAT_v4_Modelica.jl") -using .MAT_HDF5, .MAT_v5 +using .MAT_HDF5, .MAT_v5, .MAT_v4_Modelica export matopen, matread, matwrite, @read, @write diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index 0e40e35..f62a1f5 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -1,6 +1,52 @@ +# A module to read MAT files written by OpenModelica tools + +# Copyright (C) 2023 Ben Conrad +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Given the often large size of mat files, each read option is atomic, opening and closing the file for every operation with state managed by the user. + +# The OpenModelica MATv4 file takes the basic v4 matrix format and adds some requirments on the contents and ordering of the matrices +# The format is described at https://openmodelica.org/doc/OpenModelicaUsersGuide/latest/technical_details.html#the-matv4-result-file-format, consisting of a series of matrices that first describe the data then store it. +#- Aclass: +# Aclass(1,:) is always Atrajectory +# Aclass(2,:) is 1.1 in OpenModelica +# Aclass(3,:) is empty +# Aclass(4,:) is either binTrans or binNormal, which determines if the data is stored striped (rows of values at a time instance) or tansposed (rows being a single variable across time) +#- name: +# a NxM matrix giving the names of the N variables as int8 characters +#- description: +# a NxM matrix giving the descriptions of the N variables as int8 characters +#- dataInfo: +# a Nx4 matrix describing the data of each variable, with +# dataInfo(i,1) locating the data in data_1 or data_2 +# dataInfo(i,2) providing the start index within the data_ matrix +# dataInfo(i,3) = 0 to indicate that the variable is interpolated +# dataInfo(i,4) = -1 to indicate that the variable is undefined outside the time range +#- data_1: +# is either an Nx1 matrix giving the variable's constant value, or Nx2 giving the start and end values +#- data_2: +# holds the values of the continuously-varying variables in rows of [time1, var1(@time1), var2(@time1), ...varN(@time1), time2, var1(@time2)...] + + module MAT_v4_Modelica -# The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices -# The first matrix, Aclass is narrowly defined function isLittleEndian(dtype) :: Bool #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... @@ -9,7 +55,6 @@ function isLittleEndian(dtype) :: Bool return M == 0 end - function dataFormat(type) :: DataType #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... T, P, O, M = digits(type; pad=4) @@ -55,6 +100,7 @@ function typeBytes(type::T)::Int where T<:DataType end end + struct Aclass filepath::String isTranspose::Bool @@ -106,8 +152,6 @@ function readAclass( filepath::String ) end #open end - -##### the NAME matrix######################################################################################################################## struct VariableNames # names::Vector{T}(undef,undef) where T<:AbstractString names::Vector{String} @@ -283,8 +327,6 @@ function readDataInfo(ac::Aclass, vd::VariableDescriptions) end #open end - - struct MatrixHeader type::Int nRows::Int @@ -294,6 +336,7 @@ struct MatrixHeader name::String format::DataType end + """ Reads the matix header, assuming matio's position is correct to read the header """ @@ -319,15 +362,13 @@ function readMatrixHeader!(matio::IOStream) :: MatrixHeader end """ -read one variable from the thing -to read a variable, we need its index, then to look up whether it is in data_1 or data_2 +read one variable from the file """ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) open(ac.filepath, "r", lock=false) do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix # read data1 header: - # data1HeaderStart = mark(matio) mh1 = readMatrixHeader!(matio) if mh1.name != "data_1" error("trying to read matrix [data_1] but read $matrixName") @@ -388,6 +429,12 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d end #open end +""" +all-in-one +""" +function readVariable(filepath::String, name::String) :: DataFrame + +end -end #module \ No newline at end of file +end #MAT_v4_Modelica \ No newline at end of file diff --git a/src/MAT_v4_pr132.jl b/src/MAT_v4_pr132.jl deleted file mode 100644 index 1952779..0000000 --- a/src/MAT_v4_pr132.jl +++ /dev/null @@ -1,322 +0,0 @@ -# Copied from pr132 of https://github.com/JuliaIO/MAT.jl/pull/132 - -# MAT_v4.jl -# Tools for reading MATLAB v4 files in Julia -# -# Copyright (C) 2012 Simon Kornblith -# Copyright (C) 2019 Victor Saase (modified from MAT_v5.jl) -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -# MATLAB's file format documentation can be found at -# http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf - -module MAT_v4_pr132 -using BufferedStreams, HDF5, SparseArrays -import Base: read, write, close -import HDF5: names - -round_uint8(data) = round.(UInt8, data) -complex_array(a, b) = complex.(a, b) - -mutable struct Matlabv4File <: HDF5.H5DataStore - ios::IOStream - swap_bytes::Bool - varnames::Dict{String, Int64} - - Matlabv4File(ios, swap_bytes) = new(ios, swap_bytes) -end - -const mLITTLE_ENDIAN = 0 -const mBIG_ENDIAN = 1 -const mVAX_DFLOAT = 2 -const mVAX_GFLOAT = 3 -const mGRAY = 4 - -const pTYPE = Dict( - 0 => Float64, - 1 => Float32, - 2 => Int32, - 3 => Int16, - 4 => UInt16, - 5 => UInt8 -) - -const tNUMERIC = 0 -const tTEXT = 1 -const tSPARSE = 2 - -const imagfREAL = 0 -const imagfCOMPLEX = 1 - -read_bswap(f::IO, swap_bytes::Bool, ::Type{T}) where T = swap_bytes ? bswap(read(f, T)) : read(f, T) - -function read_bswap(f::IO, swap_bytes::Bool, ::Type{T}, dim::Union{Int, Tuple{Vararg{Int}}}) where T - d = read!(f, Array{T}(undef, dim)) - if swap_bytes - for i = 1:length(d) - @inbounds d[i] = bswap(d[i]) - end - end - d -end - -function read_bswap(f::IO, swap_bytes::Bool, d::AbstractArray{T}) where T - readbytes!(f, reinterpret(UInt8, d)) - if swap_bytes - for i = 1:length(d) - @inbounds d[i] = bswap(d[i]) - end - end - d -end - -function checkv4(f::IO) - M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, false) - if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 - swap_bytes = false - return (true, swap_bytes) - else - seek(f, 0) - M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, true) - if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 - swap_bytes = true - return (true, swap_bytes) - end - end - return (false, false) -end - -# Read data type and number of bytes at the start of a data element -function read_header(f::IO, swap_bytes::Bool) - dtype = read_bswap(f, swap_bytes, Int32) - - M = div(rem(dtype, 10000), 1000) - O = div(rem(dtype, 1000), 100) - P = div(rem(dtype, 100), 10) - T = div(rem(dtype, 10), 1) - - mrows = read_bswap(f, swap_bytes, Int32) - ncols = read_bswap(f, swap_bytes, Int32) - imagf = read_bswap(f, swap_bytes, Int32) - namlen = read_bswap(f, swap_bytes, Int32) - - M, O, P, T, mrows, ncols, imagf, namlen -end - -# Read matrix data -function read_matrix(f::IO, swap_bytes::Bool) - M, O, P, T, mrows, ncols, imagf, namlen = read_header(f, swap_bytes) - if ncols == 0 || mrows == 0 - # If one creates a cell array using - # y = cell(m, n) - # then MATLAB will save the empty cells as zero-byte matrices. If one creates a - # empty cells using - # a = {[], [], []} - # then MATLAB does not save the empty cells as zero-byte matrices. To avoid - # surprises, we produce an empty array in both cases. - return ("", Matrix{Union{}}(undef, 0, 0)) - end - name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) - if T == tNUMERIC || T == tSPARSE - real_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) - if T == tNUMERIC && imagf == imagfCOMPLEX - imag_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) - data = complex.(real_data, imag_data) - else - data = real_data - end - datamat = reshape(data, Int(mrows), Int(ncols)) - if T == tNUMERIC - return (name, datamat) - elseif T == tSPARSE - if size(datamat,2) == 3 - return (name, sparse(datamat[:,1], datamat[:,2], datamat[:,3])) - else - return (name, sparse(datamat[:,1], datamat[:,2], complex.(datamat[:,3], datamat[:,4]))) - end - end - elseif T == tTEXT - if mrows > 1 - charvec = UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows))) - charmat = reshape(charvec, Int(mrows), Int(ncols)) - data = [String(charmat[i,:]) for i in 1:mrows] - else - data = String(UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols)))) - end - return (name, data) - end -end - -# Open MAT file for reading -matopen(ios::IOStream, endian_indicator::Bool) = Matlabv4File(ios, endian_indicator) - -# Read whole MAT file -function read(matfile::Matlabv4File) - seek(matfile.ios, 0) - vars = Dict{String, Any}() - while !eof(matfile.ios) - (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) - vars[name] = data - end - vars -end - -# Read only variable names -function getvarnames(matfile::Matlabv4File) - if !isdefined(matfile, :varnames) - seek(matfile.ios, 0) - matfile.varnames = varnames = Dict{String, Int64}() - while !eof(matfile.ios) - offset = position(matfile.ios) - M, O, P, T, mrows, ncols, imagf, namlen = read_header(matfile.ios, matfile.swap_bytes) - f = matfile.ios - - name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) - varnames[name] = offset - imag_offset = 0 - skip(f, mrows*ncols*sizeof(pTYPE[P])) - if imagf == imagfCOMPLEX - skip(f, mrows*ncols*sizeof(pTYPE[P])) - end - end - end - matfile.varnames -end - -names(matfile::Matlabv4File) = keys(getvarnames(matfile)) - -# Read a variable from a MAT file -function read(matfile::Matlabv4File, varname::String) - varnames = getvarnames(matfile) - if !haskey(varnames, varname) - error("no variable $varname in file") - end - seek(matfile.ios, varnames[varname]) - (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) - data -end - -function colvals(A::AbstractSparseMatrix) - rows = rowvals(A) - cols = similar(rows) - m,n = size(A) - for i=1:n - for j in nzrange(A,i) - cols[j] = i - end - end - cols -end - -function write(parent::Matlabv4File, name::String, s) - M = Int(parent.swap_bytes) - O = 0 - P = 0 - for p=keys(pTYPE) - if eltype(s) == pTYPE[p] || eltype(s) == Complex{pTYPE[p]} - P = p - end - end - if pTYPE[P] != eltype(s) && Complex{pTYPE[P]} != eltype(s) && - !(s isa AbstractString && pTYPE[P] == Float64) && - !(s isa Vector{String} && pTYPE[P] == Float64) - error("invalid value type when writing v4 file") - end - if s isa AbstractSparseMatrix - T = tSPARSE - elseif s isa AbstractString || s isa Vector{String} - T = tTEXT - else - T = tNUMERIC - end - write(parent.ios, Int32(1000*M + 100*O + 10*P + T)) - - mrows = 1 - ncols = 1 - if s isa AbstractVector && !(s isa Vector{String}) - ncols = length(s) - elseif s isa AbstractMatrix - if s isa AbstractSparseMatrix - mrows = nnz(s) - ncols = 3 - if eltype(s) <: Complex - ncols = 4 - end - else - mrows, ncols = size(s) - end - elseif s isa Vector{String} - ncols = length(s[1]) - mrows = length(s) - elseif s isa AbstractString - ncols = length(s) - end - write(parent.ios, Int32(mrows)) - write(parent.ios, Int32(ncols)) - - imagf = 0 - if eltype(s) <: Complex && T == tNUMERIC - imagf = 1 - end - write(parent.ios, Int32(imagf)) - - namlen = length(name) + 1 - write(parent.ios, Int32(namlen)) - - write(parent.ios, Vector{UInt8}(name)) - write(parent.ios, UInt8(0)) - - if s isa AbstractArray && T == tNUMERIC - write(parent.ios, reshape(real(s), length(s))) - if imagf == 1 - write(parent.ios, reshape(imag(s), length(s))) - end - elseif s isa Number - write(parent.ios, real(s)) - if imagf == 1 - write(parent.ios, imag(s)) - end - elseif s isa AbstractString - floatarray = Float64.(Vector{UInt8}(s)) - write(parent.ios, floatarray) - elseif s isa Vector{String} - floatarray = Matrix{Float64}(undef, mrows, ncols) - for (i,strel) = enumerate(s) - floatarray[i,:] = Float64.(Vector{UInt8}(strel)) - end - write(parent.ios, floatarray) - elseif T == tSPARSE - rows = rowvals(s) - cols = colvals(s) - vals = nonzeros(s) - write(parent.ios, pTYPE[P].(rows)) - write(parent.ios, pTYPE[P].(cols)) - write(parent.ios, pTYPE[P].(real(vals))) - if eltype(s) <: Complex - write(parent.ios, pTYPE[P].(imag(vals))) - end - end -end - -# Close MAT file -close(matfile::Matlabv4File) = close(matfile.ios) - -end diff --git a/src/MAT_v4_pr164.jl b/src/MAT_v4_pr164.jl deleted file mode 100644 index 20f3479..0000000 --- a/src/MAT_v4_pr164.jl +++ /dev/null @@ -1,59 +0,0 @@ -module MAT_v4_pr164 - -import Base: read, write, close - -mutable struct Matlabv4File - ios::IOStream - varnames::Dict{String, Int64} - - Matlabv4File(ios) = new(ios) -end - -const V4_ELTYPE = [Float64, Float32, Int32, Int16, UInt16, UInt8] - -matopen(ios::IOStream) = Matlabv4File(ios) - -function unpack_header_type(type::Int32) - T, P, O, M = digits(type; pad=4) - iszero(O) || error("file is not a v4 MAT file (magic digit not 0)") - iszero(M) || error("only little endian v4 MAT currently supported") - iszero(T) || error("only full numeric matrices supported for v4 MAT files") - - P in 0:5 || error("invalid eltype digit in v4 MAT file: $P") - eltype = V4_ELTYPE[P+1] - - return eltype -end - -function read_one_mat!(mat::Matlabv4File) - type, mrows, ncols, imagf, namlen = read!(mat.ios, Vector{Int32}(undef, 5)) - eltype = unpack_header_type(type) - - iszero(imagf) || error("no imaginary matrix support") - - name = String(read!(mat.ios, Vector{UInt8}(undef, namlen-1))) - - # one null byte before start of matrix data - Base.read(mat.ios, UInt8) - - @show mrows, ncols - value = read!(mat.ios, Matrix{eltype}(undef, mrows, ncols)) - - return name => value -end - -function read(mat::Matlabv4File) - seekstart(mat.ios) - - results = Dict{String,Any}() - while !eof(mat.ios) - name, value = read_one_mat!(mat) - results[name] = value - end - - return results -end - -close(mat::Matlabv4File) = close(mat.ios) - -end # module diff --git a/src/Matlab_MATFileFormatV4.pdf b/src/Matlab_MATFileFormatV4.pdf deleted file mode 100644 index 8cbe7e72d5a33ded36139939783ed74ba7646eae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23557 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 M97s}9O-~*4Kg~M4yZ`_I diff --git a/src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf b/src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf deleted file mode 100644 index 9f0db31aadd2215647a07e05568a3cf7135473da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85780 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 z 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 diff --git a/test/runtests.jl b/test/runtests.jl index 222deaa..0e10836 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,6 +8,6 @@ using SparseArrays, LinearAlgebra include("read.jl") include("write.jl") - +include("runtests_modelica.jl") diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index dd37e2f..3d0f8c8 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -1,52 +1,54 @@ -cd(joinpath(@__DIR__,"..")) + +# Test origins: +# test/v4_Modelica/BouncingBall/BouncingBall_res.mat - simulated with OpenModelica v1.19.0 +# test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat - simulated with Dymola v2021 +# test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with OpenModelica v1.19.0 +# These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents + import Pkg Pkg.activate(joinpath(@__DIR__, "..")) -# test only MAT_v4 using Test using JSON -# include("../src/MAT.jl") -include("../src/MAT_v4_Modelica.jl") - - +cd(joinpath(@__DIR__,"..")) +include("../src/MAT.jl") +# include("../src/MAT_v4_Modelica.jl") +#OpenModelica v1.19.0 +matBBO = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_res.mat") +matFBB = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_res.mat") +#Dymola v2021 -- not implemented +# matBBD = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") -# The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices -# The first matrix, Aclass is narrowly defined @testset "isLittleEndian" begin - @test MAT_v4_Modelica.isLittleEndian(0) == true - @test MAT_v4_Modelica.isLittleEndian(1000) == false - @test MAT_v4_Modelica.isLittleEndian(2000) == false - @test MAT_v4_Modelica.isLittleEndian(3000) == false + @test MAT.MAT_v4_Modelica.isLittleEndian(0) == true + @test MAT.MAT_v4_Modelica.isLittleEndian(1000) == false + @test MAT.MAT_v4_Modelica.isLittleEndian(2000) == false + @test MAT.MAT_v4_Modelica.isLittleEndian(3000) == false end @testset "dataFormat" begin - @test MAT_v4_Modelica.dataFormat(0000) <: Float64 - @test MAT_v4_Modelica.dataFormat(0010) <: Float32 - @test MAT_v4_Modelica.dataFormat(0020) <: Int32 - @test MAT_v4_Modelica.dataFormat(0030) <: Int16 - @test MAT_v4_Modelica.dataFormat(0040) <: UInt16 - @test MAT_v4_Modelica.dataFormat(0050) <: UInt8 + @test MAT.MAT_v4_Modelica.dataFormat(0000) <: Float64 + @test MAT.MAT_v4_Modelica.dataFormat(0020) <: Int32 + @test MAT.MAT_v4_Modelica.dataFormat(0030) <: Int16 + @test MAT.MAT_v4_Modelica.dataFormat(0040) <: UInt16 + @test MAT.MAT_v4_Modelica.dataFormat(0050) <: UInt8 end @testset "typeBytes" begin - @test MAT_v4_Modelica.typeBytes(Int32) == 4 + @test MAT.MAT_v4_Modelica.typeBytes(Int32) == 4 end - @testset "Aclass" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) @test ac.positionStart == 0 @test ac.positionEnd == 71 end - @testset "readVariableNames" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) # @show vn @test length(vn.names) == 11 @test vn.names[1] == "time" @@ -56,21 +58,17 @@ end @test vn.positionEnd == 228 end - @testset "getVariableIndex" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 - @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[10]) == 10 + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 + @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[10]) == 10 end - @testset "readVariableDescriptions" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) @test length(vd.descriptions) == 11 @test vd.descriptions[1] == "Simulation time [s]" @test vd.descriptions[3] == "velocity of ball" @@ -78,11 +76,10 @@ end end @testset "readDataInfo" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) - di = MAT_v4_Modelica.readDataInfo(ac,vd) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) # @show di.info[3] @test di.info[1]["isWithinTimeRange"] == -1 @test di.info[3]["locatedInData"] == 2 @@ -91,45 +88,43 @@ end end @testset "readVariable: BouncingBall" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) - di = MAT_v4_Modelica.readDataInfo(ac,vd) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) # println(JSON.json(di.info, 2)) #get the data 1/2 info - eff = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 + eff = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 @test length(eff) == 2 @test eff[1] ≈ 0.77 @test eff[2] ≈ 0.77 - grav = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 + grav = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 @test length(grav) == 2 @test grav[1] ≈ 9.81 @test grav[2] ≈ 9.81 - time = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # data0 + time = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # data0 @test all(isapprox.(time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) - height = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 + height = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 @test isapprox(height[1], 111, rtol=1e-3) @test isapprox(height[2], 110.9509, rtol=1e-3) - vel = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 + vel = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 @test isapprox(vel[2], -0.981, rtol=1e-3) end @testset "readVariable: FallingBodyBox" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) - di = MAT_v4_Modelica.readDataInfo(ac,vd) + ac = MAT.MAT_v4_Modelica.readAclass(matFBB) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) # println(JSON.json(di.info, 2)) - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # display(var) ret = true for i = 2:length(var)-1 #last time is duplicated @@ -138,18 +133,18 @@ end @test ret == true #point-check values read from FallingBodyBox_res.csv - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") @test isapprox(var[16], 0.002923239, rtol=1e-3) - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1,1]") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1,1]") @test isapprox(var[26], 0.983794001, rtol=1e-3) - @test_throws ErrorException MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + @test_throws ErrorException MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") @test isapprox(var[33], -0.58818129, rtol=1e-3) - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") @test isapprox(var[72], 0.935886479, rtol=1e-3) end diff --git a/test/runtests_pr132.jl b/test/runtests_pr132.jl deleted file mode 100644 index ee4eea2..0000000 --- a/test/runtests_pr132.jl +++ /dev/null @@ -1,37 +0,0 @@ -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -# test only MAT_v4 -using Test -include("../src/MAT.jl") -include("../src/MAT_v4_pr132.jl") - -mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - -@testset "checkv4" begin - # function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Bool, ff::Bool, compress::Bool) - # function matopen(fname::AbstractString, mode::AbstractString; compress::Bool = false) - # mat = MAT.matopen(mat1s, "r" ) - - rawfid = open(mat1s, "r") - (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) - close(rawfid) - - @test isv4 == true -end - -rawfid = open(mat1s, "r") -(isv4, swap_bytes) = MAT_v4.checkv4(rawfid) -mat = MAT_v4_pr132.matopen(rawfid, swap_bytes ) - -@testset "getvarnames" begin - # function getvarnames(matfile::Matlabv4File) - @show gvn = MAT_v4_pr132.getvarnames(mat) - # gvn = MAT_v4.getvarnames(mat) = Dict("name" => 71, "Aclass" => 0, "dataInfo" => 448328, "data_2" => 501296, "data_1" => 488197, "description" => 117126) -# ...and this is not correct - @test false -end - - -close(rawfid) \ No newline at end of file diff --git a/test/runtests_pr164.jl b/test/runtests_pr164.jl deleted file mode 100644 index dea51b4..0000000 --- a/test/runtests_pr164.jl +++ /dev/null @@ -1,48 +0,0 @@ -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -# test only MAT_v4 -using Test -include("../src/MAT.jl") -include("../src/MAT_v4_pr164.jl") - -mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - - - -@testset "unpack_header" begin - rawfid = open(mat1s, "r") - # (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) - - # From 'Version-4-MAT-File-Format.pdf': - # Each matrix starts with a fixed-length 20-byte header that contains information describing certain attributes of the Matrix. - # The 20-byte header consists of five long (4-byte) integers: - @show type, mrows, ncols, imagf, namlen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(type; pad=4) - # P indicates which format the data is stored: 5 = 8bit uint - # T indicates matrix type: 1 = text matrix - - @show eltype = MAT_v4_pr164.unpack_header_type(type) - - - close(rawfid) - - # @test isv4 == true -end - -# rawfid = open(mat1s, "r") -# (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) -# mat = MAT_v4.matopen(rawfid, swap_bytes ) - -# @testset "getvarnames" begin -# # function getvarnames(matfile::Matlabv4File) -# @show gvn = MAT_v4.getvarnames(mat) -# # gvn = MAT_v4.getvarnames(mat) = Dict("name" => 71, "Aclass" => 0, "dataInfo" => 448328, "data_2" => 501296, "data_1" => 488197, "description" => 117126) -# # ...and this is not correct -# @test false -# end - - -# close(rawfid) \ No newline at end of file diff --git a/test/v4/testcomplex_4.2c_SOL2.mat b/test/v4/testcomplex_4.2c_SOL2.mat deleted file mode 100644 index 36621b25c08f18e4545100c6eaec015123c3bf9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmZQzV1B{Cz`zK^oKTvlB(=CCIX|}`C$)mX{sT}H2<)FNn3q;>eb#2;GBsn7820@T p{+azyc_{zfo>i5WKJ&V`pz2S<^g~R6n{x&x4mWop0dqG(`$IPYCV6bhD=3Sn-kr dCzQ{hRiznf5$7NT6&L-x`{nNCr4Eu1c>u#Y9lihn diff --git a/test/v4/testmatrix_4.2c_SOL2.mat b/test/v4/testmatrix_4.2c_SOL2.mat deleted file mode 100644 index 3698c8853b46d4a42194002523b57fddfb225908..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151 zcmZQzV1B{Cz`zW|tUwF`+$E{SCAo0_8%Z(4iJjLfdiEf6^2tVdAI64fzvu`z diff --git a/test/v4/testminus_4.2c_SOL2.mat b/test/v4/testminus_4.2c_SOL2.mat deleted file mode 100644 index cc207ed9f32095f39b7690e2dc1e2dc0d55ee8e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38 kcmZQzV1B{Cz`zK_K#GB@B(=CCH#4uam|_11kN^V%0A!m6lK=n! diff --git a/test/v4/testmulti_4.2c_SOL2.mat b/test/v4/testmulti_4.2c_SOL2.mat deleted file mode 100644 index 9c6ba793cf41bf36447ab7a1890447fe5e939614..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmZQzV1B{Cz`zW|tUwF`Oo>TXrytD# diff --git a/test/v4/testonechar_4.2c_SOL2.mat b/test/v4/testonechar_4.2c_SOL2.mat deleted file mode 100644 index cdb4191c7d2eb0ac66d4f6add250e1f6a604d892..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40 mcmZQzV1CKKz`zK_K#GBoBe96VA*KN&#sC0t%m%jr diff --git a/test/v4/testsparse_4.2c_SOL2.mat b/test/v4/testsparse_4.2c_SOL2.mat deleted file mode 100644 index 55cbd3c1b3d65630beae47832ffbcc7a6fd43354..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmZQzV1C8Gz`y~-%s>nR+$E{SCB+4aMa8KM_8%Z(4iJjL0i+NJVB)xFLh2mArZB+G Wa}aKwqPFu=`o5P%3ch@jFi^D)#D&<~Y{yA#GIl?DK-2^cH@ diff --git a/test/v4/teststring_4.2c_SOL2.mat b/test/v4/teststring_4.2c_SOL2.mat deleted file mode 100644 index 137561e1f636d7b08959e43e969a6984eb7a3b37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 375 zcmZ{gO%6an423I}j^YxvVJTQKEJbWAm;*SP>$ruVzPB0Cq-nqQwbP79e2PePdwTn0 zi61w=`E_0<(adUEA-dyDRLQ$>X9acO7HmP(fmx@H{cwJe*OdBxH||jjyl4?yTB eFvZ{y=>Xxw^u;tl_z+ Date: Tue, 28 Feb 2023 08:28:28 -0600 Subject: [PATCH 13/91] uncomment --- src/MAT.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MAT.jl b/src/MAT.jl index 2349282..efe55b2 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -167,12 +167,12 @@ end ### v0.10.0 deprecations ### -# import HDF5: exists -# export exists -# @noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) -# Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) -# return haskey(matfile, varname) -# end +import HDF5: exists +export exists +@noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) + Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) + return haskey(matfile, varname) +end @noinline function Base.names(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) Base.depwarn("`names(matfile)` is deprecated, use `keys(matfile)` instead.", :names) return keys(matfile) From decdd8657857e4cff022b87141eaf3f275368b31 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 08:31:11 -0600 Subject: [PATCH 14/91] remove more dev files --- test/runtests_modelica_manual.jl | 126 ---------------- test/runtests_modelica_readMatrix.jl | 209 --------------------------- 2 files changed, 335 deletions(-) delete mode 100644 test/runtests_modelica_manual.jl delete mode 100644 test/runtests_modelica_readMatrix.jl diff --git a/test/runtests_modelica_manual.jl b/test/runtests_modelica_manual.jl deleted file mode 100644 index d3ee86c..0000000 --- a/test/runtests_modelica_manual.jl +++ /dev/null @@ -1,126 +0,0 @@ -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -# test only MAT_v4 -using Test -# include("../src/MAT.jl") -# include("../src/MAT_v4_Modelica.jl") - -mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - - -@testset "manual" begin - rawfid = open(mat1s, "r") - - # This reader is collected from two references: - # (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) - # Matlab_MATFileFormatV4.pdf from https://www.eiscat.se/wp-content/uploads/2016/03/Version-4-MAT-File-Format.pdf - # OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf from https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/technical_details.html - - # Start by reading the header, following Matlab: - # Each matrix starts with a fixed-length 20-byte header that contains information describing certain attributes of the Matrix. - # The 20-byte header consists of five long (4-byte) integers: - @show type, nrows, ncols, imagf, namelen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(type; pad=4) - - # M indicates the numeric format of binary numbers on the machine that wrote the file. - # Use this table to determine the number to use for your machine: - # 0 IEEE Little Endian (PC, 386, 486, DEC Risc) - # 1 IEEE Big Endian (Macintosh, SPARC, Apollo,SGI, HP 9000/300, other Motorola) - # 2 VAX D-float - # 3 VAX G-float - # 4 Cray - @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 - @show numberFormat(M) - - # O is always 0 (zero) and is reserved for future use. - # @show O - - # P indicates which format the data is stored: - # 0 double-precision (64-bit) floating point numbers - # 1 single-precision (32-bit) floating point numbers - # 2 32-bit signed integers - # 3 16-bit signed integers - # 4 16-bit unsigned integers - # 5 8-bit unsigned integers - @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 - @show dataFormat(P) - @test dataFormat(P) == uint8 - - # T indicates matrix type: 1 = text matrix - @enum matrixFormat numericFull=0 text=1 spars=2 - @show matrixFormat(T) - - # nrows The row dimension contains an integer with the number of rows in the matrix. - @show nrows - # ncols The column dimension contains an integer with the number of columns in the matrix. - @show ncols - # imagf The imaginary flag is an integer whose value is either 0 or 1. If 1, then the matrix has an imaginary part. If 0, there is only real data. - @show imagf - # namelen The name length contains an integer with 1 plus the length of the matrix name. - @show namelen - - # Immediately following the fixed length header is the data whose length is dependent on the variables in the fixed length header: - # name The matrix name consists of namlen ASCII bytes, the last one of which must be a null character (’\0’ ). - @show nameuint = read!(rawfid, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - # pop!(nameuint) #trim \0 - # @show name = String(nameuint) - @show name = replace(String(nameuint), '\0'=>"") - - # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. - # @show realint = read!(rawfid, Vector{UInt8}(undef, 1*nrows*ncols)) # UInt8 from P is 8 bytes long - # @show realint = read!(rawfid, Vector{UInt16}(undef, 8*nrows*ncols)) # UInt8 from P is 8 bytes long - # @show realint = read(rawfid, 32) # UInt8 from P is 8 bytes long - @show realint = read!(rawfid, Matrix{UInt8}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - - # imag Imaginary part of the matrix, if any. If the imaginary flag imagf is nonzero, the imaginary part of a matrix is placed here. It is stored in the same manner as the real data. - if imagf==1 - @show imagint = read!(rawfid, Vector{UInt8}(undef, nrows*ncols)) # read the full namelen to make the pointer ready to read the data - end - - # The variables stored in the MAT-file are (in the order required by OpenModelica): - # Aclass - # • Aclass(1,:) is always Atrajectory - # • Aclass(2,:) is 1.1 in OpenModelica - # • Aclass(3,:) is empty - # • Aclass(4,:) is either binTrans or binNormal - - Aclass1 = replace(String(realint[1,:]), '\0'=>"") - Aclass2 = replace(String(realint[2,:]), '\0'=>"") - Aclass3 = replace(String(realint[3,:]), '\0'=>"") - Aclass4 = replace(String(realint[4,:]), '\0'=>"") - @test Aclass1 == "Atrajectory" - @test Aclass2 == "1.1" - @test isempty(Aclass3) - @test Aclass4 == "binNormal" || Aclass4 == "binTrans" - - #now to read the next matrix header... - # name: - # Is an n x m character (int8) matrix, where n is the number of variables stored in the result-file (including time). m is the length of the longest variable. OpenModelica stores the trailing part of the name as NIL bytes (0) whereas other tools use spaces for the trailing part. - @show type, nrows, ncols, imagf, namelen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - T, P, O, M = digits(type; pad=4) - @show numberFormat(M) - @show matrixFormat(T) - @show dataFormat(P) - - @show nrows - @show ncols - @show imagf - @show namelen - nameuint = read!(rawfid, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - @show name = replace(String(nameuint), '\0'=>"") - - realint = read!(rawfid, Matrix{UInt8}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - @show varname1 = replace(String(realint[:,1]), '\0'=>"") # note :,1 implicit transpose - @show varname2 = replace(String(realint[:,2]), '\0'=>"") # note :,1 implicit transpose - @show varname3 = replace(String(realint[:,3]), '\0'=>"") # note :,1 implicit transpose - @show varname4 = replace(String(realint[:,4]), '\0'=>"") # note :,1 implicit transpose - @show varname5 = replace(String(realint[:,5]), '\0'=>"") # note :,1 implicit transpose - - close(rawfid) -end - - diff --git a/test/runtests_modelica_readMatrix.jl b/test/runtests_modelica_readMatrix.jl deleted file mode 100644 index 742e9e5..0000000 --- a/test/runtests_modelica_readMatrix.jl +++ /dev/null @@ -1,209 +0,0 @@ -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -# test only MAT_v4 -using Test -# include("../src/MAT.jl") -# include("../src/MAT_v4_Modelica.jl") - -mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - -struct MATrix - name::String - # data::T where T<:AbstractMatrix - data::Any - # type -end - -@enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 -@enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 -@enum matrixFormat numericFull=0 text=1 spars=2 - -""" -Read a single data matrix from the opened MAT file -""" -function readMATMatrix(ios::IOStream) - @show startP = position(ios) - # The 20-byte header consists of five long (4-byte) integers: - type, nrows, ncols, imagf, namelen = read!(ios, Vector{Int32}(undef, 5)) # 32bits=4byte, * 5 qty - - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(type; pad=4) - - nameuint = read!(ios, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - - # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. - realint = [] - @show dataFormat(P) - if dataFormat(P) == uint8 - realint = read!(ios, Matrix{UInt8}(undef, nrows,ncols)) - elseif dataFormat(P) == uint16 - realint = read!(ios, Matrix{UInt16}(undef, nrows,ncols)) - elseif dataFormat(P) == int32 - realint = read!(ios, Matrix{Int32}(undef, nrows,ncols)) - elseif dataFormat(P) == single - realint = read!(ios, Matrix{Float32}(undef, nrows,ncols)) - elseif dataFormat(P) == double - realint = read!(ios, Matrix{Float64}(undef, nrows,ncols)) - else - error("Unknown data format for P=$P") - end - - # imag Imaginary part of the matrix, if any. If the imaginary flag imagf is nonzero, the imaginary part of a matrix is placed here. It is stored in the same manner as the real data. - if imagf==1 - error("imaginary not implemented") - # if dataFormat(P) - # @show imagint = read!(ios, Vector{UInt8}(undef, nrows*ncols)) # read the full namelen to make the pointer ready to read the data - end - - @show matrixName - @show endP = position(ios) - - return MATrix(matrixName, realint) -end - -""" - # The variables stored in the MAT-file are (in the order required by OpenModelica): - # Aclass - # • Aclass(1,:) is always Atrajectory - # • Aclass(2,:) is 1.1 in OpenModelica - # • Aclass(3,:) is empty - # • Aclass(4,:) is either binTrans or binNormal - - -""" -function parseAclass(mat::MATrix) - # @show mat - # Aclass1 = replace(String(mat.data[1,:]), '\0'=>"") - # Aclass2 = replace(String(data[2,:]), '\0'=>"") - # Aclass3 = replace(String(data[3,:]), '\0'=>"") - # Aclass4 = replace(String(data[4,:]), '\0'=>"") - Aclass = [ replace(String(mat.data[1,:]), '\0'=>""), - replace(String(mat.data[2,:]), '\0'=>""), - replace(String(mat.data[3,:]), '\0'=>""), - replace(String(mat.data[4,:]), '\0'=>"") ] - return Aclass -end - - -""" -name -Is an n x m character (int8) matrix, where n is the number of variables stored in the result-file (including time). -m is the length of the longest variable. -OpenModelica stores the trailing part of the name as NIL bytes (0) whereas other tools use spaces for the trailing part. -""" -function parseVariableNames(mat::MATrix) - varNames = [] - m,n = size(mat.data) - for i in 1:n - push!(varNames, replace(String(mat.data[:,i]), '\0'=>"")) - end - return varNames -end - -""" -Find the index of the given variable, returning -1 if not found. -""" -function getVariableIndex(variableNames, name) - vecAll = findall( x->x==name, variableNames) - n = length(vecAll) - - if isempty(vecAll) == true - return -1 - else - if n>1 - error("Found $n instances of variable [$name], but variables should be unique.") - end - return vecAll[1] - end -end - - -""" -description -Is an n x m character (int8) matrix containing the comment-string corresponding to the variable in the name matrix -""" -function parseVariableDescriptions(mat::MATrix) - varDesc = [] - m,n = size(mat.data) - for i in 1:n - push!(varDesc, replace(String(mat.data[:,i]), '\0'=>"")) - end - return varDesc -end - -""" -dataInfo provides indicies to access variable data - -dataInfo -Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). -• dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). -• dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. -• dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. -• dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. -""" -function parseDataInfo(mat::MATrix, varIndex::Int) - # @show mat.data[:,varIndex] - return Dict("locatedInData"=>mat.data[1,varIndex], "indexInData"=>mat.data[2,varIndex], "isInterpolated"=>mat.data[3,varIndex], "isWithinTimeRange"=>mat.data[4,varIndex] ) -end - -""" -data_1 -If it is an n x 1 matrix it contains the values of parameters. If it is an n x 2 matrix, the first and second column -signify start and stop-values. -""" -function parseData1(mat::MATrix, varIndex::Int) - @show size(mat.data) - @show mat.data[varIndex,:] -end - - -@testset "matrix reader" begin - matio = open(mat1s, "r") - seekstart(matio) - ret = readMATMatrix(matio) - Aclass = parseAclass(ret) - @test Aclass[1] == "Atrajectory" - @test Aclass[2] == "1.1" - @test isempty(Aclass[3]) == true - @test Aclass[4] == "binTrans" || Aclass[4] == "binNormal" - - ret = readMATMatrix(matio) # name - variableNames = parseVariableNames(ret) - @test variableNames[1] == "time" - @test variableNames[2490] == "world.z_label.color[3]" - @test getVariableIndex(variableNames, "time") == 1 - - - @show position(matio) - ret = readMATMatrix(matio) # description - variableDescriptions = parseVariableDescriptions(ret) - @test variableDescriptions[2490] == "Color of cylinders" - - # @allocated readMATMatrix(matio) # dataInfo - ret = readMATMatrix(matio) # dataInfo - @show dataInfo = parseDataInfo(ret, 1) - - ret = readMATMatrix(matio) # data_1 - - - # itime = getVariableIndex(variableNames, "time") - # # ditime = parse - # parseData1(ret, itime) - - # parseData1(ret, iphi) - # # parseData1(ret, ihfaf1) - - # iphi = getVariableIndex(variableNames, "revolute.phi") - # ihfaf1 = getVariableIndex(variableNames, "head.frame_a.f[1]") - - ret = readMATMatrix(matio) # data_2 - close(matio) - - @test true - -end - - From 0103c08fd361ba1ecf5f7ab68359b1166e6d8649 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 08:34:01 -0600 Subject: [PATCH 15/91] one more --- test/readwrite4.jl | 52 ---------------------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 test/readwrite4.jl diff --git a/test/readwrite4.jl b/test/readwrite4.jl deleted file mode 100644 index c751125..0000000 --- a/test/readwrite4.jl +++ /dev/null @@ -1,52 +0,0 @@ -using MAT, Test - -function check(filename, result) - matfile = matopen(filename) - for (k, v) in result - @test exists(matfile, k) - got = read(matfile, k) - if !isequal(got, v) || (typeof(got) != typeof(v) && (!isa(got, String) || !(isa(v, String)))) - close(matfile) - error(""" - Data mismatch reading $k from $filename ($format) - - Got $(typeof(got)): - - $(repr(got)) - - Expected $(typeof(v)): - - $(repr(v)) - """) - end - end - @test union!(Set(), names(matfile)) == union!(Set(), keys(result)) - close(matfile) - - mat = matread(filename) - if !isequal(mat, result) - error(""" - Data mismatch reading $filename ($format) - - Got: - - $(repr(mat)) - - Expected: - - $(repr(result)) - """) - close(matfile) - return false - end - - return true -end -cd(dirname(@__FILE__)) -for filename in readdir("v4") - #println("testing $filename") - d = matread("v4/$filename") - matwrite4("v4/tmp.mat", d) - check("v4/tmp.mat", d) - rm("v4/tmp.mat") -end From 2c6c04c1838d8f793a9ec6b519d3d9a70289f16a Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 08:38:29 -0600 Subject: [PATCH 16/91] cleaning up --- src/MAT.jl | 1 - test/runtests.jl | 8 -------- 2 files changed, 9 deletions(-) diff --git a/src/MAT.jl b/src/MAT.jl index efe55b2..b84f1ef 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -60,7 +60,6 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo end end - # Check for MAT v5 file seek(rawfid, 124) version = read(rawfid, UInt16) diff --git a/test/runtests.jl b/test/runtests.jl index 0e10836..ab4626c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,13 +1,5 @@ - -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - - using SparseArrays, LinearAlgebra include("read.jl") include("write.jl") include("runtests_modelica.jl") - - From cd29c74a8352cbb8b017a2aeeba6fc574674c0e2 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 09:42:01 -0600 Subject: [PATCH 17/91] add Pkg dependency --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 0944a5d..333dc67 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.10.3" BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] From 334688e191361fea3a548ec7ceb49ba6d6b6a170 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 09:48:36 -0600 Subject: [PATCH 18/91] remove JSON --- test/runtests_modelica.jl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 3d0f8c8..8c154dc 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -9,7 +9,6 @@ import Pkg Pkg.activate(joinpath(@__DIR__, "..")) using Test -using JSON cd(joinpath(@__DIR__,"..")) include("../src/MAT.jl") # include("../src/MAT_v4_Modelica.jl") @@ -93,8 +92,6 @@ end vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) - # println(JSON.json(di.info, 2)) #get the data 1/2 info - eff = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 @test length(eff) == 2 @test eff[1] ≈ 0.77 @@ -122,8 +119,6 @@ end vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) - # println(JSON.json(di.info, 2)) - var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # display(var) ret = true From 0b89be60aa9a6d1c8066bce69dd8672534c41ef6 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 10:02:59 -0600 Subject: [PATCH 19/91] pull out open kwarg lock=false to make Julia 1.3 happy --- src/MAT_v4_Modelica.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index f62a1f5..cbfde26 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -112,7 +112,7 @@ end Reads the Aclass matrix, returing if the data is stored binNormal or binTranspose """ function readAclass( filepath::String ) - open(filepath, "r", lock=false) do matio + open(filepath, "r") do matio seekstart(matio) # always start from the start, don't assume the position startP = position(matio) @@ -160,7 +160,7 @@ struct VariableNames end function readVariableNames(ac::Aclass) - open(ac.filepath, "r", lock=false) do matio + open(ac.filepath, "r") do matio seek(matio, ac.positionEnd) #skip over Aclass startP = position(matio) @@ -228,7 +228,7 @@ struct VariableDescriptions end function readVariableDescriptions(ac::Aclass, vn::VariableNames) - open(ac.filepath, "r", lock=false) do matio + open(ac.filepath, "r") do matio seek(matio, vn.positionEnd) #this follows the VariableNames matrix startP = position(matio) @@ -287,7 +287,7 @@ Is an n x 4 integer matrix containing information for each variable (in the same dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. """ function readDataInfo(ac::Aclass, vd::VariableDescriptions) - open(ac.filepath, "r", lock=false) do matio + open(ac.filepath, "r") do matio seek(matio, vd.positionEnd) #this follows the VariableNames matrix startP = position(matio) @@ -365,7 +365,7 @@ end read one variable from the file """ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) - open(ac.filepath, "r", lock=false) do matio + open(ac.filepath, "r") do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix # read data1 header: From 64949c90006f4d1ad2252109708e7af7e84c3c2a Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 15:44:49 -0600 Subject: [PATCH 20/91] add all-in-one readVariable() --- src/MAT_v4_Modelica.jl | 54 ++++++++++++++++++++++++++++++--------- test/runtests_modelica.jl | 20 +++++++++++++-- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index cbfde26..55eac3b 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -47,6 +47,7 @@ module MAT_v4_Modelica +using DataFrames function isLittleEndian(dtype) :: Bool #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... @@ -370,31 +371,26 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d # read data1 header: mh1 = readMatrixHeader!(matio) - if mh1.name != "data_1" - error("trying to read matrix [data_1] but read $matrixName") - end + @assert mh1.name == "data_1" "trying to read matrix [data_1] but read $matrixName" data1MatrixStart = mark(matio) try toskip = mh1.nRows*mh1.nCols*typeBytes(mh1.format) #817*2*8 = 13072, 488197+20+7+13072 = 501296 skip(matio, toskip ) catch e - error("caught error $e while reading $ac.filepath") + throw( ErrorException("Caught error $e while reading $ac.filepath") ) end # read data2 header: mh2 = readMatrixHeader!(matio) - if mh2.name != "data_2" - error("trying to read matrix [data_2] but read $(mh2.name)") - end + @assert mh2.name == "data_2" "trying to read matrix [data_2] but read $(mh2.name)" data2MatrixStart = mark(matio) #with the positions marked, read the desired variable - # println("\nlocate variable [$name]:") varInd = getVariableIndex(vn, name) if varInd < 1 - throw( ErrorException("Variable [$name] not found in file [$(ac.filepath)]") ) + throw( ArgumentError("Variable [$name] not found in file [$(ac.filepath)]") ) end if di.info[varInd]["locatedInData"] == 1 #data_1 @@ -421,20 +417,54 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d end return readns else - error("reading binTranspose not implemented, lack test data") + throw(ErrorException("reading binTranspose not implemented, lack test data") ) end else - error("variable [$name] is located in an unknown location") + throw(ErrorException("variable [$name] is located in an unknown location") ) end end #open end + """ -all-in-one +All-in-one reading of variable `name` from `filepath`, returning a DataFrame with columns "time" and `name` """ function readVariable(filepath::String, name::String) :: DataFrame + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + time = readVariable(ac, vn, vd, di, "time") + varn = readVariable(ac, vn, vd, di, name) + df = DataFrame("time"=>time, name=>varn) + return df end +""" +Reads the vector of variable `names` from mat file `filepath`, returning a DataFrame with columns "time" and `names` +""" +function readVariables(filepath::String, names::AbstractVector{T}) :: DataFrame where T<:AbstractString + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + df = DataFrame("time"=> readVariable(ac, vn, vd, di, "time") ) + for name in names + var = readVariable(ac,vn,vd,di, name) + if length(var) == 1 # a constant value + df[!, name] .= var[1] + elseif length(var) == 2 && var[1] == var[2] + df[!, name] .= var[1] + elseif length(var) == length(df.time) + df[!, name] = var + else + throw(DimensionMismatch("Length of $name [$(length(var))] differs from the dataframe [$(length(df.time))], cannot add it")) + end + end + return df +end end #MAT_v4_Modelica \ No newline at end of file diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 8c154dc..2e29e4e 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -11,7 +11,6 @@ Pkg.activate(joinpath(@__DIR__, "..")) using Test cd(joinpath(@__DIR__,"..")) include("../src/MAT.jl") -# include("../src/MAT_v4_Modelica.jl") #OpenModelica v1.19.0 matBBO = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_res.mat") @@ -134,14 +133,31 @@ end var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1,1]") @test isapprox(var[26], 0.983794001, rtol=1e-3) - @test_throws ErrorException MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") @test isapprox(var[33], -0.58818129, rtol=1e-3) var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") @test isapprox(var[72], 0.935886479, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "world.animateGravity") + @test isapprox(var[1], 1.0, rtol=1e-3) +end + +@testset "all-in-one readVariable" begin + data = MAT.MAT_v4_Modelica.readVariable(matFBB, "bodyBox.frame_a.r_0[1]") + @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(matFBB, "nullVariable") end +@testset "all-in-one readVariables" begin + data = MAT.MAT_v4_Modelica.readVariables(matFBB, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) + @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test isapprox(data[!,"bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) + @test isapprox(data[!,"world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time +end + + ; From 8d13c7852873610648c7697ef8589d11ea605603 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 15:45:52 -0600 Subject: [PATCH 21/91] add DataFrames dependency --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 333dc67..2540e2d 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.10.3" [deps] BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" From 95898f1c4fd2f87ad6ebe986a0a1bded333b82f4 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 15:46:37 -0600 Subject: [PATCH 22/91] add testsets for more readable test results --- test/read.jl | 343 ++++++++++++++++++++++++++------------------------ test/write.jl | 8 +- 2 files changed, 183 insertions(+), 168 deletions(-) diff --git a/test/read.jl b/test/read.jl index 67e62ab..c742cd6 100644 --- a/test/read.jl +++ b/test/read.jl @@ -45,188 +45,201 @@ end global format for _format in ["v6", "v7", "v7.3"] - global format = _format - cd(joinpath(dirname(@__FILE__), format)) - - result = Dict( - "int8" => Int8(1), - "uint8" => UInt8(1), - "int16" => Int16(1), - "uint16" => UInt16(1), - "int32" => Int32(1), - "uint32" => UInt32(1), - "int64" => Int64(1), - "uint64" => UInt64(1), - "single" => Float32(1), - "double" => Float64(1), - "logical" => true - ) - check("simple.mat", result) - matfile = matopen("simple.mat") - mat = read(matfile) - close(matfile) - for (k, v) in result - if(typeof(mat[k]) != typeof(v)) - error("Types for $k didn't match (expected $(typeof(v)), got $(typeof(mat[k])))") + @testset "testing $_format" begin + global format = _format + cd(joinpath(dirname(@__FILE__), format)) + + result = Dict( + "int8" => Int8(1), + "uint8" => UInt8(1), + "int16" => Int16(1), + "uint16" => UInt16(1), + "int32" => Int32(1), + "uint32" => UInt32(1), + "int64" => Int64(1), + "uint64" => UInt64(1), + "single" => Float32(1), + "double" => Float64(1), + "logical" => true + ) + check("simple.mat", result) + matfile = matopen("simple.mat") + mat = read(matfile) + close(matfile) + for (k, v) in result + if(typeof(mat[k]) != typeof(v)) + error("Types for $k didn't match (expected $(typeof(v)), got $(typeof(mat[k])))") + end end - end - - result = Dict( - "imaginary" => ComplexF64[1 -1 1+im 1-im -1+im -1-im im] - ) - check("complex.mat", result) - result = Dict( - "simple_string" => "the quick brown fox", - "accented_string" => "thé qüîck browñ fòx", - "concatenated_strings" => String["this is a string", "this is another string"], - "cell_strings" => Any["this is a string" "this is another string"], - "empty_string" => "" - ) - check("string.mat", result) - - result = Dict( - "a1x2" => [1.0 2.0], - "a2x1" => zeros(2, 1)+[1.0, 2.0], - "a2x2" => [1.0 3.0; 4.0 2.0], - "a2x2x2" => cat([1.0 3.0; 4.0 2.0], [1.0 2.0; 3.0 4.0]; dims=3), - "empty" => zeros(0, 0), - "string" => "string" - ) - check("array.mat", result) - - result = Dict( - "cell" => Any[v for _ in 1:1, - v in (1.0, 2.01, "string", Any["string1" "string2"])] - ) - check("cell.mat", result) - - result = Dict( - "s" => Dict{String,Any}( - "a" => 1.0, - "b" => [1.0 2.0], - "c" => [1.0 2.0 3.0] - ), - "s2" => Dict{String,Any}("a" => Any[1.0 2.0]) - ) - check("struct.mat", result) - - result = Dict( - "logical" => false, - "logical_mat" => [ - true false false - false true false - true false false - ] - ) - check("logical.mat", result) + result = Dict( + "imaginary" => ComplexF64[1 -1 1+im 1-im -1+im -1-im im] + ) + check("complex.mat", result) + + result = Dict( + "simple_string" => "the quick brown fox", + "accented_string" => "thé qüîck browñ fòx", + "concatenated_strings" => String["this is a string", "this is another string"], + "cell_strings" => Any["this is a string" "this is another string"], + "empty_string" => "" + ) + check("string.mat", result) + + result = Dict( + "a1x2" => [1.0 2.0], + "a2x1" => zeros(2, 1)+[1.0, 2.0], + "a2x2" => [1.0 3.0; 4.0 2.0], + "a2x2x2" => cat([1.0 3.0; 4.0 2.0], [1.0 2.0; 3.0 4.0]; dims=3), + "empty" => zeros(0, 0), + "string" => "string" + ) + check("array.mat", result) + + result = Dict( + "cell" => Any[v for _ in 1:1, + v in (1.0, 2.01, "string", Any["string1" "string2"])] + ) + check("cell.mat", result) + + result = Dict( + "s" => Dict{String,Any}( + "a" => 1.0, + "b" => [1.0 2.0], + "c" => [1.0 2.0 3.0] + ), + "s2" => Dict{String,Any}("a" => Any[1.0 2.0]) + ) + check("struct.mat", result) + + result = Dict( + "logical" => false, + "logical_mat" => [ + true false false + false true false + true false false + ] + ) + check("logical.mat", result) + + result = Dict( + "empty_cells" => Any[v for _ in 1:1, v in (zeros(0, 0), "test", zeros(0, 0))] + ) + check("empty_cells.mat", result) + + result = Dict( + "sparse_empty" => sparse(Matrix{Float64}(undef, 0, 0)), + "sparse_eye" => sparse(1.0I, 20, 20), + "sparse_logical" => SparseMatrixCSC{Bool,Int}(5, 5, [1:6;], [1:5;], fill(true, 5)), + "sparse_random" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]), + "sparse_complex" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]*(1. + 1.0im)), + "sparse_zeros" => SparseMatrixCSC(20, 20, ones(Int, 21), Int[], Float64[]) + ) + check("sparse.mat", result) + + matfile = matopen("partial.mat") + var1 = read(matfile, "var1") + @assert var1[28, 33] == 5 + var2 = read(matfile, "var2") + @assert var2[27, 90] == 10 + close(matfile) - result = Dict( - "empty_cells" => Any[v for _ in 1:1, v in (zeros(0, 0), "test", zeros(0, 0))] - ) - check("empty_cells.mat", result) + end +end +@testset "testing bigEndian" begin result = Dict( - "sparse_empty" => sparse(Matrix{Float64}(undef, 0, 0)), - "sparse_eye" => sparse(1.0I, 20, 20), - "sparse_logical" => SparseMatrixCSC{Bool,Int}(5, 5, [1:6;], [1:5;], fill(true, 5)), - "sparse_random" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]), - "sparse_complex" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]*(1. + 1.0im)), - "sparse_zeros" => SparseMatrixCSC(20, 20, ones(Int, 21), Int[], Float64[]) + "index" => [8.8604784000000000e+04 9.8707212000000000e+04 1.0394035200000000e+05 1.1429712000000000e+05 1.5474923999999999e+05 1.5475435200000001e+05 1.5501074400000001e+05 1.5505315200000000e+05 1.5505718400000001e+05 1.5506168400000001e+05 1.5506524799999999e+05 5.4945741599999997e+05 5.6345896799999999e+05 5.9956981200000003e+05 7.0691623199999996e+05 7.9063657200000004e+05 8.4311938800000004e+05 9.2225131200000003e+05 1.1248994160000000e+06 1.2508148520000000e+06 1.4164141320000000e+06 1.4275988280000000e+06 1.4744331000000001e+06 1.4982212879999999e+06 1.5549058440000000e+06 1.5870300840000000e+06 1.6192005120000001e+06 1.6766071560000000e+06 1.9386816839999999e+06 1.9969427879999999e+06 2.0021861880000001e+06 2.3272494120000000e+06 2.5309351080000000e+06 2.6743788720000000e+06], + "spikes" => [ + -3.9146236245031032e+00 -6.7657651330021364e+00 -1.0780027188484372e+01 -1.4345619557780790e+01 -1.5488781013877338e+01 -1.3241531877846004e+01 -8.6339302778751907e+00 -4.1571900578409995e+00 -1.4845719040296610e+00 2.3147400250828232e-01 2.8917910181412778e+00 6.4067867244186800e+00 8.3368575385567603e+00 7.0732985406218223e+00 4.4095174940268036e+00 3.8495932342509342e+00 7.0605464919276546e+00 1.2892731012948772e+01 1.8593404980539656e+01 2.1332908128411184e+01 2.0142332517120792e+01 1.6740473413471157e+01 1.3650330377340575e+01 1.1913871749214691e+01 1.0804794411826084e+01 8.8366401987297127e+00 5.1092331749990514e+00 5.1218216653980408e-01 -2.9327647633922682e+00 -4.4870896208146753e+00 -5.0598199463728655e+00 -4.8330524336350118e+00 -2.8556000012645000e+00 2.9794817723619027e-01 1.8265416505730325e+00 -8.6155940979615875e-02 -3.9623352473810947e+00 -6.9070013227561047e+00 -7.3941131196997647e+00 -5.7411207637544166e+00 -3.2366812420300106e+00 -1.1460492068000723e+00 1.2381260731009580e-01 1.0930145325605314e+00 2.1927876983540933e+00 2.6570284430776856e+00 1.3381366125210661e+00 -1.2539624260623763e+00 -3.3642620416729994e+00 -4.1849749207505456e+00 -3.8760400918509301e+00 -2.6869552030388291e+00 -1.6718246062697015e+00 -2.3709942853677934e+00 -4.6623835517993664e+00 -6.6575320887201714e+00 -6.9891263747717174e+00 -5.7017039068420186e+00 -3.4759011423153079e+00 -1.7092931352045238e+00 -2.3854494206243695e+00 -5.8068462168496913e+00 -9.1001745572212531e+00 -8.8479323560036516e+00 + -5.0745139212223540e+00 -9.6338046242625506e+00 -1.3472006614220170e+01 -1.5245910823426385e+01 -1.5914718584705716e+01 -1.6397777086548157e+01 -1.5688652912166024e+01 -1.2863641020969972e+01 -9.5044110719151487e+00 -8.3463890698794305e+00 -1.0177024989874276e+01 -1.3445743563344164e+01 -1.6692449999817043e+01 -1.9680052373752346e+01 -2.0121942799899024e+01 -8.3700572911264981e+00 3.0032951243048100e+01 9.4582543108269547e+01 1.5377989075611117e+02 1.7130866838277802e+02 1.4748486763605970e+02 1.0947277486760775e+02 7.2684596239613015e+01 3.8193102601464858e+01 1.0540246012312117e+01 -3.8091287155762972e+00 -5.9643997244651556e+00 -3.0829122045048947e+00 -9.3969579584981044e-01 -4.1011214330806744e-01 1.1485354724913588e-01 8.3209939369519947e-01 1.7132389429717065e-03 -3.6057351605809611e+00 -8.8203434292989460e+00 -1.2692989286632969e+01 -1.3074091545751195e+01 -1.0580269175201330e+01 -7.7262110976707312e+00 -6.1406117115548504e+00 -5.0818589866084025e+00 -3.3150901769832091e+00 -1.7731765969545847e+00 -2.5627661695922046e+00 -5.6404917366725886e+00 -8.3842617579066747e+00 -8.5769172149145199e+00 -6.8248629640086911e+00 -5.7747794329330571e+00 -7.1063437611930578e+00 -9.7162880960954556e+00 -1.1170428842658552e+01 -1.0378119546974839e+01 -8.3419883744685741e+00 -6.9209209230224635e+00 -7.4145555946251820e+00 -9.6713314325255784e+00 -1.2062946758371979e+01 -1.2965017154593347e+01 -1.2408773058757985e+01 -1.1624355346583785e+01 -1.1127849526328186e+01 -1.0048811168388408e+01 -7.6327226475166228e+00 + -3.6172800577681850e+00 -3.0496107525626650e+00 -4.0450961689646379e+00 -4.9841853952779340e+00 -4.3137243411079638e+00 -2.4211699563520748e+00 -9.3469136675988018e-01 -9.3834475312754062e-01 -2.3043613016166664e+00 -3.9905808684096256e+00 -4.6842071022425413e+00 -3.8310453992051836e+00 -2.8818230979207713e+00 -5.2038837297130556e+00 -1.1725055393804819e+01 -1.3208091586845086e+01 9.2482123494256854e+00 6.3181823866266974e+01 1.2177486062412244e+02 1.4070171740815530e+02 1.0609880427064739e+02 4.8491974791356185e+01 5.8467389218168400e+00 -9.6802170255950291e+00 -8.6605173391900614e+00 -5.1555616199626577e+00 -5.4237382663488169e+00 -8.2575373220146044e+00 -1.0156241701964554e+01 -9.1574620977201118e+00 -5.6665700132115431e+00 -1.5504659563007614e+00 1.1334383183944798e+00 1.2640431009400714e+00 -6.6794751018025034e-01 -2.9543901520038176e+00 -4.1963329340429310e+00 -4.3642482086638417e+00 -4.0042244694146101e+00 -3.1394295409389494e+00 -1.8179756080601912e+00 -9.3024527470805374e-01 -1.4736656790371958e+00 -3.4645773489091209e+00 -5.8588062538620047e+00 -7.0734948204842896e+00 -6.0481893429370261e+00 -3.3344016972431088e+00 -4.8650896484328832e-01 1.6052150099271567e+00 2.9838901666777322e+00 3.7836445807114574e+00 3.9345735120816623e+00 3.1644840267225094e+00 1.0070686684300534e+00 -2.4713922778659478e+00 -5.9984216012494951e+00 -7.8885829262850233e+00 -7.5154376078825695e+00 -5.8326333185237758e+00 -4.5003931071444478e+00 -4.2328547992043450e+00 -4.1140676583157889e+00 -3.1460951038002301e+00 + -3.6781701228839154e+01 -3.7303084834807457e+01 -3.8884510028175633e+01 -4.1148035399962872e+01 -4.3316242853459642e+01 -4.4384384580854572e+01 -4.4790855343692897e+01 -4.7038755365884079e+01 -5.2501365276546458e+01 -5.6812084874163638e+01 -4.9570438054352273e+01 -2.3981805280442412e+01 1.1015352613446286e+01 3.7025927055247365e+01 4.6196840812364755e+01 4.5435239856395953e+01 4.4108776047185387e+01 4.5957015883875258e+01 4.9338962205718175e+01 5.0907715343642117e+01 4.9487314118838199e+01 4.6376644839165742e+01 4.2907993314061748e+01 3.9175485818078528e+01 3.4646364450142663e+01 2.9438295855526434e+01 2.5290001942034166e+01 2.4585557992414284e+01 2.7869881512937130e+01 3.2856199330415350e+01 3.6123557386279117e+01 3.5905020129260436e+01 3.3291843240497656e+01 3.0657151735220140e+01 2.9399978478943620e+01 2.9238216866099457e+01 2.8701738303043562e+01 2.6077460100630137e+01 2.0689205683518288e+01 1.3779281580459998e+01 8.1712538443203133e+00 6.6925061080176569e+00 1.0147005424138726e+01 1.6509827576474752e+01 2.2276297357800129e+01 2.4765444980789738e+01 2.3565808601878054e+01 2.0365852060539318e+01 1.7482427576467405e+01 1.5922922611993604e+01 1.4534460949429915e+01 1.1655908219543699e+01 7.5216074902940893e+00 3.9334852970087364e+00 1.7985868168036778e+00 3.9751100517608062e-01 -9.6689135481901367e-01 -1.8291316756437888e+00 -1.2108732556384698e+00 1.1422814255806824e+00 4.0827052431935931e+00 5.6696392298883893e+00 4.7889899662105178e+00 1.9874261259261794e+00 + -1.1686614851500112e+00 9.8124871505894706e-01 2.4682927864975457e+00 2.9222372863110442e+00 1.6794130795786746e+00 -6.8820557107116076e-01 -2.4491510090825823e+00 -2.5573541785944220e+00 -1.5657335653505560e+00 -6.2326635393824792e-01 7.5542615186640072e-03 1.2160520506156904e+00 3.7959025732449647e+00 7.1695274203178112e+00 9.8085862745466752e+00 1.1024667458560756e+01 1.1849030121520164e+01 1.3645183550697372e+01 1.6153045543872672e+01 1.7653460112285362e+01 1.6917192047304358e+01 1.4287778990065465e+01 1.1181854556421472e+01 9.2813981144484536e+00 9.6980182244576554e+00 1.1975656236851366e+01 1.4218362205978007e+01 1.4823189770395057e+01 1.3782281414778801e+01 1.2224714159620135e+01 1.1308817315730568e+01 1.1413207645545857e+01 1.1802812942398777e+01 1.1304006120772028e+01 9.4673823725009267e+00 6.7674104407290443e+00 3.9034391463718467e+00 1.0696510964445158e+00 -2.2265622627917740e+00 -6.1619053880194183e+00 -9.4895708918063448e+00 -1.0511848180773416e+01 -9.2016379963305752e+00 -7.3655072801953869e+00 -6.6664600174160604e+00 -7.2291846775841488e+00 -8.2107481715781105e+00 -9.1021811818757321e+00 -1.0311278259980540e+01 -1.2741511638678721e+01 -1.6437776102727412e+01 -1.9514979330779195e+01 -1.9610190285680936e+01 -1.6644297020966704e+01 -1.2800100439249650e+01 -9.9046719354109900e+00 -8.2923627062655303e+00 -7.8966764067275435e+00 -8.8257663696547084e+00 -1.0735616318501446e+01 -1.2599202496900540e+01 -1.3220431400502429e+01 -1.1920594647006027e+01 -9.3084512934042376e+00 + 1.1099942135772691e+01 1.0097197127580243e+01 1.1065906114342178e+01 1.3374123078938322e+01 1.6449533911253752e+01 2.0017355902687680e+01 2.3638247141687746e+01 2.6292880854006093e+01 2.6783333166190332e+01 2.5183008648660202e+01 2.3296988595385344e+01 2.2810464758877668e+01 2.3624918756652875e+01 2.4607374587222779e+01 2.5393398749862719e+01 2.6769469521678889e+01 2.9281995275221178e+01 3.2213436799025317e+01 3.4285343581139983e+01 3.4785296465751173e+01 3.3883822298572078e+01 3.2386332385252516e+01 3.1231943637716320e+01 3.0932840342758819e+01 3.1246878043151604e+01 3.1160302181567783e+01 2.9550070933834700e+01 2.6540966708675843e+01 2.3785330200047358e+01 2.2611886801417548e+01 2.2531515536471172e+01 2.2340044415310590e+01 2.1822535590221452e+01 2.1399364443416083e+01 2.0755992097134378e+01 1.8948315967557182e+01 1.5769917402888659e+01 1.2205733819672014e+01 9.4099541981525228e+00 7.7046512181258402e+00 6.5719664417242711e+00 5.2343013227680553e+00 3.3389338216778657e+00 1.2573655827507051e+00 -5.0199990120897509e-01 -1.8987019795142805e+00 -2.9172592054133535e+00 -3.2152176994632509e+00 -2.9689210401896537e+00 -3.2720957493420673e+00 -4.8582690799070640e+00 -7.1594797906170751e+00 -9.1803890906391956e+00 -1.0495134027231670e+01 -1.1056911353417380e+01 -1.0726534970818260e+01 -9.3753223680626547e+00 -7.2907274381158134e+00 -5.1694262729300959e+00 -3.7312476583791785e+00 -3.6141234953544066e+00 -5.1662008322483448e+00 -7.7776079749577836e+00 -9.8010587078412321e+00 + -1.4685530727984641e+01 -1.0292328330297664e+01 -6.2040173116488546e+00 -2.3375015307800595e+00 1.3923917658511158e+00 5.0564769748901313e+00 8.6976966359851104e+00 1.2323245348083425e+01 1.5903913125353689e+01 1.9380777962659089e+01 2.2677898949762564e+01 2.5718454097742246e+01 2.8441073995486732e+01 3.0813082951470030e+01 3.2837981366611231e+01 3.4555661605993883e+01 3.6035311542317494e+01 3.7362431137561401e+01 3.8622571934083787e+01 3.9885068827368393e+01 4.1190035284914742e+01 4.2541236867236606e+01 4.3906275503545629e+01 4.5224046329587310e+01 4.6417965861852949e+01 4.7412309197387181e+01 4.8148368395523121e+01 4.8597180088665191e+01 4.8766260494684808e+01 4.8698991553936125e+01 4.8466780378761598e+01 4.8155568154983840e+01 4.7849401563031734e+01 4.7614370946271123e+01 4.7486145888939269e+01 4.7463615344086584e+01 4.7509911501785595e+01 4.7560611227134473e+01 4.7537464942025593e+01 4.7364890774312840e+01 4.6985915393397903e+01 4.6374354157541411e+01 4.5540779674504876e+01 4.4531077464674446e+01 4.3417878743397615e+01 4.2286593389902350e+01 4.1218852636871070e+01 4.0276692303289153e+01 3.9490658602725233e+01 3.8854229591844380e+01 3.8325674875439489e+01 3.7836979823323738e+01 3.7308039311521902e+01 3.6663265807716996e+01 3.5847270787923392e+01 3.4836464766174224e+01 3.3644242194420237e+01 3.2318708010392434e+01 3.0933403069436942e+01 2.9572894236760884e+01 2.8316128161485977e+01 2.7220897815058663e+01 2.6312547181929837e+01 2.5579187011419549e+01 + 1.5763956152315169e+01 1.6523243082892250e+01 1.7337427631570872e+01 1.8120936638484590e+01 1.8792445157811912e+01 1.9291006994381139e+01 1.9588077541817789e+01 1.9693009891213325e+01 1.9650918197573489e+01 1.9533337877188348e+01 1.9423561429962543e+01 1.9399599967723205e+01 1.9518198338118580e+01 1.9803119911795868e+01 2.0240058237539227e+01 2.0779199633821964e+01 2.1344921568275133e+01 2.1850676042659646e+01 2.2216063986224665e+01 2.2382665762479274e+01 2.2325443040524132e+01 2.2057418172501748e+01 2.1626689639781716e+01 2.1106384004760407e+01 2.0579565930487874e+01 2.0122142297473431e+01 1.9787200170220586e+01 1.9593930129327656e+01 1.9523364223698717e+01 1.9521786868823959e+01 1.9511132956806740e+01 1.9404280892123200e+01 1.9122163091585080e+01 1.8609252789360340e+01 1.7844318015586811e+01 1.6844290518050197e+01 1.5660474424296631e+01 1.4367827117720619e+01 1.3049394428891683e+01 1.1778975433290963e+01 1.0605636123380648e+01 9.5436591203415890e+00 8.5705174399266060e+00 7.6329046830511684e+00 6.6569045386579333e+00 5.5557424579780292e+00 4.2330232952205966e+00 2.5948387638090815e+00 6.0010139846456756e-01 -1.6403240489988979e+00 -3.7983903715983196e+00 -5.5143675763583140e+00 -7.0197817000212250e+00 -9.2991809829904906e+00 -1.2710108289977208e+01 -1.5773321560596763e+01 -1.6492611312664831e+01 -1.5291065260668367e+01 -1.5553348127486492e+01 -1.9596399167644933e+01 -2.5711586491024381e+01 -3.1294712703031326e+01 -3.6140274982880157e+01 -4.0736526948594026e+01 + -7.5455089032603496e+01 -8.8091850484650791e+01 -7.6894847494814442e+01 -5.1931618057453484e+01 -2.8833831644552824e+01 -6.8895988088626297e+00 2.1168348264522379e+01 4.6693994244353583e+01 5.3777387223823958e+01 4.4603130335345831e+01 3.3530654869558120e+01 2.3587826739136968e+01 8.4238787409792160e+00 -9.2318222698507917e+00 -1.4713136414898660e+01 1.9717165915666754e+00 3.1968641163889899e+01 5.7269930354848498e+01 7.0152688867705734e+01 7.3409072049238773e+01 7.0166235593951257e+01 6.1829204326045371e+01 4.6696134605544373e+01 1.8622054473547621e+01 -2.2829591964997910e+01 -6.2715671170700787e+01 -8.2653742502672856e+01 -8.1088328703707077e+01 -7.2961806812740548e+01 -7.3632394853923415e+01 -8.9841480609702060e+01 -1.1962228131535032e+02 -1.5066572610100792e+02 -1.6038121312642346e+02 -1.2981471355911390e+02 -6.3653163261418783e+01 9.3811209125033130e+00 5.8695741367623484e+01 7.0437047869761585e+01 4.9630793312944938e+01 1.6599415269056415e+01 -2.5402904227425278e+00 4.0559101821793178e+00 2.4429205550104435e+01 3.9021358713588448e+01 3.8520783430674051e+01 1.9952697815867261e+01 -2.1877257859243720e+01 -8.3907527060881065e+01 -1.4250639755111564e+02 -1.6619398657243511e+02 -1.3819218483975862e+02 -6.6785267226843359e+01 7.8497452778842174e+00 2.7663034892382331e+01 -1.9252281399357905e+01 -5.9646303432603780e+01 -1.8852103558667594e+01 6.7639555235119275e+01 9.6932460076734856e+01 4.1262889176984515e+01 -3.3610639804651925e+01 -7.8423083296187542e+01 -1.0839727633754131e+02 + 4.7736577112711117e+01 4.5700337234538978e+01 4.5322631494156710e+01 4.9171444949072850e+01 5.3757473474671947e+01 5.4141589236927501e+01 4.8790940853843097e+01 4.1857477273482019e+01 4.0754533608850998e+01 4.8608027316167508e+01 6.0100216178867996e+01 6.8180296544356494e+01 7.1421072557515430e+01 7.0807306032014466e+01 6.6514973934144194e+01 6.2945074970931898e+01 6.8196514720480167e+01 8.5012727237677268e+01 1.0720726826115141e+02 1.1957256746876999e+02 9.9138254538272335e+01 3.8069276174166163e+01 -2.7233524821672358e+01 -3.9334632563706222e+01 1.3886634524961096e+01 7.3584276961424322e+01 6.3628518198466388e+01 -2.9054572516934122e+01 -1.4209888641029568e+02 -2.0910522987863314e+02 -2.1415046392049794e+02 -1.7586701353513934e+02 -1.1844102599759782e+02 -6.1431963885049129e+01 -1.4806030025628601e+01 1.4027944301627443e+01 1.0652586257860358e+01 -3.0054086554043813e+01 -8.5072361204847368e+01 -1.2046643138190960e+02 -1.2106054250546094e+02 -9.5841527604366775e+01 -6.6055322458778861e+01 -4.4458770387691487e+01 -2.4336370991880798e+01 4.5840797349366902e-01 2.1040288319943066e+01 2.3611095638912182e+01 1.3051021255932143e+00 -2.8851154140515209e+01 -3.7619486391456242e+01 -2.9915658771811749e+01 -3.0708973922309450e+01 -3.1924567737368655e+01 -1.0202022943830535e+01 2.9706495601613049e+01 5.9067239546813376e+01 5.4702088800913174e+01 2.8343487643024787e+01 1.5951389347675942e+01 2.2837902575396686e+01 1.2908948645547047e+01 -3.3255358730535995e+01 -8.7746125812653432e+01 + -8.2377418542911354e+00 -3.4972931824407372e+00 -7.7569626244497876e+00 -1.5166206734460895e+01 -1.5445771078633909e+01 -5.2413228186791176e+00 8.4387175092257500e+00 1.5431757026108208e+01 1.2399978335869392e+01 7.4095099729504046e+00 1.0679579816928053e+01 1.8701188652942292e+01 1.7701213689196198e+01 4.8431711642004913e+00 -4.7779662604481938e+00 3.1723005655630221e+00 2.4531952072999687e+01 4.5698045473740862e+01 5.9257083049327122e+01 6.2627426268528140e+01 5.7411981420461260e+01 5.2570470576359099e+01 5.2813113343401156e+01 5.0453507704104304e+01 3.9691119012785791e+01 2.6528767918502940e+01 1.8741299694240368e+01 1.7724441294399568e+01 2.0821175647900695e+01 2.4417659444952996e+01 2.5870167533339281e+01 2.4983092349575369e+01 2.2998436116876093e+01 2.0558711472598521e+01 1.7502822279045098e+01 1.3739438787271743e+01 9.9370928813924273e+00 7.9696531198819107e+00 9.3237074864808100e+00 1.2496048137913172e+01 1.4202138161523894e+01 1.3776871923186704e+01 1.4057294551750237e+01 1.6919973545359703e+01 2.0190481439159552e+01 2.0371692873037446e+01 1.6567959095411027e+01 1.0520230640011906e+01 4.5794135735479564e+00 8.7710329733688541e-01 8.9182399282027713e-01 4.3463285075996909e+00 8.7315858121978973e+00 1.0903936631346470e+01 1.0288756426506817e+01 1.0049725699443105e+01 1.3054576528907717e+01 1.6854021523448839e+01 1.6041035800270304e+01 1.0367866836597134e+01 6.4265096090680434e+00 9.4949675891987368e+00 1.6836520945082036e+01 2.1325105933919993e+01 + -4.1679158148256432e+00 -5.2810404688221357e+00 -5.9038977834292563e+00 -6.2634044553555412e+00 -5.9749855839469195e+00 -4.3218694766614334e+00 -1.1788772851158797e+00 2.4374543533437842e+00 4.8050829080472326e+00 4.8908091608309068e+00 3.3699448624872437e+00 1.8764385857350361e+00 1.0658937699825455e+00 -2.7882253437288551e-01 -3.1356733669314134e+00 -4.1284337659893531e+00 4.9241012162347957e+00 2.8218865342091348e+01 5.5056325524296241e+01 6.5099843979883218e+01 4.9609447627592324e+01 2.1423693795580775e+01 2.5132863903596458e-01 -5.8604600478907667e+00 -2.3380828579082129e+00 1.9600991106694594e+00 2.5915747007003080e+00 2.0190047306038350e-01 -2.8577160575959448e+00 -5.2869354732703258e+00 -6.9099491413979388e+00 -7.7559496509908179e+00 -8.0339926236897785e+00 -8.1760789963289717e+00 -7.9859360445805283e+00 -6.3703713820186918e+00 -3.0321001946270005e+00 -2.8201117047924162e-02 -7.8351721876626979e-02 -3.2229219827610613e+00 -6.6022607253341592e+00 -7.6428337012652356e+00 -6.3520480710327867e+00 -4.5468067925800151e+00 -3.6792489626697655e+00 -3.7265752054412307e+00 -3.8104694479464527e+00 -3.1502606288908388e+00 -1.5302095841423975e+00 2.6517264914271876e-01 7.6568764912213050e-01 -5.6131323023144386e-01 -2.6412705089170849e+00 -4.2923899829303380e+00 -5.1769894068210993e+00 -4.9614565332821146e+00 -3.2859163281387294e+00 -1.1703534382204590e+00 -7.6910847698287388e-01 -2.6122681986842187e+00 -4.2742848318737003e+00 -3.0739009488926641e+00 6.8120601334287323e-01 3.9336266256502612e+00 + -4.6484775115089541e+00 -8.3581561181698660e+00 -1.0902142569692860e+01 -1.1511797760460409e+01 -9.8276834132977005e+00 -6.2404161549022161e+00 -1.8432175810231679e+00 1.9347492334298952e+00 4.2938226787082971e+00 5.6566425031547753e+00 6.9370739014200060e+00 8.7323889596425897e+00 1.1151007876295226e+01 1.3469254774199312e+01 1.4378076971481226e+01 1.3628707243729355e+01 1.2810890536991595e+01 1.3603571552449427e+01 1.5792969414511386e+01 1.7425203016009618e+01 1.6493276434801135e+01 1.2586445678344095e+01 7.3401463249477024e+00 3.2087842017129540e+00 1.5030100447830739e+00 1.3898465906994972e+00 8.8198090743093560e-01 -1.0493063115005812e+00 -3.7608545130075726e+00 -6.3949540211998350e+00 -9.0066585358720026e+00 -1.1745588278397154e+01 -1.3842632605375675e+01 -1.4123365855042982e+01 -1.2335802612795424e+01 -9.4739532170207532e+00 -6.3782197090918142e+00 -2.8832164731098446e+00 4.8447063694123349e-01 1.6128314119847449e+00 -4.1774621162461167e-01 -3.3629973208103769e+00 -4.5025431650456200e+00 -3.9290643942165806e+00 -3.7022642550448306e+00 -4.6991587651781321e+00 -5.6909794907882132e+00 -5.2906700399814426e+00 -3.7661436808309654e+00 -2.5302504318642383e+00 -2.0969878128246422e+00 -1.3265870686252779e+00 7.0961290345922357e-01 3.0385814819877943e+00 3.9548508946305954e+00 2.9954312845408939e+00 9.9988805713822959e-01 -9.2502312206375614e-01 -2.0384940259267319e+00 -2.2297243620823197e+00 -2.0769146009998161e+00 -2.3170471037592764e+00 -2.8506427004880215e+00 -2.6777747842245225e+00 + -7.7992155184377432e-01 8.4208602530413512e-01 2.4887283142722940e+00 2.4338293029048339e+00 8.3831805409641036e-01 -5.6099977991794492e-01 -7.1266715703072170e-01 -2.3092906084045711e-01 1.6955881483329160e-01 8.8504105274391720e-01 2.2423969436547071e+00 3.3863034262368776e+00 3.6616215908416012e+00 4.2752627336864073e+00 7.0581057720790223e+00 1.1758833658720096e+01 1.6047422019307188e+01 1.8405075233788665e+01 1.9427396781993370e+01 1.9879444229356857e+01 1.9348574654086402e+01 1.7629540788742673e+01 1.5792875449818970e+01 1.4872230427632566e+01 1.4471792816861928e+01 1.3426898347317799e+01 1.1319456367524939e+01 8.8575138135993203e+00 7.0846315948436089e+00 6.5127944804059057e+00 6.6781793515550252e+00 6.2758575263602134e+00 4.2213211609500574e+00 1.0187928511802555e+00 -1.3365602410322484e+00 -1.2247622425200726e+00 1.1481723220280773e+00 4.3321910406078903e+00 6.9648334152330520e+00 8.0154771868425438e+00 6.5719188712588483e+00 2.4474218319276826e+00 -3.0491523253799624e+00 -7.4154828913997903e+00 -8.5953677824666350e+00 -6.7813390775209372e+00 -4.2068257455106703e+00 -2.7309337921106196e+00 -2.0348235578019351e+00 -6.2767747718278888e-01 1.7379756336568186e+00 3.5393101620046572e+00 3.5444884355037840e+00 2.3746522750550279e+00 1.3500592448824880e+00 6.9976097822180139e-01 -1.5618921435797500e-01 -1.3162226599662721e+00 -2.4845798158016903e+00 -3.7797798352858396e+00 -5.2368125011457316e+00 -5.9486138818233414e+00 -5.1354371684134463e+00 -3.7528582973371494e+00 + -3.0447537175403006e+00 -1.7667805638958940e+00 -1.9778229750617011e+00 -2.2515604403315699e+00 -1.3449261643785633e+00 -1.0662626484421048e-01 -5.7657486030100791e-02 -7.4315041221388778e-01 -3.2337810363445102e-01 1.8577438758015754e+00 4.8425765941254983e+00 7.8123978114524020e+00 1.1105854493054094e+01 1.4841903991300153e+01 1.7468871344145363e+01 1.7574512951317725e+01 1.6451820522767886e+01 1.6628323973953727e+01 1.8301434258000356e+01 1.8931382166939795e+01 1.6217394592510296e+01 1.0559530299242045e+01 4.4576293834236687e+00 -1.1822470014800246e-02 -2.5727563770885062e+00 -3.7547505719161225e+00 -3.5081536657220189e+00 -1.4835065248806825e+00 1.8247576781703743e+00 4.8275703093820033e+00 5.7228574725963268e+00 3.8558888609578723e+00 4.0990146070366057e-01 -2.4574645653496310e+00 -3.1102243476353593e+00 -1.4474225309731716e+00 7.4127875680854416e-01 1.0383069562131020e+00 -1.0150232301722759e+00 -3.4198210424706135e+00 -4.3088303898183238e+00 -3.9165170232756070e+00 -3.6605958183355347e+00 -4.2469618414480417e+00 -5.2237494007048566e+00 -5.9420620236257076e+00 -6.1698135259889568e+00 -5.7089733374935268e+00 -4.0492707065696969e+00 -8.0849384208890562e-01 3.3216805308527921e+00 6.2990262694136261e+00 6.1812329879916819e+00 3.1266169114028388e+00 -4.8505368345761951e-01 -2.2625885070785667e+00 -2.0371523965480165e+00 -1.4033783831712108e+00 -1.2651726669536290e+00 -8.7131680693218405e-01 5.0106877171388586e-01 1.8406747295741790e+00 1.3085085083594870e+00 -1.5293730324086736e+00 + 2.3513445326289961e+00 1.7384880477187443e+00 2.2184897775735659e+00 5.2051223974837342e+00 1.0452816121087638e+01 1.5377084182445486e+01 1.7198975360259148e+01 1.6104713320124159e+01 1.4833420837246866e+01 1.5100670860303905e+01 1.6073099350257220e+01 1.6269109488250638e+01 1.5637757524959511e+01 1.5476499170789715e+01 1.6604024256326596e+01 1.8317676582762779e+01 1.9829075755286041e+01 2.1816151151864332e+01 2.5055347925633036e+01 2.7333907737059032e+01 2.2427805378565086e+01 3.7141556286936721e+00 -2.8648138173116212e+01 -6.3843957225821207e+01 -8.7089860614432553e+01 -9.1923311472937954e+01 -8.3938126428996938e+01 -7.2885847777427841e+01 -6.4297677965088411e+01 -5.7914366258401401e+01 -5.0470379597131561e+01 -3.9499120538780375e+01 -2.5751695307781368e+01 -1.1778187088014317e+01 1.0189820693630494e+00 1.1960524761686607e+01 1.8733303083800365e+01 1.8356414318508428e+01 1.0668647686854321e+01 -1.0075011878329772e+00 -1.2633619248121462e+01 -2.1404683668367543e+01 -2.5602377289037388e+01 -2.4560926814716566e+01 -1.8847733387633440e+01 -9.8404336731293878e+00 3.5966958558048301e-01 9.0662935627188972e+00 1.4335829915263089e+01 1.6036694595613060e+01 1.5184337823870287e+01 1.2469246394512929e+01 7.6677145828817306e+00 8.5165822752424969e-01 -5.8806346340252293e+00 -9.1084583818696743e+00 -7.2999534121027425e+00 -2.4813921667292815e+00 1.7215656085634032e+00 3.0197896060174911e+00 1.4579843578258131e+00 -1.6420102258369735e+00 -4.8732983588159042e+00 -6.9044980772313806e+00 + -7.1748224092297468e+00 -3.8936102456845245e+00 1.0743831294909865e+00 4.8816301487736782e+00 5.4189196440465226e+00 2.8421806913201926e+00 -7.8193972997551731e-01 -3.3953855854612938e+00 -3.9985877326731809e+00 -2.4284915455410818e+00 3.3095855413398589e-01 2.1860065858780535e+00 1.8280988855495470e+00 3.4048799490454618e-01 3.8790118108287441e-01 3.8556647578240639e+00 9.9718322097927121e+00 1.5912297294502212e+01 1.9511397698602284e+01 2.0651366980982488e+01 2.0134282655984631e+01 1.8560445836154724e+01 1.6287447146155721e+01 1.3561600277652611e+01 1.0795264078502715e+01 8.6030785401577266e+00 7.0022103875078896e+00 5.1888911547538754e+00 2.7145704050730384e+00 1.4143845469113936e-01 -1.9107456843838713e+00 -3.4559243419646326e+00 -4.2223452047916581e+00 -3.1130434517080205e+00 1.7183260785086762e-01 3.7869557639525033e+00 5.3380153379310666e+00 4.2352058165010487e+00 1.6573488592368961e+00 -8.5887188564406203e-01 -2.1681503131724709e+00 -2.1279908375294667e+00 -2.0792749208808359e+00 -3.3997834567720187e+00 -5.3580052019440032e+00 -5.9792374734377622e+00 -4.8403202446726707e+00 -3.3487890303481347e+00 -2.6600981462061033e+00 -2.7228934357253367e+00 -3.1308053901450172e+00 -3.8963449071942557e+00 -5.2729284759196471e+00 -7.2764195946828067e+00 -9.3283947520693822e+00 -1.0227824562298673e+01 -9.0739885142293417e+00 -6.5681575524140818e+00 -4.8038541028704778e+00 -5.3349275900632449e+00 -7.6025577687023391e+00 -9.3130362450711317e+00 -8.3152888687476345e+00 -4.2484255376825795e+00 + -1.2094820634316039e+00 -1.2706817164948352e+00 -1.2775018026388452e+00 -2.3357176552894954e-01 1.8928499829725349e+00 4.4677863928736414e+00 6.9513585826945681e+00 9.0204123867514259e+00 1.0482452392061710e+01 1.1455309752506835e+01 1.2237810752650413e+01 1.2688637793112969e+01 1.2235313891126804e+01 1.1100297213735251e+01 1.0773414547739115e+01 1.2217993905654721e+01 1.4701466325393794e+01 1.7319490559451381e+01 1.9808972202344144e+01 2.1257689253929254e+01 2.0368628657184544e+01 1.7524563363309646e+01 1.4843567969750833e+01 1.3863574192388871e+01 1.4031045614008303e+01 1.3693669004482535e+01 1.2122436338523961e+01 9.9467781708780620e+00 7.8498090003437868e+00 5.7967683127528629e+00 3.7206536395890204e+00 2.3267777177674382e+00 2.6303789001713365e+00 4.7445695138569235e+00 7.6683738268993942e+00 9.9717476628589878e+00 1.0271279571889663e+01 8.1958760665984958e+00 5.3998213275104048e+00 4.2899793853628481e+00 5.5462911375305124e+00 7.6595253723593695e+00 8.4981448223169700e+00 7.2294579833209385e+00 5.1143564806110922e+00 3.8260968045635870e+00 3.3065755059745818e+00 2.5815464286120089e+00 1.7488042411202225e+00 1.5184709880337026e+00 1.6370045591241176e+00 1.1531897091681369e+00 -1.5863929796862442e-01 -1.5759013113892080e+00 -2.3911490323759002e+00 -2.4147368159491429e+00 -1.8749121657426793e+00 -1.4086065112641286e+00 -1.6573036686621481e+00 -2.4019595691049194e+00 -2.7197582423545761e+00 -2.3228923158464436e+00 -1.8987417992229161e+00 -1.9762436025825927e+00 + -2.1788608089091870e+00 -2.3721017929273520e+00 -2.2248744539174794e+00 -1.3562719882732486e+00 1.9303974836178317e-01 1.3110471548593794e+00 1.2797866409946126e+00 1.1446512790093115e+00 2.4218364442454297e+00 5.3568199699566073e+00 9.1928444896243846e+00 1.2911855868338753e+01 1.5131303558516208e+01 1.4796675429288374e+01 1.2747818159896775e+01 1.1384776546745211e+01 1.2304499348204516e+01 1.5012766622841385e+01 1.7706586949808607e+01 1.8615946254339946e+01 1.7088985921306737e+01 1.4118105664819083e+01 1.1547667913836323e+01 1.0268505344072013e+01 9.5426651513383796e+00 8.3936752159092531e+00 6.7898835952875585e+00 4.9190137058105190e+00 2.3035160443938620e+00 -1.4454604882529436e+00 -5.5578141560543060e+00 -8.6285337833508056e+00 -9.8768639485094845e+00 -9.3497459396540972e+00 -7.4531759065479113e+00 -4.8343191161214989e+00 -2.2707557414183874e+00 -2.1273932165940168e-01 1.4763518504563440e+00 2.9929246806770484e+00 3.6484933755646152e+00 1.9940019916984792e+00 -2.3622776610960083e+00 -7.2558689942915287e+00 -9.3484617741468909e+00 -7.4954744905729234e+00 -3.9544592514927057e+00 -1.7075406868672076e+00 -1.4165546781975049e+00 -1.6221832630448796e+00 -1.0260840386708110e+00 1.6403282116014173e-01 6.0805828587662303e-01 -7.8558049118458273e-01 -3.6478240721938118e+00 -6.4046749238136291e+00 -7.7290809125845756e+00 -7.3840184127203816e+00 -5.7754694634749093e+00 -3.6798105136804447e+00 -2.5013142267663810e+00 -3.3639333122560284e+00 -5.7371424805126106e+00 -8.0051890648261459e+00 + -1.5740146351038696e+01 -1.5127188568935047e+01 -1.6157259030112712e+01 -1.7330034352768784e+01 -1.7263465597740716e+01 -1.6336957355980775e+01 -1.5946556228923413e+01 -1.6451747846316870e+01 -1.6476134055902989e+01 -1.4472580313892262e+01 -1.0935457247727644e+01 -7.9876368021814939e+00 -6.0265186692348891e+00 -2.0146808151690361e+00 8.0163390797194936e+00 2.5505537996776390e+01 4.7787314568418353e+01 6.8639540131510500e+01 8.1758985020608336e+01 8.5294009078541549e+01 8.2283381938562812e+01 7.6550409062053944e+01 6.9344678720567757e+01 6.0023044834365038e+01 4.8849934461625729e+01 3.8040291720134526e+01 2.9945383165919644e+01 2.5344624124046817e+01 2.3679402477458130e+01 2.3454441703713346e+01 2.2281261219059516e+01 1.7989994660541914e+01 1.0436193819072196e+01 2.1102305275848425e+00 -3.6577185659374600e+00 -5.5145847757485971e+00 -4.5324408328651318e+00 -1.8554008253732013e+00 2.7089378387782199e+00 9.2187555916040793e+00 1.5945566856263504e+01 2.0472163602212458e+01 2.2294516323769198e+01 2.2881411820375533e+01 2.2911237311157720e+01 2.1364859320014958e+01 1.7602082925628398e+01 1.2398805245993104e+01 6.8726800470759422e+00 1.6538594259187884e+00 -2.9437120155973195e+00 -6.5367981004048668e+00 -8.6437256178931250e+00 -9.1231101796868952e+00 -8.6648085339554566e+00 -8.4735847323575335e+00 -8.9963274994615468e+00 -9.3198443907122499e+00 -8.2504681130406006e+00 -5.5321797455350028e+00 -1.9690195798275383e+00 9.0573194795780632e-01 1.6386927935969133e+00 3.9409445444748448e-01 + -1.2097853394633344e+00 -2.3372661136012782e+00 -4.5868126352335468e+00 -7.7696106024369840e+00 -9.8847492594673341e+00 -8.4351485269365991e+00 -3.4815504396923176e+00 2.0318425367749775e+00 5.4688409099903446e+00 6.8155210844128202e+00 7.5289034993747936e+00 8.2470533843717533e+00 8.5005207895890198e+00 8.1730893089208134e+00 8.1217939375154362e+00 9.2472099093868128e+00 1.1682275429968531e+01 1.4843432519914167e+01 1.7625325319637877e+01 1.8827596749646286e+01 1.8262944365019820e+01 1.7257770010849043e+01 1.7139858491940043e+01 1.7319082568288064e+01 1.5832240015066093e+01 1.1889468934637401e+01 7.0260166885118247e+00 3.4084227701886709e+00 1.5903858359584313e+00 6.4628466394257855e-01 2.9208281149091886e-01 1.3680522046018677e+00 3.7148191371537109e+00 5.3471095100773640e+00 4.5999975157196396e+00 1.8618036691603201e+00 -1.1675467460557312e+00 -3.2014095470489683e+00 -4.1751621005792883e+00 -4.4441528031142692e+00 -3.7587484122597536e+00 -2.0282422842477690e+00 -4.8339177039881087e-01 -2.6711860399748077e-01 -3.8790549777032224e-01 8.5572500658487782e-01 3.0975713952792994e+00 4.3374149891908393e+00 3.2920941855788466e+00 3.9295487513328070e-01 -2.4783937608256150e+00 -3.2055601186298843e+00 -1.7160184560799154e+00 -6.3702741131976026e-01 -2.2383884758776662e+00 -5.5570116369254112e+00 -8.2084983887639105e+00 -9.5917739048230395e+00 -1.0331039070410487e+01 -1.0417944274770905e+01 -9.6648501662164410e+00 -8.3293893029282344e+00 -6.5549338022834966e+00 -4.3174230553074713e+00 + -1.9521686822118689e+01 -2.0828890952194818e+01 -2.1026487257169343e+01 -2.0644194340000958e+01 -2.0356661177644074e+01 -2.0486033910028034e+01 -2.0592315338305198e+01 -1.9336484142295909e+01 -1.5938473018577051e+01 -1.1921626681007268e+01 -9.9587671774048481e+00 -1.0450212320468967e+01 -1.0318268307692627e+01 -5.6239283444522234e+00 5.5321151308175374e+00 2.2417493712423962e+01 4.2078909484239603e+01 5.9973160754273493e+01 7.1702694488382562e+01 7.5072713982579657e+01 7.0924560394362075e+01 6.2663339872630232e+01 5.4356911122207862e+01 4.7989283986803173e+01 4.2765855626087045e+01 3.7486699590135927e+01 3.2734101027576685e+01 2.9993606241210717e+01 2.9069222310304095e+01 2.7645584279325117e+01 2.3767330134444784e+01 1.7501891196670105e+01 1.0097030989509490e+01 3.3105933448286935e+00 -4.4358692407392453e-01 4.2309373559202790e-01 4.6272362980750925e+00 9.2452802627883770e+00 1.2881829396441868e+01 1.6139200491488932e+01 1.9696338650752981e+01 2.2945254570047432e+01 2.4052713164028159e+01 2.1836262493236529e+01 1.7755371912217502e+01 1.4544324719156007e+01 1.3076166058655325e+01 1.2023365019266210e+01 9.9330468323536198e+00 6.5642773658552329e+00 2.6211689892425389e+00 -1.2463040312411424e+00 -4.6931152521918520e+00 -7.0610915480926959e+00 -7.8896098442295468e+00 -8.1377876309837323e+00 -9.2879431013150882e+00 -1.1118321847684978e+01 -1.1512230767538767e+01 -8.6570795004612968e+00 -3.0565564637606264e+00 2.6614202613936517e+00 5.7492933678780842e+00 5.1870636768943843e+00 + -1.2768528401242900e+01 -1.7338863074172309e+01 -2.0237569092156448e+01 -2.0576492513490894e+01 -1.9431123286614632e+01 -1.8641772316619317e+01 -1.8943170420368556e+01 -1.9167043655793719e+01 -1.7528873877656416e+01 -1.3999909814005159e+01 -1.0805366953545310e+01 -9.6142597372311247e+00 -8.9149462386606366e+00 -5.4471587606889269e+00 2.2906924892770721e+00 1.2948039993449356e+01 2.3895472387004183e+01 3.2785154492570712e+01 3.8224124421928316e+01 3.9996932598102731e+01 3.8917325110890907e+01 3.6404969952831500e+01 3.3603549356134771e+01 3.0482131529017348e+01 2.6418551514199979e+01 2.1753968035179085e+01 1.7865996396778279e+01 1.5605745867809237e+01 1.4445507979298107e+01 1.3294236667868901e+01 1.1519440091973062e+01 9.2364945825762419e+00 7.2532314388618229e+00 6.2602214003637382e+00 5.4987881290564387e+00 3.2320466512581985e+00 -9.0506248164303926e-01 -4.9456160363826012e+00 -6.3608335770462485e+00 -4.2734547891318062e+00 4.6309862256935785e-01 6.7021945441538548e+00 1.3111777285636606e+01 1.7514480785911530e+01 1.8248810290360364e+01 1.5766174142459235e+01 1.1757377659322158e+01 7.9733313460406592e+00 6.0274343310982506e+00 6.1770944745629279e+00 6.2936748428494491e+00 4.0368463533344734e+00 -4.2685170771143977e-02 -3.1865409383399417e+00 -3.8901205566515773e+00 -2.9576974863775090e+00 -1.4738094422502874e+00 4.0118819835881747e-01 2.6525695143418675e+00 4.6014650691595245e+00 5.1565376735843209e+00 4.0636905672697523e+00 2.6453975037372648e+00 2.3284476158012066e+00 + -1.3007839852110759e+01 -1.1025538159980380e+01 -1.0686371074924564e+01 -1.2744092667984233e+01 -1.5642265829843563e+01 -1.7140214084438682e+01 -1.6617034625505930e+01 -1.5300909110451405e+01 -1.4577809836556332e+01 -1.4955079002970214e+01 -1.5654258710135920e+01 -1.4407648124599390e+01 -9.0954740513659118e+00 -6.2982400317265430e-02 1.0332127909943164e+01 2.0193720207165487e+01 2.8972473785793127e+01 3.6317935037933722e+01 4.1210257687869564e+01 4.2631382536124164e+01 4.0972150948544574e+01 3.8156297398596216e+01 3.5717588235143509e+01 3.3207917004761363e+01 2.9304640994990272e+01 2.4112801189469973e+01 1.9353557646954098e+01 1.6308754098888908e+01 1.4516954476871140e+01 1.2392183920492100e+01 8.3965380716467521e+00 2.2326430594848903e+00 -4.3996278411324354e+00 -8.8062298459598374e+00 -9.2226405800100828e+00 -5.6733723455263254e+00 3.7975785593939992e-02 5.0496912837583618e+00 7.5061362215685907e+00 8.1297047935707027e+00 8.9880765155299258e+00 1.0960262021995606e+01 1.3066019970002715e+01 1.3738034632375317e+01 1.1896051602413694e+01 7.5099861905659733e+00 2.1853151873444245e+00 -1.4107524690116966e+00 -1.7374936024760426e+00 4.7474258642688838e-02 1.2777176662851293e+00 5.9151627918367655e-01 -9.4200465373474518e-01 -1.3835697125582160e+00 -1.1439767142326973e-02 2.0431818226269547e+00 2.8931387751496276e+00 1.7217225031925740e+00 -3.6057387583206468e-01 -1.4188595163897433e+00 -5.3401986141825453e-01 1.7331384545428845e+00 4.1028021413802733e+00 5.1530807234519207e+00 + -5.9423907462970762e+00 -6.6393370296983978e+00 -7.7903948031970467e+00 -9.0887616470108981e+00 -9.0730958856624682e+00 -7.3051960638397269e+00 -5.1092125692791504e+00 -4.1499019811620803e+00 -5.1825095304735012e+00 -7.4650822660744725e+00 -8.7858341576439987e+00 -6.9165862119378128e+00 -1.1152780425762092e+00 7.4456360751231196e+00 1.5504355240213883e+01 1.9891995742575336e+01 2.1880531164099967e+01 2.5705009499507383e+01 3.1274011045582068e+01 3.4010306181075244e+01 3.2770325333513270e+01 3.0948804502035770e+01 2.9539194246932787e+01 2.6197352223405726e+01 2.0544443634266784e+01 1.4590436095771173e+01 9.4132762585289544e+00 5.0726802461989733e+00 1.8845604581530462e+00 -2.0830751870538844e-01 -2.0248221543626945e+00 -3.9513021238798549e+00 -5.3937857883630116e+00 -5.7622214722998990e+00 -5.0964393510051575e+00 -3.6685468807689410e+00 -1.9925824037801600e+00 -1.3116773229001528e+00 -2.6369224209795075e+00 -4.8818299037998507e+00 -5.7575190263162872e+00 -4.9151597170028811e+00 -4.2580797535905948e+00 -5.3162000432783687e+00 -8.0260447395650854e+00 -1.1124603514599908e+01 -1.2372738496954803e+01 -9.9327640511974646e+00 -4.8837906274471230e+00 -8.7396451836104794e-01 -1.7270469647056230e-01 -1.5776571699924549e+00 -2.7982572746004415e+00 -3.1062409337303580e+00 -3.0845221509902201e+00 -3.0432684148630136e+00 -2.5011943998532136e+00 -1.1481969001182208e+00 3.2261647132979526e-01 7.8134574976169557e-01 -1.2641763242828769e-01 -1.4193228153058111e+00 -1.7047633076684265e+00 -8.4627935364228413e-01 + -3.3511583497758619e+00 -5.1854891048629845e+00 -8.2045548190539002e+00 -1.1321927344458468e+01 -1.3232156516018623e+01 -1.2891455905593750e+01 -1.0308983110741357e+01 -7.2770589395120400e+00 -6.3230641518260899e+00 -7.7668924711145673e+00 -8.5533116261484974e+00 -5.1754472720840532e+00 2.5464912688278725e+00 1.1150146477409665e+01 1.6749765390457195e+01 1.8459480819125350e+01 1.8853443625394785e+01 2.0968383227964456e+01 2.4745356436846293e+01 2.6900729147509413e+01 2.4586028405507879e+01 1.8423893823872788e+01 1.1687464461034605e+01 7.4694246602111161e+00 6.5063740610776568e+00 6.6399254866014452e+00 4.8727847407542608e+00 6.2948434274342469e-01 -3.8180925201354374e+00 -5.9745650071051903e+00 -5.0248037653094384e+00 -1.7240345099049890e+00 2.4707226972860665e+00 6.1789893211512830e+00 8.4606300098802265e+00 9.1837056122684562e+00 9.4175247215558571e+00 1.0361111194478536e+01 1.1355825197106681e+01 1.0443694556801294e+01 7.3275805488596522e+00 4.0729400267880624e+00 2.7849489919865249e+00 4.0449011920910980e+00 6.8903062423979726e+00 8.8579270913084418e+00 7.2409153007186635e+00 1.9815538314749910e+00 -3.4963983686766915e+00 -5.7025552070066450e+00 -4.6316183670653945e+00 -2.8570944089319230e+00 -2.1945685959883381e+00 -2.7295179820568904e+00 -4.1440094951781976e+00 -6.1739578627827907e+00 -7.9441831061071788e+00 -8.3008782660488869e+00 -7.1697557563545278e+00 -5.6642787357607363e+00 -4.5049090776747240e+00 -3.1919541675350729e+00 -9.9748944534117390e-01 1.8822644573222371e+00 + 1.6022978144703672e+00 -1.4311786252489094e+00 -4.9990745496272506e+00 -7.1229734861916816e+00 -6.0298488474363783e+00 -1.8843154305695582e+00 2.8057312397445893e+00 5.3045500670547181e+00 5.2217607939007324e+00 4.6015143428315151e+00 5.3985890315998715e+00 7.6627280386075034e+00 1.0581969356003244e+01 1.3680704869130523e+01 1.6133038629367640e+01 1.6836134617835810e+01 1.5985757935422852e+01 1.5071121283266066e+01 1.5015214850097710e+01 1.5311877473734373e+01 1.4952636894507775e+01 1.3509895518873545e+01 1.1287529167684998e+01 8.8944719667341001e+00 6.8056307961237392e+00 5.1599082109694479e+00 3.9096368310365088e+00 3.0884796567205659e+00 2.8027781380901557e+00 2.7427378963657629e+00 1.8080112317158328e+00 -7.2093739358622477e-01 -3.2256888875692162e+00 -2.5485135905025946e+00 2.1938957914977939e+00 7.8096722519713868e+00 1.0108587362109473e+01 8.0119639048454889e+00 3.7875674256494771e+00 -1.7909623605085379e-01 -3.6408942410859053e+00 -7.2043954289116119e+00 -1.0036996806105844e+01 -1.0480191286127010e+01 -8.6104715538045582e+00 -6.5737607267403781e+00 -6.4789888225111065e+00 -8.9832745466307422e+00 -1.3098938079216277e+01 -1.6488189562785699e+01 -1.7072074337181476e+01 -1.4675862164708811e+01 -1.0393673234405423e+01 -5.7706460525680994e+00 -3.2783034075657795e+00 -4.8699351422351356e+00 -9.0537498769047904e+00 -1.1681066334269010e+01 -1.0244235531230570e+01 -5.9719523689580347e+00 -1.9644630163354910e+00 -6.8837039873644690e-01 -2.7600482002062674e+00 -6.6765535392302047e+00 + -2.6793902875688205e+00 4.3700049391893547e-01 5.2207839165959946e-01 -2.5289383815408999e+00 -6.4357928708025778e+00 -8.6801895727348093e+00 -8.6519420551714941e+00 -7.5157946794804973e+00 -6.9229481363763350e+00 -7.7994153444953156e+00 -9.3675453247907754e+00 -1.0006058503692298e+01 -9.5066143246051311e+00 -9.1193248923592503e+00 -9.0487355120456403e+00 -6.9972012375818347e+00 -1.6948530860574240e-01 1.0752688787226244e+01 2.0728963668504598e+01 2.4824092328193505e+01 2.2702194691381209e+01 1.8103111981612606e+01 1.5186220180215653e+01 1.5435411462559298e+01 1.6851176785915300e+01 1.6256911281706046e+01 1.2853830842714240e+01 8.7992906000849906e+00 6.3848237032232804e+00 6.1798137367968202e+00 7.8154545356284144e+00 1.0467840599577846e+01 1.2480989360982822e+01 1.2582337684046703e+01 1.1590145096205708e+01 1.1337803770903093e+01 1.2054473739609797e+01 1.2250402949861966e+01 1.0983877637654345e+01 8.6551288071765491e+00 5.8174031977868585e+00 2.9188587271482644e+00 8.3930412496566609e-01 2.3622649323786860e-01 7.1444423031664739e-01 1.2655853456754595e+00 1.3229336834759118e+00 1.3578720434847311e+00 2.1076821170226498e+00 3.0728270343869779e+00 2.7843524321077324e+00 9.0852714062149698e-01 -1.0017936404451253e+00 -1.3974751167516584e+00 -6.4988794902308711e-01 -5.0807738982896855e-01 -2.0685431131427148e+00 -4.6510643108399936e+00 -6.4865750445095642e+00 -6.4858500824649585e+00 -5.1351447719989922e+00 -3.4051840546128562e+00 -1.7184951490663052e+00 -5.0407691071619776e-01 + -3.6981497476642828e+00 -3.0390710048817726e+00 -1.4989922553639727e+00 1.0284003810800462e+00 3.6622719317496890e+00 4.7174258831055269e+00 3.5121017080473034e+00 2.1663525068616294e+00 3.8933497703894613e+00 8.9756839635313099e+00 1.4369540972251285e+01 1.7517537513748934e+01 1.8367043951422612e+01 1.7487527428352760e+01 1.4910602011410518e+01 1.1788546361447585e+01 1.0695666486764603e+01 1.2768873027185609e+01 1.6000928788239836e+01 1.7360605935279679e+01 1.5930200170467559e+01 1.3260280784655849e+01 1.0849461771852765e+01 8.4797732256321510e+00 5.3784098602866708e+00 1.7165101724011995e+00 -1.4089618767998704e+00 -2.7952639837436752e+00 -2.2276522503149407e+00 -9.5316276326730420e-01 -5.3299313777074875e-01 -1.5019739358173170e+00 -3.5515201972803432e+00 -6.3187612986855708e+00 -9.3137337609793054e+00 -1.1318424950944941e+01 -1.0816559293228659e+01 -7.7508782461822854e+00 -4.2600195254335116e+00 -2.8712335676010809e+00 -4.2724290569942225e+00 -6.8899881431600578e+00 -8.1742447353742449e+00 -6.8082430068650392e+00 -3.9299615464428408e+00 -1.4896688083453651e+00 -4.1155002553450060e-02 6.2487647569787685e-01 -2.9020287195242966e-01 -3.8853316012394776e+00 -9.2125322820605025e+00 -1.3050926297558480e+01 -1.2737108773295276e+01 -8.7818443279864002e+00 -4.3127261410662650e+00 -2.3302722768023965e+00 -3.5325752167188584e+00 -6.2805892811261916e+00 -8.5016614679139195e+00 -9.4759820546177966e+00 -9.7204496390574704e+00 -9.7546714698047463e+00 -9.5752380341344967e+00 -9.3036539221877597e+00 + -1.7414985024478632e+01 -1.7710392269365567e+01 -1.8830345987045778e+01 -2.0905808395741552e+01 -2.3089781413981267e+01 -2.3899680760146289e+01 -2.2409641713666709e+01 -1.8930485800816182e+01 -1.4601034805586801e+01 -1.0520204067883871e+01 -7.1082264501151942e+00 -4.1771499408845667e+00 -1.2985932549142798e+00 2.5888198678848857e+00 9.5645053119594277e+00 2.1215332248500019e+01 3.6152478620496041e+01 5.0021402817940384e+01 5.8774208038896376e+01 6.1292360127806575e+01 5.8550762166546200e+01 5.1882091757242364e+01 4.2968986888033839e+01 3.3918424522864342e+01 2.6125731850668569e+01 1.9638134139516286e+01 1.3840450777647449e+01 8.3996525004490294e+00 3.8021795817073860e+00 1.1190628236490561e+00 7.5962516407305214e-01 1.4058807055741134e+00 8.2163118862213358e-01 -2.0188943881722592e+00 -6.0499550558624495e+00 -9.4755634193659208e+00 -1.1248638187248371e+01 -1.0876954767354995e+01 -7.8849814096440030e+00 -2.2277330312359691e+00 5.4138464272246747e+00 1.3796881454212489e+01 2.0984396320719696e+01 2.4893133374375935e+01 2.4942654855570076e+01 2.2326104353812248e+01 1.8202892776374178e+01 1.2706268587571685e+01 5.8859262076676249e+00 -1.1947559660524161e+00 -6.5689254319385437e+00 -8.7772879515580264e+00 -8.1899879887551386e+00 -6.5832072140625550e+00 -5.4688753261733805e+00 -4.9175403512819100e+00 -3.8699212831075047e+00 -1.6574438279084263e+00 8.5455765389835547e-01 2.0911729100665450e+00 1.8202474430112003e+00 1.6089416985684442e+00 2.9743808071641795e+00 5.8948563383564005e+00 + -3.3338514474187440e+00 -4.1130389374748058e+00 -5.1547704706642552e+00 -5.4193898987702704e+00 -4.0201652371671948e+00 -1.8076212696539353e+00 -8.9324938220319039e-01 -1.9868709786288514e+00 -3.3105995821020633e+00 -2.6515298234057667e+00 5.1903435078819071e-01 4.9231535819829615e+00 8.5469168923530585e+00 1.0071331008104004e+01 9.9283440319809362e+00 9.9281856288240444e+00 1.1689028944723646e+01 1.5180332000597723e+01 1.8495089144282854e+01 1.9468749633612084e+01 1.7774035163293483e+01 1.4795935112020807e+01 1.1632060118366278e+01 8.3107388002809923e+00 4.7969367455220766e+00 1.5228719476515731e+00 -1.2245839851149136e+00 -3.6724142837818849e+00 -6.1258400070447641e+00 -8.3043113945111795e+00 -9.2382296924864278e+00 -8.2818369343941640e+00 -6.3024540208842872e+00 -5.1153884354297716e+00 -5.4012598664097871e+00 -5.9086039260782988e+00 -5.2469498739198066e+00 -3.8701397078727497e+00 -3.4699836914403539e+00 -4.6999263838482115e+00 -6.4704904874754252e+00 -7.1253409419073517e+00 -5.1911313857217234e+00 -5.8248621615308416e-02 6.3735411041921815e+00 9.9842526040059774e+00 8.0901302020184040e+00 2.0765046689514737e+00 -4.0814624682782448e+00 -6.9335386550902367e+00 -5.3396830806898752e+00 -6.1569207664081205e-01 4.2371402928715165e+00 6.1453027828107594e+00 4.0729416305299182e+00 -5.6892224917549727e-01 -5.7266782214867291e+00 -9.4817569691134302e+00 -9.4466837493039364e+00 -4.4716815460577326e+00 2.5480231258059796e+00 6.3862725120871939e+00 4.7626827206150235e+00 2.7250354238558483e-01 + -6.6519531318265823e+00 -5.1307688760840637e+00 -1.5990450796308475e+00 3.2882196698155921e+00 7.5434887899979923e+00 9.8601744151731392e+00 1.0431338476778441e+01 1.0132207096708374e+01 9.8633193310657052e+00 1.0117849589142009e+01 1.0556320935364319e+01 1.0529237565608828e+01 1.0064399849178292e+01 9.8902096970315743e+00 1.0802538876610429e+01 1.3065613777540369e+01 1.5904751019353327e+01 1.7996878960824926e+01 1.8887262226773334e+01 1.9137392198836288e+01 1.8914272666242024e+01 1.7510508054314936e+01 1.4350155185280446e+01 9.5073777371400769e+00 3.7350791719348706e+00 -9.6044902572999469e-01 -2.1523757668680674e+00 1.2726446256922297e-01 2.7975425794223376e+00 3.1310049165812357e+00 1.4400885075259127e+00 -9.4699453877260531e-03 5.2252907539724913e-01 2.9730000242572210e+00 5.8714424054021102e+00 7.9092364851498598e+00 8.7344188791480644e+00 7.9030314193993458e+00 4.5624392185691374e+00 -5.8583551307959691e-01 -4.3527827486539321e+00 -3.8542689679266942e+00 -3.3407572539104535e-02 3.0190256438646816e+00 2.5671043830395353e+00 -2.7379787916712028e-01 -2.8159393124422332e+00 -3.9496120934673513e+00 -4.3869096765280995e+00 -4.8326892938827388e+00 -4.8327167767960031e+00 -3.6048592531173460e+00 -1.4049856452797012e+00 6.1244691414035868e-01 1.4045344432842721e+00 4.0378165896259843e-01 -2.2594911404427971e+00 -5.2758916592334257e+00 -6.6711436023807975e+00 -5.4988956630405870e+00 -2.7595297745975564e+00 -4.3083978002099643e-01 1.2310669289941012e-02 -1.8074724020788719e+00 + -2.5417468582312877e+00 -1.9298735695592950e-01 1.4524591906329190e+00 1.4111623962585536e+00 -3.3194621049731687e-01 -2.5702905387021753e+00 -3.3145376224302021e+00 -1.6531002526600589e+00 1.5085470936791965e+00 4.9384825610490894e+00 8.1158180601966112e+00 1.0592317046651424e+01 1.1720352844683582e+01 1.1731547138555802e+01 1.1880590058094510e+01 1.2779413370052000e+01 1.4075646883094780e+01 1.5918248275430194e+01 1.8326489365386571e+01 1.9316206123129387e+01 1.6690629155310674e+01 1.1661681239753394e+01 8.0053968635282153e+00 7.5533291045585287e+00 8.4805002524471416e+00 7.9431009917907147e+00 4.7893096294793169e+00 -2.9205326061448478e-01 -5.9299905729305458e+00 -1.0889025474342041e+01 -1.4253802911511571e+01 -1.5424425634403272e+01 -1.4219613607718578e+01 -1.0988129270893715e+01 -6.7271231240914489e+00 -2.8803629271713325e+00 -5.3221218228894118e-01 -1.4819897860306691e-01 -2.2309622192950278e+00 -6.7555441266626337e+00 -1.1288796495269558e+01 -1.1879369455774969e+01 -7.6695223775198915e+00 -2.7345250210875287e+00 -1.2464522929563033e+00 -2.5982520939275595e+00 -3.2907753012277650e+00 -2.0946248103194671e+00 -1.2670396502483379e+00 -2.9734875330694628e+00 -6.1944576306095183e+00 -8.0259110770747917e+00 -7.0406747672630017e+00 -4.3818830819913117e+00 -2.2337026899490038e+00 -1.7757039249321303e+00 -2.1690903031406541e+00 -1.7112483834128271e+00 -2.2443726961269805e-01 7.8312271984712389e-01 7.2340304513709708e-01 9.8143716696324113e-01 2.5456848227724063e+00 4.1446278797649949e+00 + 5.9509298374097952e+00 5.0678810528340978e+00 4.6985100256137695e+00 5.6806474798139019e+00 7.1270807068985986e+00 7.3896907513926973e+00 6.1956015245970972e+00 5.5852197604153861e+00 7.9143806050507850e+00 1.2903336221013145e+01 1.7478615210727796e+01 1.8733526717119613e+01 1.6557524998442712e+01 1.3237833414525580e+01 1.1194587213855545e+01 1.1742137197718762e+01 1.5292487123411739e+01 2.1089872768798958e+01 2.6615090233197662e+01 2.9112067948790987e+01 2.8381112807191901e+01 2.6745309556602031e+01 2.6230622139582557e+01 2.6873626067912664e+01 2.7379954661291766e+01 2.6724918542001461e+01 2.5352283105591539e+01 2.4558243711441179e+01 2.4278527981980282e+01 2.2429473534878777e+01 1.7501264178786681e+01 1.0987541340332779e+01 5.6451288588220212e+00 1.5426875751835269e+00 -4.2209755312065607e+00 -1.3196792287091672e+01 -2.2788705236686901e+01 -2.9082273423343587e+01 -3.1121693514905303e+01 -3.0849786691440926e+01 -2.9901975701231901e+01 -2.8187902353798506e+01 -2.4897002861955549e+01 -1.9629404309588349e+01 -1.3381778061564034e+01 -8.8077830839680740e+00 -8.0332756560395637e+00 -1.0120926859300432e+01 -1.2004163217689651e+01 -1.1842485990658060e+01 -1.0575962516702482e+01 -1.0149295488895902e+01 -1.0971618377841839e+01 -1.1645676947992518e+01 -1.0824020132311130e+01 -8.7473035146007145e+00 -6.7816366706045397e+00 -5.8695256679524599e+00 -6.1984801392743734e+00 -7.8537905857434129e+00 -1.0416960930281348e+01 -1.2429989647191839e+01 -1.2650740393840184e+01 -1.1521198276477271e+01 + ] ) - check("sparse.mat", result) - - matfile = matopen("partial.mat") - var1 = read(matfile, "var1") - @assert var1[28, 33] == 5 - var2 = read(matfile, "var2") - @assert var2[27, 90] == 10 - close(matfile) - + check(joinpath(dirname(@__FILE__), "big_endian.mat"), result) end -result = Dict( - "index" => [8.8604784000000000e+04 9.8707212000000000e+04 1.0394035200000000e+05 1.1429712000000000e+05 1.5474923999999999e+05 1.5475435200000001e+05 1.5501074400000001e+05 1.5505315200000000e+05 1.5505718400000001e+05 1.5506168400000001e+05 1.5506524799999999e+05 5.4945741599999997e+05 5.6345896799999999e+05 5.9956981200000003e+05 7.0691623199999996e+05 7.9063657200000004e+05 8.4311938800000004e+05 9.2225131200000003e+05 1.1248994160000000e+06 1.2508148520000000e+06 1.4164141320000000e+06 1.4275988280000000e+06 1.4744331000000001e+06 1.4982212879999999e+06 1.5549058440000000e+06 1.5870300840000000e+06 1.6192005120000001e+06 1.6766071560000000e+06 1.9386816839999999e+06 1.9969427879999999e+06 2.0021861880000001e+06 2.3272494120000000e+06 2.5309351080000000e+06 2.6743788720000000e+06], - "spikes" => [ - -3.9146236245031032e+00 -6.7657651330021364e+00 -1.0780027188484372e+01 -1.4345619557780790e+01 -1.5488781013877338e+01 -1.3241531877846004e+01 -8.6339302778751907e+00 -4.1571900578409995e+00 -1.4845719040296610e+00 2.3147400250828232e-01 2.8917910181412778e+00 6.4067867244186800e+00 8.3368575385567603e+00 7.0732985406218223e+00 4.4095174940268036e+00 3.8495932342509342e+00 7.0605464919276546e+00 1.2892731012948772e+01 1.8593404980539656e+01 2.1332908128411184e+01 2.0142332517120792e+01 1.6740473413471157e+01 1.3650330377340575e+01 1.1913871749214691e+01 1.0804794411826084e+01 8.8366401987297127e+00 5.1092331749990514e+00 5.1218216653980408e-01 -2.9327647633922682e+00 -4.4870896208146753e+00 -5.0598199463728655e+00 -4.8330524336350118e+00 -2.8556000012645000e+00 2.9794817723619027e-01 1.8265416505730325e+00 -8.6155940979615875e-02 -3.9623352473810947e+00 -6.9070013227561047e+00 -7.3941131196997647e+00 -5.7411207637544166e+00 -3.2366812420300106e+00 -1.1460492068000723e+00 1.2381260731009580e-01 1.0930145325605314e+00 2.1927876983540933e+00 2.6570284430776856e+00 1.3381366125210661e+00 -1.2539624260623763e+00 -3.3642620416729994e+00 -4.1849749207505456e+00 -3.8760400918509301e+00 -2.6869552030388291e+00 -1.6718246062697015e+00 -2.3709942853677934e+00 -4.6623835517993664e+00 -6.6575320887201714e+00 -6.9891263747717174e+00 -5.7017039068420186e+00 -3.4759011423153079e+00 -1.7092931352045238e+00 -2.3854494206243695e+00 -5.8068462168496913e+00 -9.1001745572212531e+00 -8.8479323560036516e+00 - -5.0745139212223540e+00 -9.6338046242625506e+00 -1.3472006614220170e+01 -1.5245910823426385e+01 -1.5914718584705716e+01 -1.6397777086548157e+01 -1.5688652912166024e+01 -1.2863641020969972e+01 -9.5044110719151487e+00 -8.3463890698794305e+00 -1.0177024989874276e+01 -1.3445743563344164e+01 -1.6692449999817043e+01 -1.9680052373752346e+01 -2.0121942799899024e+01 -8.3700572911264981e+00 3.0032951243048100e+01 9.4582543108269547e+01 1.5377989075611117e+02 1.7130866838277802e+02 1.4748486763605970e+02 1.0947277486760775e+02 7.2684596239613015e+01 3.8193102601464858e+01 1.0540246012312117e+01 -3.8091287155762972e+00 -5.9643997244651556e+00 -3.0829122045048947e+00 -9.3969579584981044e-01 -4.1011214330806744e-01 1.1485354724913588e-01 8.3209939369519947e-01 1.7132389429717065e-03 -3.6057351605809611e+00 -8.8203434292989460e+00 -1.2692989286632969e+01 -1.3074091545751195e+01 -1.0580269175201330e+01 -7.7262110976707312e+00 -6.1406117115548504e+00 -5.0818589866084025e+00 -3.3150901769832091e+00 -1.7731765969545847e+00 -2.5627661695922046e+00 -5.6404917366725886e+00 -8.3842617579066747e+00 -8.5769172149145199e+00 -6.8248629640086911e+00 -5.7747794329330571e+00 -7.1063437611930578e+00 -9.7162880960954556e+00 -1.1170428842658552e+01 -1.0378119546974839e+01 -8.3419883744685741e+00 -6.9209209230224635e+00 -7.4145555946251820e+00 -9.6713314325255784e+00 -1.2062946758371979e+01 -1.2965017154593347e+01 -1.2408773058757985e+01 -1.1624355346583785e+01 -1.1127849526328186e+01 -1.0048811168388408e+01 -7.6327226475166228e+00 - -3.6172800577681850e+00 -3.0496107525626650e+00 -4.0450961689646379e+00 -4.9841853952779340e+00 -4.3137243411079638e+00 -2.4211699563520748e+00 -9.3469136675988018e-01 -9.3834475312754062e-01 -2.3043613016166664e+00 -3.9905808684096256e+00 -4.6842071022425413e+00 -3.8310453992051836e+00 -2.8818230979207713e+00 -5.2038837297130556e+00 -1.1725055393804819e+01 -1.3208091586845086e+01 9.2482123494256854e+00 6.3181823866266974e+01 1.2177486062412244e+02 1.4070171740815530e+02 1.0609880427064739e+02 4.8491974791356185e+01 5.8467389218168400e+00 -9.6802170255950291e+00 -8.6605173391900614e+00 -5.1555616199626577e+00 -5.4237382663488169e+00 -8.2575373220146044e+00 -1.0156241701964554e+01 -9.1574620977201118e+00 -5.6665700132115431e+00 -1.5504659563007614e+00 1.1334383183944798e+00 1.2640431009400714e+00 -6.6794751018025034e-01 -2.9543901520038176e+00 -4.1963329340429310e+00 -4.3642482086638417e+00 -4.0042244694146101e+00 -3.1394295409389494e+00 -1.8179756080601912e+00 -9.3024527470805374e-01 -1.4736656790371958e+00 -3.4645773489091209e+00 -5.8588062538620047e+00 -7.0734948204842896e+00 -6.0481893429370261e+00 -3.3344016972431088e+00 -4.8650896484328832e-01 1.6052150099271567e+00 2.9838901666777322e+00 3.7836445807114574e+00 3.9345735120816623e+00 3.1644840267225094e+00 1.0070686684300534e+00 -2.4713922778659478e+00 -5.9984216012494951e+00 -7.8885829262850233e+00 -7.5154376078825695e+00 -5.8326333185237758e+00 -4.5003931071444478e+00 -4.2328547992043450e+00 -4.1140676583157889e+00 -3.1460951038002301e+00 - -3.6781701228839154e+01 -3.7303084834807457e+01 -3.8884510028175633e+01 -4.1148035399962872e+01 -4.3316242853459642e+01 -4.4384384580854572e+01 -4.4790855343692897e+01 -4.7038755365884079e+01 -5.2501365276546458e+01 -5.6812084874163638e+01 -4.9570438054352273e+01 -2.3981805280442412e+01 1.1015352613446286e+01 3.7025927055247365e+01 4.6196840812364755e+01 4.5435239856395953e+01 4.4108776047185387e+01 4.5957015883875258e+01 4.9338962205718175e+01 5.0907715343642117e+01 4.9487314118838199e+01 4.6376644839165742e+01 4.2907993314061748e+01 3.9175485818078528e+01 3.4646364450142663e+01 2.9438295855526434e+01 2.5290001942034166e+01 2.4585557992414284e+01 2.7869881512937130e+01 3.2856199330415350e+01 3.6123557386279117e+01 3.5905020129260436e+01 3.3291843240497656e+01 3.0657151735220140e+01 2.9399978478943620e+01 2.9238216866099457e+01 2.8701738303043562e+01 2.6077460100630137e+01 2.0689205683518288e+01 1.3779281580459998e+01 8.1712538443203133e+00 6.6925061080176569e+00 1.0147005424138726e+01 1.6509827576474752e+01 2.2276297357800129e+01 2.4765444980789738e+01 2.3565808601878054e+01 2.0365852060539318e+01 1.7482427576467405e+01 1.5922922611993604e+01 1.4534460949429915e+01 1.1655908219543699e+01 7.5216074902940893e+00 3.9334852970087364e+00 1.7985868168036778e+00 3.9751100517608062e-01 -9.6689135481901367e-01 -1.8291316756437888e+00 -1.2108732556384698e+00 1.1422814255806824e+00 4.0827052431935931e+00 5.6696392298883893e+00 4.7889899662105178e+00 1.9874261259261794e+00 - -1.1686614851500112e+00 9.8124871505894706e-01 2.4682927864975457e+00 2.9222372863110442e+00 1.6794130795786746e+00 -6.8820557107116076e-01 -2.4491510090825823e+00 -2.5573541785944220e+00 -1.5657335653505560e+00 -6.2326635393824792e-01 7.5542615186640072e-03 1.2160520506156904e+00 3.7959025732449647e+00 7.1695274203178112e+00 9.8085862745466752e+00 1.1024667458560756e+01 1.1849030121520164e+01 1.3645183550697372e+01 1.6153045543872672e+01 1.7653460112285362e+01 1.6917192047304358e+01 1.4287778990065465e+01 1.1181854556421472e+01 9.2813981144484536e+00 9.6980182244576554e+00 1.1975656236851366e+01 1.4218362205978007e+01 1.4823189770395057e+01 1.3782281414778801e+01 1.2224714159620135e+01 1.1308817315730568e+01 1.1413207645545857e+01 1.1802812942398777e+01 1.1304006120772028e+01 9.4673823725009267e+00 6.7674104407290443e+00 3.9034391463718467e+00 1.0696510964445158e+00 -2.2265622627917740e+00 -6.1619053880194183e+00 -9.4895708918063448e+00 -1.0511848180773416e+01 -9.2016379963305752e+00 -7.3655072801953869e+00 -6.6664600174160604e+00 -7.2291846775841488e+00 -8.2107481715781105e+00 -9.1021811818757321e+00 -1.0311278259980540e+01 -1.2741511638678721e+01 -1.6437776102727412e+01 -1.9514979330779195e+01 -1.9610190285680936e+01 -1.6644297020966704e+01 -1.2800100439249650e+01 -9.9046719354109900e+00 -8.2923627062655303e+00 -7.8966764067275435e+00 -8.8257663696547084e+00 -1.0735616318501446e+01 -1.2599202496900540e+01 -1.3220431400502429e+01 -1.1920594647006027e+01 -9.3084512934042376e+00 - 1.1099942135772691e+01 1.0097197127580243e+01 1.1065906114342178e+01 1.3374123078938322e+01 1.6449533911253752e+01 2.0017355902687680e+01 2.3638247141687746e+01 2.6292880854006093e+01 2.6783333166190332e+01 2.5183008648660202e+01 2.3296988595385344e+01 2.2810464758877668e+01 2.3624918756652875e+01 2.4607374587222779e+01 2.5393398749862719e+01 2.6769469521678889e+01 2.9281995275221178e+01 3.2213436799025317e+01 3.4285343581139983e+01 3.4785296465751173e+01 3.3883822298572078e+01 3.2386332385252516e+01 3.1231943637716320e+01 3.0932840342758819e+01 3.1246878043151604e+01 3.1160302181567783e+01 2.9550070933834700e+01 2.6540966708675843e+01 2.3785330200047358e+01 2.2611886801417548e+01 2.2531515536471172e+01 2.2340044415310590e+01 2.1822535590221452e+01 2.1399364443416083e+01 2.0755992097134378e+01 1.8948315967557182e+01 1.5769917402888659e+01 1.2205733819672014e+01 9.4099541981525228e+00 7.7046512181258402e+00 6.5719664417242711e+00 5.2343013227680553e+00 3.3389338216778657e+00 1.2573655827507051e+00 -5.0199990120897509e-01 -1.8987019795142805e+00 -2.9172592054133535e+00 -3.2152176994632509e+00 -2.9689210401896537e+00 -3.2720957493420673e+00 -4.8582690799070640e+00 -7.1594797906170751e+00 -9.1803890906391956e+00 -1.0495134027231670e+01 -1.1056911353417380e+01 -1.0726534970818260e+01 -9.3753223680626547e+00 -7.2907274381158134e+00 -5.1694262729300959e+00 -3.7312476583791785e+00 -3.6141234953544066e+00 -5.1662008322483448e+00 -7.7776079749577836e+00 -9.8010587078412321e+00 - -1.4685530727984641e+01 -1.0292328330297664e+01 -6.2040173116488546e+00 -2.3375015307800595e+00 1.3923917658511158e+00 5.0564769748901313e+00 8.6976966359851104e+00 1.2323245348083425e+01 1.5903913125353689e+01 1.9380777962659089e+01 2.2677898949762564e+01 2.5718454097742246e+01 2.8441073995486732e+01 3.0813082951470030e+01 3.2837981366611231e+01 3.4555661605993883e+01 3.6035311542317494e+01 3.7362431137561401e+01 3.8622571934083787e+01 3.9885068827368393e+01 4.1190035284914742e+01 4.2541236867236606e+01 4.3906275503545629e+01 4.5224046329587310e+01 4.6417965861852949e+01 4.7412309197387181e+01 4.8148368395523121e+01 4.8597180088665191e+01 4.8766260494684808e+01 4.8698991553936125e+01 4.8466780378761598e+01 4.8155568154983840e+01 4.7849401563031734e+01 4.7614370946271123e+01 4.7486145888939269e+01 4.7463615344086584e+01 4.7509911501785595e+01 4.7560611227134473e+01 4.7537464942025593e+01 4.7364890774312840e+01 4.6985915393397903e+01 4.6374354157541411e+01 4.5540779674504876e+01 4.4531077464674446e+01 4.3417878743397615e+01 4.2286593389902350e+01 4.1218852636871070e+01 4.0276692303289153e+01 3.9490658602725233e+01 3.8854229591844380e+01 3.8325674875439489e+01 3.7836979823323738e+01 3.7308039311521902e+01 3.6663265807716996e+01 3.5847270787923392e+01 3.4836464766174224e+01 3.3644242194420237e+01 3.2318708010392434e+01 3.0933403069436942e+01 2.9572894236760884e+01 2.8316128161485977e+01 2.7220897815058663e+01 2.6312547181929837e+01 2.5579187011419549e+01 - 1.5763956152315169e+01 1.6523243082892250e+01 1.7337427631570872e+01 1.8120936638484590e+01 1.8792445157811912e+01 1.9291006994381139e+01 1.9588077541817789e+01 1.9693009891213325e+01 1.9650918197573489e+01 1.9533337877188348e+01 1.9423561429962543e+01 1.9399599967723205e+01 1.9518198338118580e+01 1.9803119911795868e+01 2.0240058237539227e+01 2.0779199633821964e+01 2.1344921568275133e+01 2.1850676042659646e+01 2.2216063986224665e+01 2.2382665762479274e+01 2.2325443040524132e+01 2.2057418172501748e+01 2.1626689639781716e+01 2.1106384004760407e+01 2.0579565930487874e+01 2.0122142297473431e+01 1.9787200170220586e+01 1.9593930129327656e+01 1.9523364223698717e+01 1.9521786868823959e+01 1.9511132956806740e+01 1.9404280892123200e+01 1.9122163091585080e+01 1.8609252789360340e+01 1.7844318015586811e+01 1.6844290518050197e+01 1.5660474424296631e+01 1.4367827117720619e+01 1.3049394428891683e+01 1.1778975433290963e+01 1.0605636123380648e+01 9.5436591203415890e+00 8.5705174399266060e+00 7.6329046830511684e+00 6.6569045386579333e+00 5.5557424579780292e+00 4.2330232952205966e+00 2.5948387638090815e+00 6.0010139846456756e-01 -1.6403240489988979e+00 -3.7983903715983196e+00 -5.5143675763583140e+00 -7.0197817000212250e+00 -9.2991809829904906e+00 -1.2710108289977208e+01 -1.5773321560596763e+01 -1.6492611312664831e+01 -1.5291065260668367e+01 -1.5553348127486492e+01 -1.9596399167644933e+01 -2.5711586491024381e+01 -3.1294712703031326e+01 -3.6140274982880157e+01 -4.0736526948594026e+01 - -7.5455089032603496e+01 -8.8091850484650791e+01 -7.6894847494814442e+01 -5.1931618057453484e+01 -2.8833831644552824e+01 -6.8895988088626297e+00 2.1168348264522379e+01 4.6693994244353583e+01 5.3777387223823958e+01 4.4603130335345831e+01 3.3530654869558120e+01 2.3587826739136968e+01 8.4238787409792160e+00 -9.2318222698507917e+00 -1.4713136414898660e+01 1.9717165915666754e+00 3.1968641163889899e+01 5.7269930354848498e+01 7.0152688867705734e+01 7.3409072049238773e+01 7.0166235593951257e+01 6.1829204326045371e+01 4.6696134605544373e+01 1.8622054473547621e+01 -2.2829591964997910e+01 -6.2715671170700787e+01 -8.2653742502672856e+01 -8.1088328703707077e+01 -7.2961806812740548e+01 -7.3632394853923415e+01 -8.9841480609702060e+01 -1.1962228131535032e+02 -1.5066572610100792e+02 -1.6038121312642346e+02 -1.2981471355911390e+02 -6.3653163261418783e+01 9.3811209125033130e+00 5.8695741367623484e+01 7.0437047869761585e+01 4.9630793312944938e+01 1.6599415269056415e+01 -2.5402904227425278e+00 4.0559101821793178e+00 2.4429205550104435e+01 3.9021358713588448e+01 3.8520783430674051e+01 1.9952697815867261e+01 -2.1877257859243720e+01 -8.3907527060881065e+01 -1.4250639755111564e+02 -1.6619398657243511e+02 -1.3819218483975862e+02 -6.6785267226843359e+01 7.8497452778842174e+00 2.7663034892382331e+01 -1.9252281399357905e+01 -5.9646303432603780e+01 -1.8852103558667594e+01 6.7639555235119275e+01 9.6932460076734856e+01 4.1262889176984515e+01 -3.3610639804651925e+01 -7.8423083296187542e+01 -1.0839727633754131e+02 - 4.7736577112711117e+01 4.5700337234538978e+01 4.5322631494156710e+01 4.9171444949072850e+01 5.3757473474671947e+01 5.4141589236927501e+01 4.8790940853843097e+01 4.1857477273482019e+01 4.0754533608850998e+01 4.8608027316167508e+01 6.0100216178867996e+01 6.8180296544356494e+01 7.1421072557515430e+01 7.0807306032014466e+01 6.6514973934144194e+01 6.2945074970931898e+01 6.8196514720480167e+01 8.5012727237677268e+01 1.0720726826115141e+02 1.1957256746876999e+02 9.9138254538272335e+01 3.8069276174166163e+01 -2.7233524821672358e+01 -3.9334632563706222e+01 1.3886634524961096e+01 7.3584276961424322e+01 6.3628518198466388e+01 -2.9054572516934122e+01 -1.4209888641029568e+02 -2.0910522987863314e+02 -2.1415046392049794e+02 -1.7586701353513934e+02 -1.1844102599759782e+02 -6.1431963885049129e+01 -1.4806030025628601e+01 1.4027944301627443e+01 1.0652586257860358e+01 -3.0054086554043813e+01 -8.5072361204847368e+01 -1.2046643138190960e+02 -1.2106054250546094e+02 -9.5841527604366775e+01 -6.6055322458778861e+01 -4.4458770387691487e+01 -2.4336370991880798e+01 4.5840797349366902e-01 2.1040288319943066e+01 2.3611095638912182e+01 1.3051021255932143e+00 -2.8851154140515209e+01 -3.7619486391456242e+01 -2.9915658771811749e+01 -3.0708973922309450e+01 -3.1924567737368655e+01 -1.0202022943830535e+01 2.9706495601613049e+01 5.9067239546813376e+01 5.4702088800913174e+01 2.8343487643024787e+01 1.5951389347675942e+01 2.2837902575396686e+01 1.2908948645547047e+01 -3.3255358730535995e+01 -8.7746125812653432e+01 - -8.2377418542911354e+00 -3.4972931824407372e+00 -7.7569626244497876e+00 -1.5166206734460895e+01 -1.5445771078633909e+01 -5.2413228186791176e+00 8.4387175092257500e+00 1.5431757026108208e+01 1.2399978335869392e+01 7.4095099729504046e+00 1.0679579816928053e+01 1.8701188652942292e+01 1.7701213689196198e+01 4.8431711642004913e+00 -4.7779662604481938e+00 3.1723005655630221e+00 2.4531952072999687e+01 4.5698045473740862e+01 5.9257083049327122e+01 6.2627426268528140e+01 5.7411981420461260e+01 5.2570470576359099e+01 5.2813113343401156e+01 5.0453507704104304e+01 3.9691119012785791e+01 2.6528767918502940e+01 1.8741299694240368e+01 1.7724441294399568e+01 2.0821175647900695e+01 2.4417659444952996e+01 2.5870167533339281e+01 2.4983092349575369e+01 2.2998436116876093e+01 2.0558711472598521e+01 1.7502822279045098e+01 1.3739438787271743e+01 9.9370928813924273e+00 7.9696531198819107e+00 9.3237074864808100e+00 1.2496048137913172e+01 1.4202138161523894e+01 1.3776871923186704e+01 1.4057294551750237e+01 1.6919973545359703e+01 2.0190481439159552e+01 2.0371692873037446e+01 1.6567959095411027e+01 1.0520230640011906e+01 4.5794135735479564e+00 8.7710329733688541e-01 8.9182399282027713e-01 4.3463285075996909e+00 8.7315858121978973e+00 1.0903936631346470e+01 1.0288756426506817e+01 1.0049725699443105e+01 1.3054576528907717e+01 1.6854021523448839e+01 1.6041035800270304e+01 1.0367866836597134e+01 6.4265096090680434e+00 9.4949675891987368e+00 1.6836520945082036e+01 2.1325105933919993e+01 - -4.1679158148256432e+00 -5.2810404688221357e+00 -5.9038977834292563e+00 -6.2634044553555412e+00 -5.9749855839469195e+00 -4.3218694766614334e+00 -1.1788772851158797e+00 2.4374543533437842e+00 4.8050829080472326e+00 4.8908091608309068e+00 3.3699448624872437e+00 1.8764385857350361e+00 1.0658937699825455e+00 -2.7882253437288551e-01 -3.1356733669314134e+00 -4.1284337659893531e+00 4.9241012162347957e+00 2.8218865342091348e+01 5.5056325524296241e+01 6.5099843979883218e+01 4.9609447627592324e+01 2.1423693795580775e+01 2.5132863903596458e-01 -5.8604600478907667e+00 -2.3380828579082129e+00 1.9600991106694594e+00 2.5915747007003080e+00 2.0190047306038350e-01 -2.8577160575959448e+00 -5.2869354732703258e+00 -6.9099491413979388e+00 -7.7559496509908179e+00 -8.0339926236897785e+00 -8.1760789963289717e+00 -7.9859360445805283e+00 -6.3703713820186918e+00 -3.0321001946270005e+00 -2.8201117047924162e-02 -7.8351721876626979e-02 -3.2229219827610613e+00 -6.6022607253341592e+00 -7.6428337012652356e+00 -6.3520480710327867e+00 -4.5468067925800151e+00 -3.6792489626697655e+00 -3.7265752054412307e+00 -3.8104694479464527e+00 -3.1502606288908388e+00 -1.5302095841423975e+00 2.6517264914271876e-01 7.6568764912213050e-01 -5.6131323023144386e-01 -2.6412705089170849e+00 -4.2923899829303380e+00 -5.1769894068210993e+00 -4.9614565332821146e+00 -3.2859163281387294e+00 -1.1703534382204590e+00 -7.6910847698287388e-01 -2.6122681986842187e+00 -4.2742848318737003e+00 -3.0739009488926641e+00 6.8120601334287323e-01 3.9336266256502612e+00 - -4.6484775115089541e+00 -8.3581561181698660e+00 -1.0902142569692860e+01 -1.1511797760460409e+01 -9.8276834132977005e+00 -6.2404161549022161e+00 -1.8432175810231679e+00 1.9347492334298952e+00 4.2938226787082971e+00 5.6566425031547753e+00 6.9370739014200060e+00 8.7323889596425897e+00 1.1151007876295226e+01 1.3469254774199312e+01 1.4378076971481226e+01 1.3628707243729355e+01 1.2810890536991595e+01 1.3603571552449427e+01 1.5792969414511386e+01 1.7425203016009618e+01 1.6493276434801135e+01 1.2586445678344095e+01 7.3401463249477024e+00 3.2087842017129540e+00 1.5030100447830739e+00 1.3898465906994972e+00 8.8198090743093560e-01 -1.0493063115005812e+00 -3.7608545130075726e+00 -6.3949540211998350e+00 -9.0066585358720026e+00 -1.1745588278397154e+01 -1.3842632605375675e+01 -1.4123365855042982e+01 -1.2335802612795424e+01 -9.4739532170207532e+00 -6.3782197090918142e+00 -2.8832164731098446e+00 4.8447063694123349e-01 1.6128314119847449e+00 -4.1774621162461167e-01 -3.3629973208103769e+00 -4.5025431650456200e+00 -3.9290643942165806e+00 -3.7022642550448306e+00 -4.6991587651781321e+00 -5.6909794907882132e+00 -5.2906700399814426e+00 -3.7661436808309654e+00 -2.5302504318642383e+00 -2.0969878128246422e+00 -1.3265870686252779e+00 7.0961290345922357e-01 3.0385814819877943e+00 3.9548508946305954e+00 2.9954312845408939e+00 9.9988805713822959e-01 -9.2502312206375614e-01 -2.0384940259267319e+00 -2.2297243620823197e+00 -2.0769146009998161e+00 -2.3170471037592764e+00 -2.8506427004880215e+00 -2.6777747842245225e+00 - -7.7992155184377432e-01 8.4208602530413512e-01 2.4887283142722940e+00 2.4338293029048339e+00 8.3831805409641036e-01 -5.6099977991794492e-01 -7.1266715703072170e-01 -2.3092906084045711e-01 1.6955881483329160e-01 8.8504105274391720e-01 2.2423969436547071e+00 3.3863034262368776e+00 3.6616215908416012e+00 4.2752627336864073e+00 7.0581057720790223e+00 1.1758833658720096e+01 1.6047422019307188e+01 1.8405075233788665e+01 1.9427396781993370e+01 1.9879444229356857e+01 1.9348574654086402e+01 1.7629540788742673e+01 1.5792875449818970e+01 1.4872230427632566e+01 1.4471792816861928e+01 1.3426898347317799e+01 1.1319456367524939e+01 8.8575138135993203e+00 7.0846315948436089e+00 6.5127944804059057e+00 6.6781793515550252e+00 6.2758575263602134e+00 4.2213211609500574e+00 1.0187928511802555e+00 -1.3365602410322484e+00 -1.2247622425200726e+00 1.1481723220280773e+00 4.3321910406078903e+00 6.9648334152330520e+00 8.0154771868425438e+00 6.5719188712588483e+00 2.4474218319276826e+00 -3.0491523253799624e+00 -7.4154828913997903e+00 -8.5953677824666350e+00 -6.7813390775209372e+00 -4.2068257455106703e+00 -2.7309337921106196e+00 -2.0348235578019351e+00 -6.2767747718278888e-01 1.7379756336568186e+00 3.5393101620046572e+00 3.5444884355037840e+00 2.3746522750550279e+00 1.3500592448824880e+00 6.9976097822180139e-01 -1.5618921435797500e-01 -1.3162226599662721e+00 -2.4845798158016903e+00 -3.7797798352858396e+00 -5.2368125011457316e+00 -5.9486138818233414e+00 -5.1354371684134463e+00 -3.7528582973371494e+00 - -3.0447537175403006e+00 -1.7667805638958940e+00 -1.9778229750617011e+00 -2.2515604403315699e+00 -1.3449261643785633e+00 -1.0662626484421048e-01 -5.7657486030100791e-02 -7.4315041221388778e-01 -3.2337810363445102e-01 1.8577438758015754e+00 4.8425765941254983e+00 7.8123978114524020e+00 1.1105854493054094e+01 1.4841903991300153e+01 1.7468871344145363e+01 1.7574512951317725e+01 1.6451820522767886e+01 1.6628323973953727e+01 1.8301434258000356e+01 1.8931382166939795e+01 1.6217394592510296e+01 1.0559530299242045e+01 4.4576293834236687e+00 -1.1822470014800246e-02 -2.5727563770885062e+00 -3.7547505719161225e+00 -3.5081536657220189e+00 -1.4835065248806825e+00 1.8247576781703743e+00 4.8275703093820033e+00 5.7228574725963268e+00 3.8558888609578723e+00 4.0990146070366057e-01 -2.4574645653496310e+00 -3.1102243476353593e+00 -1.4474225309731716e+00 7.4127875680854416e-01 1.0383069562131020e+00 -1.0150232301722759e+00 -3.4198210424706135e+00 -4.3088303898183238e+00 -3.9165170232756070e+00 -3.6605958183355347e+00 -4.2469618414480417e+00 -5.2237494007048566e+00 -5.9420620236257076e+00 -6.1698135259889568e+00 -5.7089733374935268e+00 -4.0492707065696969e+00 -8.0849384208890562e-01 3.3216805308527921e+00 6.2990262694136261e+00 6.1812329879916819e+00 3.1266169114028388e+00 -4.8505368345761951e-01 -2.2625885070785667e+00 -2.0371523965480165e+00 -1.4033783831712108e+00 -1.2651726669536290e+00 -8.7131680693218405e-01 5.0106877171388586e-01 1.8406747295741790e+00 1.3085085083594870e+00 -1.5293730324086736e+00 - 2.3513445326289961e+00 1.7384880477187443e+00 2.2184897775735659e+00 5.2051223974837342e+00 1.0452816121087638e+01 1.5377084182445486e+01 1.7198975360259148e+01 1.6104713320124159e+01 1.4833420837246866e+01 1.5100670860303905e+01 1.6073099350257220e+01 1.6269109488250638e+01 1.5637757524959511e+01 1.5476499170789715e+01 1.6604024256326596e+01 1.8317676582762779e+01 1.9829075755286041e+01 2.1816151151864332e+01 2.5055347925633036e+01 2.7333907737059032e+01 2.2427805378565086e+01 3.7141556286936721e+00 -2.8648138173116212e+01 -6.3843957225821207e+01 -8.7089860614432553e+01 -9.1923311472937954e+01 -8.3938126428996938e+01 -7.2885847777427841e+01 -6.4297677965088411e+01 -5.7914366258401401e+01 -5.0470379597131561e+01 -3.9499120538780375e+01 -2.5751695307781368e+01 -1.1778187088014317e+01 1.0189820693630494e+00 1.1960524761686607e+01 1.8733303083800365e+01 1.8356414318508428e+01 1.0668647686854321e+01 -1.0075011878329772e+00 -1.2633619248121462e+01 -2.1404683668367543e+01 -2.5602377289037388e+01 -2.4560926814716566e+01 -1.8847733387633440e+01 -9.8404336731293878e+00 3.5966958558048301e-01 9.0662935627188972e+00 1.4335829915263089e+01 1.6036694595613060e+01 1.5184337823870287e+01 1.2469246394512929e+01 7.6677145828817306e+00 8.5165822752424969e-01 -5.8806346340252293e+00 -9.1084583818696743e+00 -7.2999534121027425e+00 -2.4813921667292815e+00 1.7215656085634032e+00 3.0197896060174911e+00 1.4579843578258131e+00 -1.6420102258369735e+00 -4.8732983588159042e+00 -6.9044980772313806e+00 - -7.1748224092297468e+00 -3.8936102456845245e+00 1.0743831294909865e+00 4.8816301487736782e+00 5.4189196440465226e+00 2.8421806913201926e+00 -7.8193972997551731e-01 -3.3953855854612938e+00 -3.9985877326731809e+00 -2.4284915455410818e+00 3.3095855413398589e-01 2.1860065858780535e+00 1.8280988855495470e+00 3.4048799490454618e-01 3.8790118108287441e-01 3.8556647578240639e+00 9.9718322097927121e+00 1.5912297294502212e+01 1.9511397698602284e+01 2.0651366980982488e+01 2.0134282655984631e+01 1.8560445836154724e+01 1.6287447146155721e+01 1.3561600277652611e+01 1.0795264078502715e+01 8.6030785401577266e+00 7.0022103875078896e+00 5.1888911547538754e+00 2.7145704050730384e+00 1.4143845469113936e-01 -1.9107456843838713e+00 -3.4559243419646326e+00 -4.2223452047916581e+00 -3.1130434517080205e+00 1.7183260785086762e-01 3.7869557639525033e+00 5.3380153379310666e+00 4.2352058165010487e+00 1.6573488592368961e+00 -8.5887188564406203e-01 -2.1681503131724709e+00 -2.1279908375294667e+00 -2.0792749208808359e+00 -3.3997834567720187e+00 -5.3580052019440032e+00 -5.9792374734377622e+00 -4.8403202446726707e+00 -3.3487890303481347e+00 -2.6600981462061033e+00 -2.7228934357253367e+00 -3.1308053901450172e+00 -3.8963449071942557e+00 -5.2729284759196471e+00 -7.2764195946828067e+00 -9.3283947520693822e+00 -1.0227824562298673e+01 -9.0739885142293417e+00 -6.5681575524140818e+00 -4.8038541028704778e+00 -5.3349275900632449e+00 -7.6025577687023391e+00 -9.3130362450711317e+00 -8.3152888687476345e+00 -4.2484255376825795e+00 - -1.2094820634316039e+00 -1.2706817164948352e+00 -1.2775018026388452e+00 -2.3357176552894954e-01 1.8928499829725349e+00 4.4677863928736414e+00 6.9513585826945681e+00 9.0204123867514259e+00 1.0482452392061710e+01 1.1455309752506835e+01 1.2237810752650413e+01 1.2688637793112969e+01 1.2235313891126804e+01 1.1100297213735251e+01 1.0773414547739115e+01 1.2217993905654721e+01 1.4701466325393794e+01 1.7319490559451381e+01 1.9808972202344144e+01 2.1257689253929254e+01 2.0368628657184544e+01 1.7524563363309646e+01 1.4843567969750833e+01 1.3863574192388871e+01 1.4031045614008303e+01 1.3693669004482535e+01 1.2122436338523961e+01 9.9467781708780620e+00 7.8498090003437868e+00 5.7967683127528629e+00 3.7206536395890204e+00 2.3267777177674382e+00 2.6303789001713365e+00 4.7445695138569235e+00 7.6683738268993942e+00 9.9717476628589878e+00 1.0271279571889663e+01 8.1958760665984958e+00 5.3998213275104048e+00 4.2899793853628481e+00 5.5462911375305124e+00 7.6595253723593695e+00 8.4981448223169700e+00 7.2294579833209385e+00 5.1143564806110922e+00 3.8260968045635870e+00 3.3065755059745818e+00 2.5815464286120089e+00 1.7488042411202225e+00 1.5184709880337026e+00 1.6370045591241176e+00 1.1531897091681369e+00 -1.5863929796862442e-01 -1.5759013113892080e+00 -2.3911490323759002e+00 -2.4147368159491429e+00 -1.8749121657426793e+00 -1.4086065112641286e+00 -1.6573036686621481e+00 -2.4019595691049194e+00 -2.7197582423545761e+00 -2.3228923158464436e+00 -1.8987417992229161e+00 -1.9762436025825927e+00 - -2.1788608089091870e+00 -2.3721017929273520e+00 -2.2248744539174794e+00 -1.3562719882732486e+00 1.9303974836178317e-01 1.3110471548593794e+00 1.2797866409946126e+00 1.1446512790093115e+00 2.4218364442454297e+00 5.3568199699566073e+00 9.1928444896243846e+00 1.2911855868338753e+01 1.5131303558516208e+01 1.4796675429288374e+01 1.2747818159896775e+01 1.1384776546745211e+01 1.2304499348204516e+01 1.5012766622841385e+01 1.7706586949808607e+01 1.8615946254339946e+01 1.7088985921306737e+01 1.4118105664819083e+01 1.1547667913836323e+01 1.0268505344072013e+01 9.5426651513383796e+00 8.3936752159092531e+00 6.7898835952875585e+00 4.9190137058105190e+00 2.3035160443938620e+00 -1.4454604882529436e+00 -5.5578141560543060e+00 -8.6285337833508056e+00 -9.8768639485094845e+00 -9.3497459396540972e+00 -7.4531759065479113e+00 -4.8343191161214989e+00 -2.2707557414183874e+00 -2.1273932165940168e-01 1.4763518504563440e+00 2.9929246806770484e+00 3.6484933755646152e+00 1.9940019916984792e+00 -2.3622776610960083e+00 -7.2558689942915287e+00 -9.3484617741468909e+00 -7.4954744905729234e+00 -3.9544592514927057e+00 -1.7075406868672076e+00 -1.4165546781975049e+00 -1.6221832630448796e+00 -1.0260840386708110e+00 1.6403282116014173e-01 6.0805828587662303e-01 -7.8558049118458273e-01 -3.6478240721938118e+00 -6.4046749238136291e+00 -7.7290809125845756e+00 -7.3840184127203816e+00 -5.7754694634749093e+00 -3.6798105136804447e+00 -2.5013142267663810e+00 -3.3639333122560284e+00 -5.7371424805126106e+00 -8.0051890648261459e+00 - -1.5740146351038696e+01 -1.5127188568935047e+01 -1.6157259030112712e+01 -1.7330034352768784e+01 -1.7263465597740716e+01 -1.6336957355980775e+01 -1.5946556228923413e+01 -1.6451747846316870e+01 -1.6476134055902989e+01 -1.4472580313892262e+01 -1.0935457247727644e+01 -7.9876368021814939e+00 -6.0265186692348891e+00 -2.0146808151690361e+00 8.0163390797194936e+00 2.5505537996776390e+01 4.7787314568418353e+01 6.8639540131510500e+01 8.1758985020608336e+01 8.5294009078541549e+01 8.2283381938562812e+01 7.6550409062053944e+01 6.9344678720567757e+01 6.0023044834365038e+01 4.8849934461625729e+01 3.8040291720134526e+01 2.9945383165919644e+01 2.5344624124046817e+01 2.3679402477458130e+01 2.3454441703713346e+01 2.2281261219059516e+01 1.7989994660541914e+01 1.0436193819072196e+01 2.1102305275848425e+00 -3.6577185659374600e+00 -5.5145847757485971e+00 -4.5324408328651318e+00 -1.8554008253732013e+00 2.7089378387782199e+00 9.2187555916040793e+00 1.5945566856263504e+01 2.0472163602212458e+01 2.2294516323769198e+01 2.2881411820375533e+01 2.2911237311157720e+01 2.1364859320014958e+01 1.7602082925628398e+01 1.2398805245993104e+01 6.8726800470759422e+00 1.6538594259187884e+00 -2.9437120155973195e+00 -6.5367981004048668e+00 -8.6437256178931250e+00 -9.1231101796868952e+00 -8.6648085339554566e+00 -8.4735847323575335e+00 -8.9963274994615468e+00 -9.3198443907122499e+00 -8.2504681130406006e+00 -5.5321797455350028e+00 -1.9690195798275383e+00 9.0573194795780632e-01 1.6386927935969133e+00 3.9409445444748448e-01 - -1.2097853394633344e+00 -2.3372661136012782e+00 -4.5868126352335468e+00 -7.7696106024369840e+00 -9.8847492594673341e+00 -8.4351485269365991e+00 -3.4815504396923176e+00 2.0318425367749775e+00 5.4688409099903446e+00 6.8155210844128202e+00 7.5289034993747936e+00 8.2470533843717533e+00 8.5005207895890198e+00 8.1730893089208134e+00 8.1217939375154362e+00 9.2472099093868128e+00 1.1682275429968531e+01 1.4843432519914167e+01 1.7625325319637877e+01 1.8827596749646286e+01 1.8262944365019820e+01 1.7257770010849043e+01 1.7139858491940043e+01 1.7319082568288064e+01 1.5832240015066093e+01 1.1889468934637401e+01 7.0260166885118247e+00 3.4084227701886709e+00 1.5903858359584313e+00 6.4628466394257855e-01 2.9208281149091886e-01 1.3680522046018677e+00 3.7148191371537109e+00 5.3471095100773640e+00 4.5999975157196396e+00 1.8618036691603201e+00 -1.1675467460557312e+00 -3.2014095470489683e+00 -4.1751621005792883e+00 -4.4441528031142692e+00 -3.7587484122597536e+00 -2.0282422842477690e+00 -4.8339177039881087e-01 -2.6711860399748077e-01 -3.8790549777032224e-01 8.5572500658487782e-01 3.0975713952792994e+00 4.3374149891908393e+00 3.2920941855788466e+00 3.9295487513328070e-01 -2.4783937608256150e+00 -3.2055601186298843e+00 -1.7160184560799154e+00 -6.3702741131976026e-01 -2.2383884758776662e+00 -5.5570116369254112e+00 -8.2084983887639105e+00 -9.5917739048230395e+00 -1.0331039070410487e+01 -1.0417944274770905e+01 -9.6648501662164410e+00 -8.3293893029282344e+00 -6.5549338022834966e+00 -4.3174230553074713e+00 - -1.9521686822118689e+01 -2.0828890952194818e+01 -2.1026487257169343e+01 -2.0644194340000958e+01 -2.0356661177644074e+01 -2.0486033910028034e+01 -2.0592315338305198e+01 -1.9336484142295909e+01 -1.5938473018577051e+01 -1.1921626681007268e+01 -9.9587671774048481e+00 -1.0450212320468967e+01 -1.0318268307692627e+01 -5.6239283444522234e+00 5.5321151308175374e+00 2.2417493712423962e+01 4.2078909484239603e+01 5.9973160754273493e+01 7.1702694488382562e+01 7.5072713982579657e+01 7.0924560394362075e+01 6.2663339872630232e+01 5.4356911122207862e+01 4.7989283986803173e+01 4.2765855626087045e+01 3.7486699590135927e+01 3.2734101027576685e+01 2.9993606241210717e+01 2.9069222310304095e+01 2.7645584279325117e+01 2.3767330134444784e+01 1.7501891196670105e+01 1.0097030989509490e+01 3.3105933448286935e+00 -4.4358692407392453e-01 4.2309373559202790e-01 4.6272362980750925e+00 9.2452802627883770e+00 1.2881829396441868e+01 1.6139200491488932e+01 1.9696338650752981e+01 2.2945254570047432e+01 2.4052713164028159e+01 2.1836262493236529e+01 1.7755371912217502e+01 1.4544324719156007e+01 1.3076166058655325e+01 1.2023365019266210e+01 9.9330468323536198e+00 6.5642773658552329e+00 2.6211689892425389e+00 -1.2463040312411424e+00 -4.6931152521918520e+00 -7.0610915480926959e+00 -7.8896098442295468e+00 -8.1377876309837323e+00 -9.2879431013150882e+00 -1.1118321847684978e+01 -1.1512230767538767e+01 -8.6570795004612968e+00 -3.0565564637606264e+00 2.6614202613936517e+00 5.7492933678780842e+00 5.1870636768943843e+00 - -1.2768528401242900e+01 -1.7338863074172309e+01 -2.0237569092156448e+01 -2.0576492513490894e+01 -1.9431123286614632e+01 -1.8641772316619317e+01 -1.8943170420368556e+01 -1.9167043655793719e+01 -1.7528873877656416e+01 -1.3999909814005159e+01 -1.0805366953545310e+01 -9.6142597372311247e+00 -8.9149462386606366e+00 -5.4471587606889269e+00 2.2906924892770721e+00 1.2948039993449356e+01 2.3895472387004183e+01 3.2785154492570712e+01 3.8224124421928316e+01 3.9996932598102731e+01 3.8917325110890907e+01 3.6404969952831500e+01 3.3603549356134771e+01 3.0482131529017348e+01 2.6418551514199979e+01 2.1753968035179085e+01 1.7865996396778279e+01 1.5605745867809237e+01 1.4445507979298107e+01 1.3294236667868901e+01 1.1519440091973062e+01 9.2364945825762419e+00 7.2532314388618229e+00 6.2602214003637382e+00 5.4987881290564387e+00 3.2320466512581985e+00 -9.0506248164303926e-01 -4.9456160363826012e+00 -6.3608335770462485e+00 -4.2734547891318062e+00 4.6309862256935785e-01 6.7021945441538548e+00 1.3111777285636606e+01 1.7514480785911530e+01 1.8248810290360364e+01 1.5766174142459235e+01 1.1757377659322158e+01 7.9733313460406592e+00 6.0274343310982506e+00 6.1770944745629279e+00 6.2936748428494491e+00 4.0368463533344734e+00 -4.2685170771143977e-02 -3.1865409383399417e+00 -3.8901205566515773e+00 -2.9576974863775090e+00 -1.4738094422502874e+00 4.0118819835881747e-01 2.6525695143418675e+00 4.6014650691595245e+00 5.1565376735843209e+00 4.0636905672697523e+00 2.6453975037372648e+00 2.3284476158012066e+00 - -1.3007839852110759e+01 -1.1025538159980380e+01 -1.0686371074924564e+01 -1.2744092667984233e+01 -1.5642265829843563e+01 -1.7140214084438682e+01 -1.6617034625505930e+01 -1.5300909110451405e+01 -1.4577809836556332e+01 -1.4955079002970214e+01 -1.5654258710135920e+01 -1.4407648124599390e+01 -9.0954740513659118e+00 -6.2982400317265430e-02 1.0332127909943164e+01 2.0193720207165487e+01 2.8972473785793127e+01 3.6317935037933722e+01 4.1210257687869564e+01 4.2631382536124164e+01 4.0972150948544574e+01 3.8156297398596216e+01 3.5717588235143509e+01 3.3207917004761363e+01 2.9304640994990272e+01 2.4112801189469973e+01 1.9353557646954098e+01 1.6308754098888908e+01 1.4516954476871140e+01 1.2392183920492100e+01 8.3965380716467521e+00 2.2326430594848903e+00 -4.3996278411324354e+00 -8.8062298459598374e+00 -9.2226405800100828e+00 -5.6733723455263254e+00 3.7975785593939992e-02 5.0496912837583618e+00 7.5061362215685907e+00 8.1297047935707027e+00 8.9880765155299258e+00 1.0960262021995606e+01 1.3066019970002715e+01 1.3738034632375317e+01 1.1896051602413694e+01 7.5099861905659733e+00 2.1853151873444245e+00 -1.4107524690116966e+00 -1.7374936024760426e+00 4.7474258642688838e-02 1.2777176662851293e+00 5.9151627918367655e-01 -9.4200465373474518e-01 -1.3835697125582160e+00 -1.1439767142326973e-02 2.0431818226269547e+00 2.8931387751496276e+00 1.7217225031925740e+00 -3.6057387583206468e-01 -1.4188595163897433e+00 -5.3401986141825453e-01 1.7331384545428845e+00 4.1028021413802733e+00 5.1530807234519207e+00 - -5.9423907462970762e+00 -6.6393370296983978e+00 -7.7903948031970467e+00 -9.0887616470108981e+00 -9.0730958856624682e+00 -7.3051960638397269e+00 -5.1092125692791504e+00 -4.1499019811620803e+00 -5.1825095304735012e+00 -7.4650822660744725e+00 -8.7858341576439987e+00 -6.9165862119378128e+00 -1.1152780425762092e+00 7.4456360751231196e+00 1.5504355240213883e+01 1.9891995742575336e+01 2.1880531164099967e+01 2.5705009499507383e+01 3.1274011045582068e+01 3.4010306181075244e+01 3.2770325333513270e+01 3.0948804502035770e+01 2.9539194246932787e+01 2.6197352223405726e+01 2.0544443634266784e+01 1.4590436095771173e+01 9.4132762585289544e+00 5.0726802461989733e+00 1.8845604581530462e+00 -2.0830751870538844e-01 -2.0248221543626945e+00 -3.9513021238798549e+00 -5.3937857883630116e+00 -5.7622214722998990e+00 -5.0964393510051575e+00 -3.6685468807689410e+00 -1.9925824037801600e+00 -1.3116773229001528e+00 -2.6369224209795075e+00 -4.8818299037998507e+00 -5.7575190263162872e+00 -4.9151597170028811e+00 -4.2580797535905948e+00 -5.3162000432783687e+00 -8.0260447395650854e+00 -1.1124603514599908e+01 -1.2372738496954803e+01 -9.9327640511974646e+00 -4.8837906274471230e+00 -8.7396451836104794e-01 -1.7270469647056230e-01 -1.5776571699924549e+00 -2.7982572746004415e+00 -3.1062409337303580e+00 -3.0845221509902201e+00 -3.0432684148630136e+00 -2.5011943998532136e+00 -1.1481969001182208e+00 3.2261647132979526e-01 7.8134574976169557e-01 -1.2641763242828769e-01 -1.4193228153058111e+00 -1.7047633076684265e+00 -8.4627935364228413e-01 - -3.3511583497758619e+00 -5.1854891048629845e+00 -8.2045548190539002e+00 -1.1321927344458468e+01 -1.3232156516018623e+01 -1.2891455905593750e+01 -1.0308983110741357e+01 -7.2770589395120400e+00 -6.3230641518260899e+00 -7.7668924711145673e+00 -8.5533116261484974e+00 -5.1754472720840532e+00 2.5464912688278725e+00 1.1150146477409665e+01 1.6749765390457195e+01 1.8459480819125350e+01 1.8853443625394785e+01 2.0968383227964456e+01 2.4745356436846293e+01 2.6900729147509413e+01 2.4586028405507879e+01 1.8423893823872788e+01 1.1687464461034605e+01 7.4694246602111161e+00 6.5063740610776568e+00 6.6399254866014452e+00 4.8727847407542608e+00 6.2948434274342469e-01 -3.8180925201354374e+00 -5.9745650071051903e+00 -5.0248037653094384e+00 -1.7240345099049890e+00 2.4707226972860665e+00 6.1789893211512830e+00 8.4606300098802265e+00 9.1837056122684562e+00 9.4175247215558571e+00 1.0361111194478536e+01 1.1355825197106681e+01 1.0443694556801294e+01 7.3275805488596522e+00 4.0729400267880624e+00 2.7849489919865249e+00 4.0449011920910980e+00 6.8903062423979726e+00 8.8579270913084418e+00 7.2409153007186635e+00 1.9815538314749910e+00 -3.4963983686766915e+00 -5.7025552070066450e+00 -4.6316183670653945e+00 -2.8570944089319230e+00 -2.1945685959883381e+00 -2.7295179820568904e+00 -4.1440094951781976e+00 -6.1739578627827907e+00 -7.9441831061071788e+00 -8.3008782660488869e+00 -7.1697557563545278e+00 -5.6642787357607363e+00 -4.5049090776747240e+00 -3.1919541675350729e+00 -9.9748944534117390e-01 1.8822644573222371e+00 - 1.6022978144703672e+00 -1.4311786252489094e+00 -4.9990745496272506e+00 -7.1229734861916816e+00 -6.0298488474363783e+00 -1.8843154305695582e+00 2.8057312397445893e+00 5.3045500670547181e+00 5.2217607939007324e+00 4.6015143428315151e+00 5.3985890315998715e+00 7.6627280386075034e+00 1.0581969356003244e+01 1.3680704869130523e+01 1.6133038629367640e+01 1.6836134617835810e+01 1.5985757935422852e+01 1.5071121283266066e+01 1.5015214850097710e+01 1.5311877473734373e+01 1.4952636894507775e+01 1.3509895518873545e+01 1.1287529167684998e+01 8.8944719667341001e+00 6.8056307961237392e+00 5.1599082109694479e+00 3.9096368310365088e+00 3.0884796567205659e+00 2.8027781380901557e+00 2.7427378963657629e+00 1.8080112317158328e+00 -7.2093739358622477e-01 -3.2256888875692162e+00 -2.5485135905025946e+00 2.1938957914977939e+00 7.8096722519713868e+00 1.0108587362109473e+01 8.0119639048454889e+00 3.7875674256494771e+00 -1.7909623605085379e-01 -3.6408942410859053e+00 -7.2043954289116119e+00 -1.0036996806105844e+01 -1.0480191286127010e+01 -8.6104715538045582e+00 -6.5737607267403781e+00 -6.4789888225111065e+00 -8.9832745466307422e+00 -1.3098938079216277e+01 -1.6488189562785699e+01 -1.7072074337181476e+01 -1.4675862164708811e+01 -1.0393673234405423e+01 -5.7706460525680994e+00 -3.2783034075657795e+00 -4.8699351422351356e+00 -9.0537498769047904e+00 -1.1681066334269010e+01 -1.0244235531230570e+01 -5.9719523689580347e+00 -1.9644630163354910e+00 -6.8837039873644690e-01 -2.7600482002062674e+00 -6.6765535392302047e+00 - -2.6793902875688205e+00 4.3700049391893547e-01 5.2207839165959946e-01 -2.5289383815408999e+00 -6.4357928708025778e+00 -8.6801895727348093e+00 -8.6519420551714941e+00 -7.5157946794804973e+00 -6.9229481363763350e+00 -7.7994153444953156e+00 -9.3675453247907754e+00 -1.0006058503692298e+01 -9.5066143246051311e+00 -9.1193248923592503e+00 -9.0487355120456403e+00 -6.9972012375818347e+00 -1.6948530860574240e-01 1.0752688787226244e+01 2.0728963668504598e+01 2.4824092328193505e+01 2.2702194691381209e+01 1.8103111981612606e+01 1.5186220180215653e+01 1.5435411462559298e+01 1.6851176785915300e+01 1.6256911281706046e+01 1.2853830842714240e+01 8.7992906000849906e+00 6.3848237032232804e+00 6.1798137367968202e+00 7.8154545356284144e+00 1.0467840599577846e+01 1.2480989360982822e+01 1.2582337684046703e+01 1.1590145096205708e+01 1.1337803770903093e+01 1.2054473739609797e+01 1.2250402949861966e+01 1.0983877637654345e+01 8.6551288071765491e+00 5.8174031977868585e+00 2.9188587271482644e+00 8.3930412496566609e-01 2.3622649323786860e-01 7.1444423031664739e-01 1.2655853456754595e+00 1.3229336834759118e+00 1.3578720434847311e+00 2.1076821170226498e+00 3.0728270343869779e+00 2.7843524321077324e+00 9.0852714062149698e-01 -1.0017936404451253e+00 -1.3974751167516584e+00 -6.4988794902308711e-01 -5.0807738982896855e-01 -2.0685431131427148e+00 -4.6510643108399936e+00 -6.4865750445095642e+00 -6.4858500824649585e+00 -5.1351447719989922e+00 -3.4051840546128562e+00 -1.7184951490663052e+00 -5.0407691071619776e-01 - -3.6981497476642828e+00 -3.0390710048817726e+00 -1.4989922553639727e+00 1.0284003810800462e+00 3.6622719317496890e+00 4.7174258831055269e+00 3.5121017080473034e+00 2.1663525068616294e+00 3.8933497703894613e+00 8.9756839635313099e+00 1.4369540972251285e+01 1.7517537513748934e+01 1.8367043951422612e+01 1.7487527428352760e+01 1.4910602011410518e+01 1.1788546361447585e+01 1.0695666486764603e+01 1.2768873027185609e+01 1.6000928788239836e+01 1.7360605935279679e+01 1.5930200170467559e+01 1.3260280784655849e+01 1.0849461771852765e+01 8.4797732256321510e+00 5.3784098602866708e+00 1.7165101724011995e+00 -1.4089618767998704e+00 -2.7952639837436752e+00 -2.2276522503149407e+00 -9.5316276326730420e-01 -5.3299313777074875e-01 -1.5019739358173170e+00 -3.5515201972803432e+00 -6.3187612986855708e+00 -9.3137337609793054e+00 -1.1318424950944941e+01 -1.0816559293228659e+01 -7.7508782461822854e+00 -4.2600195254335116e+00 -2.8712335676010809e+00 -4.2724290569942225e+00 -6.8899881431600578e+00 -8.1742447353742449e+00 -6.8082430068650392e+00 -3.9299615464428408e+00 -1.4896688083453651e+00 -4.1155002553450060e-02 6.2487647569787685e-01 -2.9020287195242966e-01 -3.8853316012394776e+00 -9.2125322820605025e+00 -1.3050926297558480e+01 -1.2737108773295276e+01 -8.7818443279864002e+00 -4.3127261410662650e+00 -2.3302722768023965e+00 -3.5325752167188584e+00 -6.2805892811261916e+00 -8.5016614679139195e+00 -9.4759820546177966e+00 -9.7204496390574704e+00 -9.7546714698047463e+00 -9.5752380341344967e+00 -9.3036539221877597e+00 - -1.7414985024478632e+01 -1.7710392269365567e+01 -1.8830345987045778e+01 -2.0905808395741552e+01 -2.3089781413981267e+01 -2.3899680760146289e+01 -2.2409641713666709e+01 -1.8930485800816182e+01 -1.4601034805586801e+01 -1.0520204067883871e+01 -7.1082264501151942e+00 -4.1771499408845667e+00 -1.2985932549142798e+00 2.5888198678848857e+00 9.5645053119594277e+00 2.1215332248500019e+01 3.6152478620496041e+01 5.0021402817940384e+01 5.8774208038896376e+01 6.1292360127806575e+01 5.8550762166546200e+01 5.1882091757242364e+01 4.2968986888033839e+01 3.3918424522864342e+01 2.6125731850668569e+01 1.9638134139516286e+01 1.3840450777647449e+01 8.3996525004490294e+00 3.8021795817073860e+00 1.1190628236490561e+00 7.5962516407305214e-01 1.4058807055741134e+00 8.2163118862213358e-01 -2.0188943881722592e+00 -6.0499550558624495e+00 -9.4755634193659208e+00 -1.1248638187248371e+01 -1.0876954767354995e+01 -7.8849814096440030e+00 -2.2277330312359691e+00 5.4138464272246747e+00 1.3796881454212489e+01 2.0984396320719696e+01 2.4893133374375935e+01 2.4942654855570076e+01 2.2326104353812248e+01 1.8202892776374178e+01 1.2706268587571685e+01 5.8859262076676249e+00 -1.1947559660524161e+00 -6.5689254319385437e+00 -8.7772879515580264e+00 -8.1899879887551386e+00 -6.5832072140625550e+00 -5.4688753261733805e+00 -4.9175403512819100e+00 -3.8699212831075047e+00 -1.6574438279084263e+00 8.5455765389835547e-01 2.0911729100665450e+00 1.8202474430112003e+00 1.6089416985684442e+00 2.9743808071641795e+00 5.8948563383564005e+00 - -3.3338514474187440e+00 -4.1130389374748058e+00 -5.1547704706642552e+00 -5.4193898987702704e+00 -4.0201652371671948e+00 -1.8076212696539353e+00 -8.9324938220319039e-01 -1.9868709786288514e+00 -3.3105995821020633e+00 -2.6515298234057667e+00 5.1903435078819071e-01 4.9231535819829615e+00 8.5469168923530585e+00 1.0071331008104004e+01 9.9283440319809362e+00 9.9281856288240444e+00 1.1689028944723646e+01 1.5180332000597723e+01 1.8495089144282854e+01 1.9468749633612084e+01 1.7774035163293483e+01 1.4795935112020807e+01 1.1632060118366278e+01 8.3107388002809923e+00 4.7969367455220766e+00 1.5228719476515731e+00 -1.2245839851149136e+00 -3.6724142837818849e+00 -6.1258400070447641e+00 -8.3043113945111795e+00 -9.2382296924864278e+00 -8.2818369343941640e+00 -6.3024540208842872e+00 -5.1153884354297716e+00 -5.4012598664097871e+00 -5.9086039260782988e+00 -5.2469498739198066e+00 -3.8701397078727497e+00 -3.4699836914403539e+00 -4.6999263838482115e+00 -6.4704904874754252e+00 -7.1253409419073517e+00 -5.1911313857217234e+00 -5.8248621615308416e-02 6.3735411041921815e+00 9.9842526040059774e+00 8.0901302020184040e+00 2.0765046689514737e+00 -4.0814624682782448e+00 -6.9335386550902367e+00 -5.3396830806898752e+00 -6.1569207664081205e-01 4.2371402928715165e+00 6.1453027828107594e+00 4.0729416305299182e+00 -5.6892224917549727e-01 -5.7266782214867291e+00 -9.4817569691134302e+00 -9.4466837493039364e+00 -4.4716815460577326e+00 2.5480231258059796e+00 6.3862725120871939e+00 4.7626827206150235e+00 2.7250354238558483e-01 - -6.6519531318265823e+00 -5.1307688760840637e+00 -1.5990450796308475e+00 3.2882196698155921e+00 7.5434887899979923e+00 9.8601744151731392e+00 1.0431338476778441e+01 1.0132207096708374e+01 9.8633193310657052e+00 1.0117849589142009e+01 1.0556320935364319e+01 1.0529237565608828e+01 1.0064399849178292e+01 9.8902096970315743e+00 1.0802538876610429e+01 1.3065613777540369e+01 1.5904751019353327e+01 1.7996878960824926e+01 1.8887262226773334e+01 1.9137392198836288e+01 1.8914272666242024e+01 1.7510508054314936e+01 1.4350155185280446e+01 9.5073777371400769e+00 3.7350791719348706e+00 -9.6044902572999469e-01 -2.1523757668680674e+00 1.2726446256922297e-01 2.7975425794223376e+00 3.1310049165812357e+00 1.4400885075259127e+00 -9.4699453877260531e-03 5.2252907539724913e-01 2.9730000242572210e+00 5.8714424054021102e+00 7.9092364851498598e+00 8.7344188791480644e+00 7.9030314193993458e+00 4.5624392185691374e+00 -5.8583551307959691e-01 -4.3527827486539321e+00 -3.8542689679266942e+00 -3.3407572539104535e-02 3.0190256438646816e+00 2.5671043830395353e+00 -2.7379787916712028e-01 -2.8159393124422332e+00 -3.9496120934673513e+00 -4.3869096765280995e+00 -4.8326892938827388e+00 -4.8327167767960031e+00 -3.6048592531173460e+00 -1.4049856452797012e+00 6.1244691414035868e-01 1.4045344432842721e+00 4.0378165896259843e-01 -2.2594911404427971e+00 -5.2758916592334257e+00 -6.6711436023807975e+00 -5.4988956630405870e+00 -2.7595297745975564e+00 -4.3083978002099643e-01 1.2310669289941012e-02 -1.8074724020788719e+00 - -2.5417468582312877e+00 -1.9298735695592950e-01 1.4524591906329190e+00 1.4111623962585536e+00 -3.3194621049731687e-01 -2.5702905387021753e+00 -3.3145376224302021e+00 -1.6531002526600589e+00 1.5085470936791965e+00 4.9384825610490894e+00 8.1158180601966112e+00 1.0592317046651424e+01 1.1720352844683582e+01 1.1731547138555802e+01 1.1880590058094510e+01 1.2779413370052000e+01 1.4075646883094780e+01 1.5918248275430194e+01 1.8326489365386571e+01 1.9316206123129387e+01 1.6690629155310674e+01 1.1661681239753394e+01 8.0053968635282153e+00 7.5533291045585287e+00 8.4805002524471416e+00 7.9431009917907147e+00 4.7893096294793169e+00 -2.9205326061448478e-01 -5.9299905729305458e+00 -1.0889025474342041e+01 -1.4253802911511571e+01 -1.5424425634403272e+01 -1.4219613607718578e+01 -1.0988129270893715e+01 -6.7271231240914489e+00 -2.8803629271713325e+00 -5.3221218228894118e-01 -1.4819897860306691e-01 -2.2309622192950278e+00 -6.7555441266626337e+00 -1.1288796495269558e+01 -1.1879369455774969e+01 -7.6695223775198915e+00 -2.7345250210875287e+00 -1.2464522929563033e+00 -2.5982520939275595e+00 -3.2907753012277650e+00 -2.0946248103194671e+00 -1.2670396502483379e+00 -2.9734875330694628e+00 -6.1944576306095183e+00 -8.0259110770747917e+00 -7.0406747672630017e+00 -4.3818830819913117e+00 -2.2337026899490038e+00 -1.7757039249321303e+00 -2.1690903031406541e+00 -1.7112483834128271e+00 -2.2443726961269805e-01 7.8312271984712389e-01 7.2340304513709708e-01 9.8143716696324113e-01 2.5456848227724063e+00 4.1446278797649949e+00 - 5.9509298374097952e+00 5.0678810528340978e+00 4.6985100256137695e+00 5.6806474798139019e+00 7.1270807068985986e+00 7.3896907513926973e+00 6.1956015245970972e+00 5.5852197604153861e+00 7.9143806050507850e+00 1.2903336221013145e+01 1.7478615210727796e+01 1.8733526717119613e+01 1.6557524998442712e+01 1.3237833414525580e+01 1.1194587213855545e+01 1.1742137197718762e+01 1.5292487123411739e+01 2.1089872768798958e+01 2.6615090233197662e+01 2.9112067948790987e+01 2.8381112807191901e+01 2.6745309556602031e+01 2.6230622139582557e+01 2.6873626067912664e+01 2.7379954661291766e+01 2.6724918542001461e+01 2.5352283105591539e+01 2.4558243711441179e+01 2.4278527981980282e+01 2.2429473534878777e+01 1.7501264178786681e+01 1.0987541340332779e+01 5.6451288588220212e+00 1.5426875751835269e+00 -4.2209755312065607e+00 -1.3196792287091672e+01 -2.2788705236686901e+01 -2.9082273423343587e+01 -3.1121693514905303e+01 -3.0849786691440926e+01 -2.9901975701231901e+01 -2.8187902353798506e+01 -2.4897002861955549e+01 -1.9629404309588349e+01 -1.3381778061564034e+01 -8.8077830839680740e+00 -8.0332756560395637e+00 -1.0120926859300432e+01 -1.2004163217689651e+01 -1.1842485990658060e+01 -1.0575962516702482e+01 -1.0149295488895902e+01 -1.0971618377841839e+01 -1.1645676947992518e+01 -1.0824020132311130e+01 -8.7473035146007145e+00 -6.7816366706045397e+00 -5.8695256679524599e+00 -6.1984801392743734e+00 -7.8537905857434129e+00 -1.0416960930281348e+01 -1.2429989647191839e+01 -1.2650740393840184e+01 -1.1521198276477271e+01 - ] -) -check(joinpath(dirname(@__FILE__), "big_endian.mat"), result) - -# test reading of mxOBJECT_CLASS (#57) -let objtestfile = "obj.mat" - vars = matread(joinpath(dirname(@__FILE__), objtestfile)) - # check if all variables were read - for key in ("A", "tm", "signal") - @test key in keys(vars) +@testset "test reading of mxOBJECT_CLASS (#57)" begin + let objtestfile = "obj.mat" + vars = matread(joinpath(dirname(@__FILE__), objtestfile)) + # check if all variables were read + for key in ("A", "tm", "signal") + @test key in keys(vars) + end + # check if class name was read correctly + @test vars["A"]["class"] == "Assoc" end - # check if class name was read correctly - @test vars["A"]["class"] == "Assoc" end -# test reading of empty struct -let objtestfile = "empty_struct.mat" - vars = matread(joinpath(dirname(@__FILE__), objtestfile)) - @test "a" in keys(vars) - @test vars["a"]["size"] == [] - @test vars["a"]["params"] == [] +@testset "test reading of empty struct" begin + let objtestfile = "empty_struct.mat" + vars = matread(joinpath(dirname(@__FILE__), objtestfile)) + @test "a" in keys(vars) + @test vars["a"]["size"] == [] + @test vars["a"]["params"] == [] + end end -# test reading of a Matlab figure -let objtestfile = "figure.fig" - vars = matread(joinpath(dirname(@__FILE__), objtestfile)) - @test "hgS_070000" in keys(vars) - @test vars["hgS_070000"]["handle"] == 1.0 - @test vars["hgS_070000"]["type"] == "figure" +@testset "test reading of a Matlab figure" begin + let objtestfile = "figure.fig" + vars = matread(joinpath(dirname(@__FILE__), objtestfile)) + @test "hgS_070000" in keys(vars) + @test vars["hgS_070000"]["handle"] == 1.0 + @test vars["hgS_070000"]["type"] == "figure" + end end + # test reading file containing Matlab function handle, table, and datetime objects # since we don't support these objects, just make sure that there are no errors # reading the file and that the variables are there and replaced with `missing` -let objtestfile = "function_handles.mat" - vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile)) - @test "sin" in keys(vars) - @test ismissing(vars["sin"]) - @test "anonymous" in keys(vars) - @test ismissing(vars["anonymous"]) +@testset "test reading unsupported function_handles" begin + let objtestfile = "function_handles.mat" + vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile)) + @test "sin" in keys(vars) + @test ismissing(vars["sin"]) + @test "anonymous" in keys(vars) + @test ismissing(vars["anonymous"]) + end end -let objtestfile = "struct_table_datetime.mat" - vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile))["s"] - @test "testTable" in keys(vars) - @test ismissing(vars["testTable"]) - @test "testDatetime" in keys(vars) - @test ismissing(vars["testDatetime"]) + +@testset "test reading unsupported struct, table, and datetime objects" begin + let objtestfile = "struct_table_datetime.mat" + vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile))["s"] + @test "testTable" in keys(vars) + @test ismissing(vars["testTable"]) + @test "testDatetime" in keys(vars) + @test ismissing(vars["testDatetime"]) + end end diff --git a/test/write.jl b/test/write.jl index 3a1d820..d9e6c6d 100644 --- a/test/write.jl +++ b/test/write.jl @@ -96,9 +96,11 @@ test_write(Dict( "sparse_zeros" => SparseMatrixCSC(20, 20, ones(Int, 21), Int[], Float64[]) )) -@test_throws ErrorException test_write(Dict("1invalidkey" => "starts with a number")) -@test_throws ErrorException test_write(Dict("another invalid key" => "invalid characters")) -@test_throws ErrorException test_write(Dict("yetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkey" => "too long")) +@testset "write exceptions" begin + @test_throws ErrorException test_write(Dict("1invalidkey" => "starts with a number")) + @test_throws ErrorException test_write(Dict("another invalid key" => "invalid characters")) + @test_throws ErrorException test_write(Dict("yetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkey" => "too long")) +end struct TestCompositeKind field1::AbstractString From 0998bde9a75f3c98ce600ed47499c05701e9d144 Mon Sep 17 00:00:00 2001 From: Brian Quadras Date: Fri, 19 May 2023 22:31:17 +0530 Subject: [PATCH 23/91] Added support for Dymola .mat files --- src/MAT_v4_Modelica.jl | 24 ++++++++++++------------ test/runtests_modelica.jl | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index 55eac3b..ca611c9 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -134,7 +134,7 @@ function readAclass( filepath::String ) end nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - name = replace(String(nameuint), '\0'=>"") + name = strip(replace(String(nameuint), '\0'=>"")) if name != "Aclass" error("First matrix must be named Aclass, is instead [$name]. This likely means that [$filepath] is not a Modelica MAT v4 file.") end @@ -143,10 +143,10 @@ function readAclass( filepath::String ) fmt = dataFormat(dtype) # read the format type before reading realint = read!(matio, Matrix{UInt8}(undef, nrows,ncols)) - Aclass1 = replace(String(realint[1,:]), '\0'=>"") - Aclass2 = replace(String(realint[2,:]), '\0'=>"") - Aclass3 = replace(String(realint[3,:]), '\0'=>"") - Aclass4 = replace(String(realint[4,:]), '\0'=>"") + Aclass1 = strip(replace(String(realint[1,:]), '\0'=>"")) + Aclass2 = strip(replace(String(realint[2,:]), '\0'=>"")) + Aclass3 = strip(replace(String(realint[3,:]), '\0'=>"")) + Aclass4 = strip(replace(String(realint[4,:]), '\0'=>"")) if Aclass1 == "Atrajectory" && Aclass2 == "1.1" && isempty(Aclass3) && Aclass4 == "binNormal" || Aclass4 == "binTrans" return Aclass( filepath, Aclass4 == "binTranspose", startP, position(matio) ) end @@ -180,7 +180,7 @@ function readVariableNames(ac::Aclass) #read the matrix name nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") + matrixName = strip(replace(String(nameuint), '\0'=>"")) if matrixName != "name" error("trying to read matrix [name] but read [$matrixName]") end @@ -197,7 +197,7 @@ function readVariableNames(ac::Aclass) #pull the names out of the matrix vnames = [] for i in 1:ncols - push!(vnames, replace(String(realint[:,i]), '\0'=>"")) # note :,1 = implicit transpose + push!(vnames, strip(replace(String(realint[:,i]), '\0'=>""))) # note :,1 = implicit transpose end return VariableNames(vnames, startP, position(matio)) @@ -247,7 +247,7 @@ function readVariableDescriptions(ac::Aclass, vn::VariableNames) #read the matrix name nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") + matrixName = strip(replace(String(nameuint), '\0'=>"")) if matrixName != "description" error("trying to read matrix [description] but read [$matrixName]") end @@ -263,7 +263,7 @@ function readVariableDescriptions(ac::Aclass, vn::VariableNames) vdesc = [] for i in 1:ncols - push!(vdesc, replace(String(realread[:,i]), '\0'=>"")) # note :,1 = implicit transpose + push!(vdesc, strip(replace(String(realread[:,i]), '\0'=>""))) # note :,1 = implicit transpose end return VariableDescriptions(vn.names, vdesc, startP, position(matio)) @@ -306,7 +306,7 @@ function readDataInfo(ac::Aclass, vd::VariableDescriptions) #read the matrix name nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") + matrixName = strip(replace(String(nameuint), '\0'=>"")) if matrixName != "dataInfo" error("trying to read variable [dataInfo] but read [$matrixName]") end @@ -355,7 +355,7 @@ function readMatrixHeader!(matio::IOStream) :: MatrixHeader # data1MatrixName = mark(matio) nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") + matrixName = strip(replace(String(nameuint), '\0'=>"")) fmt = dataFormat(dtype) # read the format type before reading @@ -406,7 +406,7 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d return readns end - elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 + elseif name == "Time" || name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 #read the matrix data_2 if ac.isTranspose == false # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 2e29e4e..ce7e9a8 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -15,8 +15,8 @@ include("../src/MAT.jl") #OpenModelica v1.19.0 matBBO = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_res.mat") matFBB = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_res.mat") -#Dymola v2021 -- not implemented -# matBBD = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") +#Dymola v2021 +matBBD = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") @testset "isLittleEndian" begin @@ -85,7 +85,7 @@ end @test di.info[11]["isWithinTimeRange"] == 0 end -@testset "readVariable: BouncingBall" begin +@testset "readVariable: BouncingBall OpenModelica" begin ac = MAT.MAT_v4_Modelica.readAclass(matBBO) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) @@ -112,6 +112,33 @@ end @test isapprox(vel[2], -0.981, rtol=1e-3) end +@testset "readVariable: BouncingBall Dymola" begin + ac = MAT.MAT_v4_Modelica.readAclass(matBBD) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) + + eff = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 + @test length(eff) == 2 + @test eff[1] ≈ 0.77 + @test eff[2] ≈ 0.77 + + grav = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 + @test length(grav) == 2 + @test grav[1] ≈ 9.81 + @test grav[2] ≈ 9.81 + + Time = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "Time") # data0 + @test all(isapprox.(Time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) + + height = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 + @test isapprox(height[1], 111, rtol=1e-3) + @test isapprox(height[2], 110.9509, rtol=1e-3) + + vel = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 + @test isapprox(vel[2], -0.981, rtol=1e-3) +end + @testset "readVariable: FallingBodyBox" begin ac = MAT.MAT_v4_Modelica.readAclass(matFBB) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) From 508d164daeb09cacb0a36c7fc0a847af15513aef Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Fri, 9 Jun 2023 15:34:13 -0500 Subject: [PATCH 24/91] remove DataFrames --- Project.toml | 1 - src/MAT_v4_Modelica.jl | 45 +-------------------------------------- test/runtests_modelica.jl | 24 ++++++++++----------- 3 files changed, 13 insertions(+), 57 deletions(-) diff --git a/Project.toml b/Project.toml index 0aac4ec..b7d7067 100644 --- a/Project.toml +++ b/Project.toml @@ -5,7 +5,6 @@ version = "0.10.4" [deps] BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" -DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index ca611c9..0922162 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -1,5 +1,5 @@ # A module to read MAT files written by OpenModelica tools - + # Copyright (C) 2023 Ben Conrad # # Permission is hereby granted, free of charge, to any person obtaining @@ -47,7 +47,6 @@ module MAT_v4_Modelica -using DataFrames function isLittleEndian(dtype) :: Bool #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... @@ -425,46 +424,4 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d end #open end - -""" -All-in-one reading of variable `name` from `filepath`, returning a DataFrame with columns "time" and `name` -""" -function readVariable(filepath::String, name::String) :: DataFrame - ac = readAclass(filepath) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) - di = readDataInfo(ac,vd) - - time = readVariable(ac, vn, vd, di, "time") - varn = readVariable(ac, vn, vd, di, name) - - df = DataFrame("time"=>time, name=>varn) - return df -end - -""" -Reads the vector of variable `names` from mat file `filepath`, returning a DataFrame with columns "time" and `names` -""" -function readVariables(filepath::String, names::AbstractVector{T}) :: DataFrame where T<:AbstractString - ac = readAclass(filepath) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) - di = readDataInfo(ac,vd) - - df = DataFrame("time"=> readVariable(ac, vn, vd, di, "time") ) - for name in names - var = readVariable(ac,vn,vd,di, name) - if length(var) == 1 # a constant value - df[!, name] .= var[1] - elseif length(var) == 2 && var[1] == var[2] - df[!, name] .= var[1] - elseif length(var) == length(df.time) - df[!, name] = var - else - throw(DimensionMismatch("Length of $name [$(length(var))] differs from the dataframe [$(length(df.time))], cannot add it")) - end - end - return df -end - end #MAT_v4_Modelica \ No newline at end of file diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index ce7e9a8..8ca0b90 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -172,18 +172,18 @@ end @test isapprox(var[1], 1.0, rtol=1e-3) end -@testset "all-in-one readVariable" begin - data = MAT.MAT_v4_Modelica.readVariable(matFBB, "bodyBox.frame_a.r_0[1]") - @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) - @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(matFBB, "nullVariable") -end - -@testset "all-in-one readVariables" begin - data = MAT.MAT_v4_Modelica.readVariables(matFBB, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) - @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) - @test isapprox(data[!,"bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) - @test isapprox(data[!,"world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time -end +# @testset "all-in-one readVariable" begin +# data = MAT.MAT_v4_Modelica.readVariable(matFBB, "bodyBox.frame_a.r_0[1]") +# @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) +# @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(matFBB, "nullVariable") +# end + +# @testset "all-in-one readVariables" begin +# data = MAT.MAT_v4_Modelica.readVariables(matFBB, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) +# @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) +# @test isapprox(data[!,"bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) +# @test isapprox(data[!,"world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time +# end ; From b2276e6dc761ebf591e2c805d144bb7fefc240e1 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Fri, 9 Jun 2023 16:41:47 -0500 Subject: [PATCH 25/91] add isMatV4Modelica() --- src/MAT_v4_Modelica.jl | 20 +++++++ test/runtests_modelica.jl | 49 +++++++++--------- ...Ball_res.mat => BouncingBall_om1.19.0.mat} | Bin ...ox_res.mat => FallingBodyBox_om1.19.0.mat} | Bin 4 files changed, 44 insertions(+), 25 deletions(-) rename test/v4_Modelica/BouncingBall/{BouncingBall_res.mat => BouncingBall_om1.19.0.mat} (100%) rename test/v4_Modelica/FallingBodyBox/{FallingBodyBox_res.mat => FallingBodyBox_om1.19.0.mat} (100%) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index 0922162..1dedddb 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -424,4 +424,24 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d end #open end +""" +Determine whether the `filepath` is a mat file in the Modelica format. +""" +function isMatV4Modelica(filepath::String) + #first check the extension + ret = splitext(filepath)[2] == ".mat" || splitext(filepath)[2] == ".MAT" + + #now we need to interrogate the contents, by ensuring that all of the internal functions return correctly + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + ret &= typeof(ac) == Aclass + ret &= typeof(vn) == VariableNames + ret &= typeof(vd) == VariableDescriptions + ret &= typeof(di) == DataInfo + return ret; +end + end #MAT_v4_Modelica \ No newline at end of file diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 8ca0b90..109db17 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -13,10 +13,11 @@ cd(joinpath(@__DIR__,"..")) include("../src/MAT.jl") #OpenModelica v1.19.0 -matBBO = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_res.mat") -matFBB = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_res.mat") +bbOM = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_om1.19.0.mat") +fbbOM = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_om1.19.0.mat") + #Dymola v2021 -matBBD = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") +bbDy = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") @testset "isLittleEndian" begin @@ -38,14 +39,25 @@ end @test MAT.MAT_v4_Modelica.typeBytes(Int32) == 4 end +@testset "isModelicaFormat" begin + @test MAT.MAT_v4_Modelica.isMatV4Modelica( bbOM ); + @test MAT.MAT_v4_Modelica.isMatV4Modelica( fbbOM ); + @test MAT.MAT_v4_Modelica.isMatV4Modelica( bbDy ); + + #cursorily check negative coverage + @test_throws EOFError MAT.MAT_v4_Modelica.isMatV4Modelica( joinpath( @__DIR__, "v6", "array.mat") ) + @test_throws EOFError MAT.MAT_v4_Modelica.isMatV4Modelica( joinpath( @__DIR__, "v7", "array.mat") ) + @test_throws EOFError MAT.MAT_v4_Modelica.isMatV4Modelica( joinpath( @__DIR__, "v7.3", "array.mat") ) +end + @testset "Aclass" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) @test ac.positionStart == 0 @test ac.positionEnd == 71 end @testset "readVariableNames" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) # @show vn @test length(vn.names) == 11 @@ -57,14 +69,14 @@ end end @testset "getVariableIndex" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[10]) == 10 end @testset "readVariableDescriptions" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) @test length(vd.descriptions) == 11 @@ -74,7 +86,7 @@ end end @testset "readDataInfo" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) @@ -86,7 +98,7 @@ end end @testset "readVariable: BouncingBall OpenModelica" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) @@ -113,7 +125,7 @@ end end @testset "readVariable: BouncingBall Dymola" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBD) + ac = MAT.MAT_v4_Modelica.readAclass(bbDy) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) @@ -139,8 +151,8 @@ end @test isapprox(vel[2], -0.981, rtol=1e-3) end -@testset "readVariable: FallingBodyBox" begin - ac = MAT.MAT_v4_Modelica.readAclass(matFBB) +@testset "readVariable: FallingBodyBox OpenModelica" begin + ac = MAT.MAT_v4_Modelica.readAclass(fbbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) @@ -172,19 +184,6 @@ end @test isapprox(var[1], 1.0, rtol=1e-3) end -# @testset "all-in-one readVariable" begin -# data = MAT.MAT_v4_Modelica.readVariable(matFBB, "bodyBox.frame_a.r_0[1]") -# @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) -# @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(matFBB, "nullVariable") -# end - -# @testset "all-in-one readVariables" begin -# data = MAT.MAT_v4_Modelica.readVariables(matFBB, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) -# @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) -# @test isapprox(data[!,"bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) -# @test isapprox(data[!,"world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time -# end - ; diff --git a/test/v4_Modelica/BouncingBall/BouncingBall_res.mat b/test/v4_Modelica/BouncingBall/BouncingBall_om1.19.0.mat similarity index 100% rename from test/v4_Modelica/BouncingBall/BouncingBall_res.mat rename to test/v4_Modelica/BouncingBall/BouncingBall_om1.19.0.mat diff --git a/test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.mat b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_om1.19.0.mat similarity index 100% rename from test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.mat rename to test/v4_Modelica/FallingBodyBox/FallingBodyBox_om1.19.0.mat From 7f88e9a3c56cd3ed3d086685b3fcfd62d866dddb Mon Sep 17 00:00:00 2001 From: Brian Quadras Date: Sat, 10 Jun 2023 19:24:41 +0530 Subject: [PATCH 26/91] Added result file for FallingBodyBox_dymola2021 file and tests --- test/runtests_modelica.jl | 36 ++++++++++++++++++ .../FallingBodyBox_dymola2021.mat | Bin 0 -> 169803 bytes 2 files changed, 36 insertions(+) create mode 100644 test/v4_Modelica/FallingBodyBox/FallingBodyBox_dymola2021.mat diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 109db17..d501155 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -3,6 +3,7 @@ # test/v4_Modelica/BouncingBall/BouncingBall_res.mat - simulated with OpenModelica v1.19.0 # test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat - simulated with Dymola v2021 # test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with OpenModelica v1.19.0 +# test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) # These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents import Pkg @@ -18,6 +19,7 @@ fbbOM = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_om1.19 #Dymola v2021 bbDy = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") +fbbDy = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_dymola2021.mat") @testset "isLittleEndian" begin @@ -184,6 +186,40 @@ end @test isapprox(var[1], 1.0, rtol=1e-3) end +@testset "readVariable: FallingBodyBox Dymola" begin + ac = MAT.MAT_v4_Modelica.readAclass(fbbDy) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "Time") + + # display(var) + ret = true + for i = 2:length(var)-1 #last time is duplicated + ret &= isapprox(var[i]-var[i-1], 0.002, rtol=1e-4) + end + @test ret == true + + #point-check values read from FallingBodyBox_res.csv + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") + @test isapprox(var[16], 0.002923239, rtol=1e-2) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1, 1]") + @test isapprox(var[26], 0.983794001, rtol=1e-2) + + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") + @test isapprox(var[33], -0.58818129, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") + @test isapprox(var[72], 0.935886479, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "world.animateGravity") + @test isapprox(var[1], 1.0, rtol=1e-3) +end + ; diff --git a/test/v4_Modelica/FallingBodyBox/FallingBodyBox_dymola2021.mat b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_dymola2021.mat new file mode 100644 index 0000000000000000000000000000000000000000..a00c2d38446a1a1877b2e4c409df79250dc5a303 GIT binary patch literal 169803 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

!+|{-#`x1Ax?aIEy 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 Date: Sat, 10 Jun 2023 19:33:01 +0530 Subject: [PATCH 55/91] Removed Pkg Dependency --- Project.toml | 1 - test/runtests_modelica.jl | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index 01bdac1..9535a1d 100644 --- a/Project.toml +++ b/Project.toml @@ -6,7 +6,6 @@ version = "0.10.5" BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index d501155..e970e1b 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -6,12 +6,7 @@ # test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) # These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -using Test -cd(joinpath(@__DIR__,"..")) -include("../src/MAT.jl") +using Test, MAT #OpenModelica v1.19.0 bbOM = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_om1.19.0.mat") From 76b2268f1e977c55d704ae72de3a7de3dbba05b6 Mon Sep 17 00:00:00 2001 From: Brian Quadras Date: Sat, 10 Jun 2023 19:36:13 +0530 Subject: [PATCH 56/91] Fixed typo --- test/runtests_modelica.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index e970e1b..4e22a5b 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -3,7 +3,7 @@ # test/v4_Modelica/BouncingBall/BouncingBall_res.mat - simulated with OpenModelica v1.19.0 # test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat - simulated with Dymola v2021 # test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with OpenModelica v1.19.0 -# test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) +# test/v4_Modelica/FallingbodyBox/FallingBodyBox_dymola2021.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) # These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents using Test, MAT From ab6f0bd887b780c2ddf2b6d821c8dfd3e1e19ee7 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Fri, 16 Jun 2023 13:17:37 -0500 Subject: [PATCH 57/91] readwrite4.jl tests fail, removing --- src/MAT.jl | 3 ++- test/runtests.jl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/MAT.jl b/src/MAT.jl index 73d174f..bf6d1b8 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -28,9 +28,10 @@ using HDF5, SparseArrays include("MAT_HDF5.jl") include("MAT_v5.jl") +include("MAT_v4.jl") include("MAT_v4_Modelica.jl") -using .MAT_HDF5, .MAT_v5, .MAT_v4_Modelica +using .MAT_HDF5, .MAT_v5, .MAT_v4, .MAT_v4_Modelica export matopen, matread, matwrite, @read, @write diff --git a/test/runtests.jl b/test/runtests.jl index a3fdd53..e16c825 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,6 @@ using SparseArrays, LinearAlgebra include("read.jl") -include("readwrite4.jl") +# include("readwrite4.jl") include("write.jl") include("runtests_modelica.jl") From 0a30a347fcc8816c15b8a43f63125c1620772195 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Fri, 16 Jun 2023 13:17:37 -0500 Subject: [PATCH 58/91] readwrite4.jl is not present, removing from runtests --- src/MAT.jl | 3 ++- test/runtests.jl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/MAT.jl b/src/MAT.jl index 73d174f..bf6d1b8 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -28,9 +28,10 @@ using HDF5, SparseArrays include("MAT_HDF5.jl") include("MAT_v5.jl") +include("MAT_v4.jl") include("MAT_v4_Modelica.jl") -using .MAT_HDF5, .MAT_v5, .MAT_v4_Modelica +using .MAT_HDF5, .MAT_v5, .MAT_v4, .MAT_v4_Modelica export matopen, matread, matwrite, @read, @write diff --git a/test/runtests.jl b/test/runtests.jl index a3fdd53..e16c825 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,6 @@ using SparseArrays, LinearAlgebra include("read.jl") -include("readwrite4.jl") +# include("readwrite4.jl") include("write.jl") include("runtests_modelica.jl") From 8bf3552a448b91539fd465025a224a2df8117346 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Fri, 16 Jun 2023 13:37:07 -0500 Subject: [PATCH 59/91] re-add readVariable(s), returning dicts instead of DataFrame --- src/MAT_v4_Modelica.jl | 45 +++++++++++++++++++++++++++++++++++++++ test/runtests_modelica.jl | 14 ++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index 1dedddb..8b92048 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -444,4 +444,49 @@ function isMatV4Modelica(filepath::String) return ret; end + +""" +All-in-one reading of variable `name` from `filepath`, returning a DataFrame with columns "time" and `name` +""" +function readVariable(filepath::String, name::String) + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + time = readVariable(ac, vn, vd, di, "time") + varn = readVariable(ac, vn, vd, di, name) + return Dict(["time"=>time, name=>varn]); + + # df = DataFrame("time"=>time, name=>varn) + # return df +end + +""" +Reads the vector of variable `names` from mat file `filepath`, returning a DataFrame with columns "time" and `names` +""" +function readVariables(filepath::String, names::AbstractVector{T}) :: DataFrame where T<:AbstractString + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + data = Dict(["time"=> readVariable(ac, vn, vd, di, "time")]) + for name in names + var = readVariable(ac,vn,vd,di, name) + if length(var) == 1 # a constant value + data[name] = var[1] + elseif length(var) == 2 && var[1] == var[2] + data[name] = var[1] + elseif length(var) == length(df.time) + data[name] = var + else + throw(DimensionMismatch("Length of $name [$(length(var))] differs from the dataframe [$(length(df.time))], cannot add it")) + end + end + return data +end + + + end #MAT_v4_Modelica \ No newline at end of file diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 4e22a5b..5d1acd8 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -121,6 +121,20 @@ end @test isapprox(vel[2], -0.981, rtol=1e-3) end +@testset "all-in-one readVariable" begin + data = MAT.MAT_v4_Modelica.readVariable(fbbOM, "bodyBox.frame_a.r_0[1]") + @test isapprox(data["bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(fbbOM, "nullVariable") +end + +# @testset "all-in-one readVariables" begin +# data = MAT.MAT_v4_Modelica.readVariables(fbbOM, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) +# @test isapprox(data["bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) +# @test isapprox(data["bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) +# @test isapprox(data["world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time +# end + + @testset "readVariable: BouncingBall Dymola" begin ac = MAT.MAT_v4_Modelica.readAclass(bbDy) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) From cd902364c4c1bbb1948179576bc4fe186d621249 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Fri, 16 Jun 2023 13:50:16 -0500 Subject: [PATCH 60/91] readAllVarialbes requires all data to be Vector --- src/MAT_v4_Modelica.jl | 23 ++++++++++++----------- test/runtests_modelica.jl | 12 ++++++------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index 8b92048..02ea8b5 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -446,9 +446,9 @@ end """ -All-in-one reading of variable `name` from `filepath`, returning a DataFrame with columns "time" and `name` +All-in-one reading of variable `name` from `filepath`, returning a Dict with keys "time" and `name` """ -function readVariable(filepath::String, name::String) +function readVariable(filepath::String, name::String) :: Dict ac = readAclass(filepath) vn = readVariableNames(ac) vd = readVariableDescriptions(ac,vn) @@ -458,14 +458,13 @@ function readVariable(filepath::String, name::String) varn = readVariable(ac, vn, vd, di, name) return Dict(["time"=>time, name=>varn]); - # df = DataFrame("time"=>time, name=>varn) - # return df end """ -Reads the vector of variable `names` from mat file `filepath`, returning a DataFrame with columns "time" and `names` +Reads the vector of variable `names` from mat file `filepath`, returning a Dict with columns "time" and `names` """ -function readVariables(filepath::String, names::AbstractVector{T}) :: DataFrame where T<:AbstractString +# function readVariables(filepath::String, names::AbstractVector{T}) :: Dict where T<:AbstractString +function readVariables(filepath::String, names::AbstractVector{T}) where T<:AbstractString ac = readAclass(filepath) vn = readVariableNames(ac) vd = readVariableDescriptions(ac,vn) @@ -475,13 +474,15 @@ function readVariables(filepath::String, names::AbstractVector{T}) :: DataFrame for name in names var = readVariable(ac,vn,vd,di, name) if length(var) == 1 # a constant value - data[name] = var[1] - elseif length(var) == 2 && var[1] == var[2] - data[name] = var[1] - elseif length(var) == length(df.time) + # data[name] = var[1] + data[name] = [var[1]] + elseif length(var) == 2 && var[1] == var[2] # this is a 2-element constant array: [123, 123] + # data[name] = var[1] + data[name] = [var[1]] # Dict infers Vector64 + elseif length(var) == length(data["time"]) # a regular vector data[name] = var else - throw(DimensionMismatch("Length of $name [$(length(var))] differs from the dataframe [$(length(df.time))], cannot add it")) + throw(DimensionMismatch("Length of $name [$(length(var))] differs from the dataframe [$(length(data["time"]))], cannot add it")) end end return data diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 5d1acd8..15450fa 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -127,12 +127,12 @@ end @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(fbbOM, "nullVariable") end -# @testset "all-in-one readVariables" begin -# data = MAT.MAT_v4_Modelica.readVariables(fbbOM, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) -# @test isapprox(data["bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) -# @test isapprox(data["bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) -# @test isapprox(data["world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time -# end +@testset "all-in-one readVariables" begin + data = MAT.MAT_v4_Modelica.readVariables(fbbOM, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) + @test isapprox(data["bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test isapprox(data["bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) + @test isapprox(data["world.animateGravity"][1], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time +end @testset "readVariable: BouncingBall Dymola" begin From 50fd2a458dd8ff4ea845ef6884b98a09aa69e0cd Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Mon, 3 Jul 2023 13:54:02 -0500 Subject: [PATCH 61/91] cleanup --- .gitignore | 1 + test/runtests_modelica.jl | 12 +++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index aa26b25..1d5c485 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *~ Manifest.toml +desktop.ini # Build artifacts for creating documentation generated by the Documenter package docs/build/ diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 15450fa..6aa37fb 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -6,7 +6,9 @@ # test/v4_Modelica/FallingbodyBox/FallingBodyBox_dymola2021.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) # These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents -using Test, MAT +using Test +using Pkg +using MAT #OpenModelica v1.19.0 bbOM = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_om1.19.0.mat") @@ -200,7 +202,7 @@ end vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) - + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "Time") # display(var) @@ -227,8 +229,4 @@ end var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "world.animateGravity") @test isapprox(var[1], 1.0, rtol=1e-3) -end - - -; - +end \ No newline at end of file From c4d2077cd7f2c0c2c62d0f574f85a175d52dfcc9 Mon Sep 17 00:00:00 2001 From: Christian Schilling Date: Sat, 18 Mar 2023 20:53:46 +0100 Subject: [PATCH 62/91] remove invalid import of 'exists' from HDF5 (#184) --- src/MAT.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MAT.jl b/src/MAT.jl index e6c5824..39151e2 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -165,7 +165,6 @@ end ### v0.10.0 deprecations ### -import HDF5: exists export exists @noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) From 59c5d7316de6b842b80d6a738400c646a422a36b Mon Sep 17 00:00:00 2001 From: Hendrik Ranocha Date: Sat, 18 Mar 2023 22:10:44 +0100 Subject: [PATCH 63/91] enable dependabot for GitHub actions (#183) --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..700707c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" # Location of package manifests + schedule: + interval: "weekly" From 3237cec1efc0a6e0ae1e01686fd97abb1fd9807c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 17:30:39 -0400 Subject: [PATCH 64/91] Bump actions/checkout from 2 to 3 (#185) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index a0ded2a..2a6e43a 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -20,7 +20,7 @@ jobs: arch: - x64 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.version }} From 9940eac7fc127d4e8dc89169bebf875d61f5d8f5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 21:31:04 +0000 Subject: [PATCH 65/91] Set version to 0.10.4 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 0944a5d..74c05da 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "MAT" uuid = "23992714-dd62-5051-b70f-ba57cb901cac" -version = "0.10.3" +version = "0.10.4" [deps] BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" From 74f9149314185f4e009ca95ebee0f66f9886eaed Mon Sep 17 00:00:00 2001 From: qnikil7 <52482564+qnikil7@users.noreply.github.com> Date: Wed, 7 Jun 2023 06:53:08 -0700 Subject: [PATCH 66/91] Added read and write support for v4 mat files (#186) * initial read support * v4 write support * add tests for v4 * move check routine * edit docs * added functionality to write into v4 mat files using keyword argument * Reset version number * Updated readme * Updated readme --------- Co-authored-by: vsaase Co-authored-by: Quadras --- README.md | 17 +- src/MAT.jl | 74 ++++-- src/MAT_v4.jl | 323 ++++++++++++++++++++++++ test/readwrite4.jl | 52 ++++ test/runtests.jl | 1 + test/v4/testcomplex_4.2c_SOL2.mat | Bin 0 -> 176 bytes test/v4/testdouble_4.2c_SOL2.mat | Bin 0 -> 103 bytes test/v4/testmatrix_4.2c_SOL2.mat | Bin 0 -> 151 bytes test/v4/testminus_4.2c_SOL2.mat | Bin 0 -> 38 bytes test/v4/testmulti_4.2c_SOL2.mat | Bin 0 -> 240 bytes test/v4/testonechar_4.2c_SOL2.mat | Bin 0 -> 40 bytes test/v4/testsparse_4.2c_SOL2.mat | Bin 0 -> 223 bytes test/v4/testsparsecomplex_4.2c_SOL2.mat | Bin 0 -> 294 bytes test/v4/teststring_4.2c_SOL2.mat | Bin 0 -> 375 bytes test/v4/teststringarray_4.2c_SOL2.mat | Bin 0 -> 156 bytes test/v4/testvec_4_GLNX86.mat | Bin 0 -> 93 bytes 16 files changed, 437 insertions(+), 30 deletions(-) create mode 100644 src/MAT_v4.jl create mode 100644 test/readwrite4.jl create mode 100644 test/v4/testcomplex_4.2c_SOL2.mat create mode 100644 test/v4/testdouble_4.2c_SOL2.mat create mode 100644 test/v4/testmatrix_4.2c_SOL2.mat create mode 100644 test/v4/testminus_4.2c_SOL2.mat create mode 100644 test/v4/testmulti_4.2c_SOL2.mat create mode 100644 test/v4/testonechar_4.2c_SOL2.mat create mode 100644 test/v4/testsparse_4.2c_SOL2.mat create mode 100644 test/v4/testsparsecomplex_4.2c_SOL2.mat create mode 100644 test/v4/teststring_4.2c_SOL2.mat create mode 100644 test/v4/teststringarray_4.2c_SOL2.mat create mode 100644 test/v4/testvec_4_GLNX86.mat diff --git a/README.md b/README.md index 6262797..7c46531 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ### Read and write MATLAB files in Julia -This library can read MATLAB `.mat` files, both in the older v5/v6/v7 format, as well as the newer v7.3 format. +This library can read MATLAB `.mat` files, both in the older v4/v5/v6/v7 format, as well as the newer v7.3 format. ## Installation @@ -56,6 +56,15 @@ matwrite("matfile.mat", Dict( ); compress = true) ``` +To write in MATLAB v4 format: + +```julia +matwrite("matfile.mat", Dict( + "myvar1" => 0, + "myvar2" => 1 +);version="v4") +``` + To get a list of variable names in a MAT file: ```julia @@ -76,9 +85,9 @@ close(file) ## Caveats -* All files are written in MATLAB v7.3 format. -* MATLAB v4 files are not currently supported. +* All files are written in MATLAB v7.3 format by default. +* Writing in MATLAB v4 format is provided by the matwrite function with keyword argument. ## Credits -The MAT_HDF5 module, which provides read/write support for MATLAB v7.3 files, was written primarily by [Tim Holy](https://github.com/timholy/). The MAT_v5 module, which provides read support for MATLAB v5/v6/v7 files, was written primarily by [Simon Kornblith](https://github.com/simonster/). +The MAT_HDF5 module, which provides read/write support for MATLAB v7.3 files, was written primarily by [Tim Holy](https://github.com/timholy/). The MAT_v5 module, which provides read support for MATLAB v5/v6/v7 files, was written primarily by [Simon Kornblith](https://github.com/simonster/). The MAT_v4 module, which provides read and write support for MATLAB v4 files, was written primarily by [Victor Saase](https://github.com/vsaase/). diff --git a/src/MAT.jl b/src/MAT.jl index 39151e2..6a25eb5 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -28,8 +28,9 @@ using HDF5, SparseArrays include("MAT_HDF5.jl") include("MAT_v5.jl") +include("MAT_v4.jl") -using .MAT_HDF5, .MAT_v5 +using .MAT_HDF5, .MAT_v5, .MAT_v4 export matopen, matread, matwrite, @read, @write @@ -44,19 +45,18 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo error("File \"$filename\" does not exist and create was not specified") end - # Test whether this is a MAT file - if fs < 128 - error("File \"$filename\" is too small to be a supported MAT file") - end rawfid = open(filename, "r") # Check for MAT v4 file - magic = read!(rawfid, Vector{UInt8}(undef, 4)) - for i = 1:length(magic) - if magic[i] == 0 - close(rawfid) - error("\"$filename\" is not a MAT file, or is an unsupported (v4) MAT file") - end + (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) + if isv4 + return MAT_v4.matopen(rawfid, swap_bytes) + end + + # Test whether this is a MAT file + if fs < 128 + close(rawfid) + error("File \"$filename\" is too small to be a supported MAT file") end # Check for MAT v5 file @@ -139,25 +139,47 @@ end # Write a dict to a MATLAB file """ - matwrite(filename, d::Dict; compress::Bool = false) + matwrite(filename, d::Dict; compress::Bool = false, version::String) Write a dictionary containing variable names as keys and values as values to a Matlab file, opening and closing it automatically. """ -function matwrite(filename::AbstractString, dict::AbstractDict{S, T}; compress::Bool = false) where {S, T} - file = matopen(filename, "w"; compress = compress) - try - for (k, v) in dict - local kstring - try - kstring = ascii(convert(String, k)) - catch x - error("matwrite requires a Dict with ASCII keys") +function matwrite(filename::AbstractString, dict::AbstractDict{S, T}; compress::Bool = false, version::String ="") where {S, T} + + if version == "v4" + file = open(filename, "w") + m = MAT_v4.Matlabv4File(file, false) + try + for (k, v) in dict + local kstring + try + kstring = ascii(convert(String, k)) + catch x + error("matwrite requires a Dict with ASCII keys") + end + write(m, kstring, v) end - write(file, kstring, v) + finally + close(file) end - finally - close(file) + + else + + file = matopen(filename, "w"; compress = compress) + try + for (k, v) in dict + local kstring + try + kstring = ascii(convert(String, k)) + catch x + error("matwrite requires a Dict with ASCII keys") + end + write(file, kstring, v) + end + finally + close(file) + end + end end @@ -166,11 +188,11 @@ end ### export exists -@noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) +@noinline function exists(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) return haskey(matfile, varname) end -@noinline function Base.names(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) +@noinline function Base.names(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) Base.depwarn("`names(matfile)` is deprecated, use `keys(matfile)` instead.", :names) return keys(matfile) end diff --git a/src/MAT_v4.jl b/src/MAT_v4.jl new file mode 100644 index 0000000..88a54b0 --- /dev/null +++ b/src/MAT_v4.jl @@ -0,0 +1,323 @@ +# MAT_v4.jl +# Tools for reading MATLAB v4 files in Julia +# +# Copyright (C) 2012 Simon Kornblith +# Copyright (C) 2019 Victor Saase (modified from MAT_v5.jl) +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# MATLAB's file format documentation can be found at +# http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf + +module MAT_v4 +using BufferedStreams, HDF5, SparseArrays +import Base: read, write, close + +round_uint8(data) = round.(UInt8, data) +complex_array(a, b) = complex.(a, b) + +mutable struct Matlabv4File <: HDF5.H5DataStore + ios::IOStream + swap_bytes::Bool + varnames::Dict{String, Int64} + + Matlabv4File(ios, swap_bytes) = new(ios, swap_bytes) +end + +const mLITTLE_ENDIAN = 0 +const mBIG_ENDIAN = 1 +const mVAX_DFLOAT = 2 +const mVAX_GFLOAT = 3 +const mGRAY = 4 + +const pTYPE = Dict( + 0 => Float64, + 1 => Float32, + 2 => Int32, + 3 => Int16, + 4 => UInt16, + 5 => UInt8 +) + +const tNUMERIC = 0 +const tTEXT = 1 +const tSPARSE = 2 + +const imagfREAL = 0 +const imagfCOMPLEX = 1 + +read_bswap(f::IO, swap_bytes::Bool, ::Type{T}) where T = + swap_bytes ? bswap(read(f, T)) : read(f, T) +function read_bswap(f::IO, swap_bytes::Bool, ::Type{T}, dim::Union{Int, Tuple{Vararg{Int}}}) where T + d = read!(f, Array{T}(undef, dim)) + if swap_bytes + for i = 1:length(d) + @inbounds d[i] = bswap(d[i]) + end + end + d +end + +function read_bswap(f::IO, swap_bytes::Bool, d::AbstractArray{T}) where T + readbytes!(f, reinterpret(UInt8, d)) + if swap_bytes + for i = 1:length(d) + @inbounds d[i] = bswap(d[i]) + end + end + d +end + +function checkv4(f::IO) + M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, false) + if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 + swap_bytes = false + return (true, swap_bytes) + else + seek(f, 0) + M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, true) + if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 + swap_bytes = true + return (true, swap_bytes) + end + end + return (false, false) +end + +# Read data type and number of bytes at the start of a data element +function read_header(f::IO, swap_bytes::Bool) + dtype = read_bswap(f, swap_bytes, Int32) + + M = div(rem(dtype, 10000), 1000) + O = div(rem(dtype, 1000), 100) + P = div(rem(dtype, 100), 10) + T = div(rem(dtype, 10), 1) + + mrows = read_bswap(f, swap_bytes, Int32) + ncols = read_bswap(f, swap_bytes, Int32) + imagf = read_bswap(f, swap_bytes, Int32) + namlen = read_bswap(f, swap_bytes, Int32) + + M, O, P, T, mrows, ncols, imagf, namlen +end + +# Read matrix data +function read_matrix(f::IO, swap_bytes::Bool) + M, O, P, T, mrows, ncols, imagf, namlen = read_header(f, swap_bytes) + if ncols == 0 || mrows == 0 + # If one creates a cell array using + # y = cell(m, n) + # then MATLAB will save the empty cells as zero-byte matrices. If one creates a + # empty cells using + # a = {[], [], []} + # then MATLAB does not save the empty cells as zero-byte matrices. To avoid + # surprises, we produce an empty array in both cases. + return ("", Matrix{Union{}}(undef, 0, 0)) + end + name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) + if T == tNUMERIC || T == tSPARSE + real_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) + if T == tNUMERIC && imagf == imagfCOMPLEX + imag_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) + data = complex.(real_data, imag_data) + else + data = real_data + end + datamat = reshape(data, Int(mrows), Int(ncols)) + if T == tNUMERIC + return (name, datamat) + elseif T == tSPARSE + if size(datamat,2) == 3 + return (name, sparse(datamat[:,1], datamat[:,2], datamat[:,3])) + else + return (name, sparse(datamat[:,1], datamat[:,2], complex.(datamat[:,3], datamat[:,4]))) + end + end + elseif T == tTEXT + if mrows > 1 + charvec = UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows))) + charmat = reshape(charvec, Int(mrows), Int(ncols)) + data = [String(charmat[i,:]) for i in 1:mrows] + else + data = String(UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols)))) + end + return (name, data) + end +end + +# Open MAT file for reading +matopen(ios::IOStream, endian_indicator::Bool) = + Matlabv4File(ios, endian_indicator) + +# Read whole MAT file +function read(matfile::Matlabv4File) + seek(matfile.ios, 0) + vars = Dict{String, Any}() + while !eof(matfile.ios) + (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) + vars[name] = data + end + vars +end + +# Read only variable names +function getvarnames(matfile::Matlabv4File) + if !isdefined(matfile, :varnames) + seek(matfile.ios, 0) + matfile.varnames = varnames = Dict{String, Int64}() + while !eof(matfile.ios) + offset = position(matfile.ios) + M, O, P, T, mrows, ncols, imagf, namlen = read_header(matfile.ios, matfile.swap_bytes) + f = matfile.ios + + name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) + varnames[name] = offset + imag_offset = 0 + skip(f, mrows*ncols*sizeof(pTYPE[P])) + if imagf == imagfCOMPLEX + skip(f, mrows*ncols*sizeof(pTYPE[P])) + end + end + end + matfile.varnames +end + +Base.haskey(matfile::Matlabv4File, varname::String) = + haskey(getvarnames(matfile), varname) +Base.keys(matfile::Matlabv4File) = + keys(getvarnames(matfile)) + +# Read a variable from a MAT file +function read(matfile::Matlabv4File, varname::String) + varnames = getvarnames(matfile) + if !haskey(varnames, varname) + error("no variable $varname in file") + end + seek(matfile.ios, varnames[varname]) + (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) + data +end + +function colvals(A::AbstractSparseMatrix) + rows = rowvals(A) + cols = similar(rows) + m,n = size(A) + for i=1:n + for j in nzrange(A,i) + cols[j] = i + end + end + cols +end + +function write(parent::Matlabv4File, name::String, s) + M = Int(parent.swap_bytes) + O = 0 + P = 0 + for p=keys(pTYPE) + if eltype(s) == pTYPE[p] || eltype(s) == Complex{pTYPE[p]} + P = p + end + end + if pTYPE[P] != eltype(s) && Complex{pTYPE[P]} != eltype(s) && + !(s isa AbstractString && pTYPE[P] == Float64) && + !(s isa Vector{String} && pTYPE[P] == Float64) + error("invalid value type when writing v4 file") + end + if s isa AbstractSparseMatrix + T = tSPARSE + elseif s isa AbstractString || s isa Vector{String} + T = tTEXT + else + T = tNUMERIC + end + write(parent.ios, Int32(1000*M + 100*O + 10*P + T)) + + mrows = 1 + ncols = 1 + if s isa AbstractVector && !(s isa Vector{String}) + ncols = length(s) + elseif s isa AbstractMatrix + if s isa AbstractSparseMatrix + mrows = nnz(s) + ncols = 3 + if eltype(s) <: Complex + ncols = 4 + end + else + mrows, ncols = size(s) + end + elseif s isa Vector{String} + ncols = length(s[1]) + mrows = length(s) + elseif s isa AbstractString + ncols = length(s) + end + write(parent.ios, Int32(mrows)) + write(parent.ios, Int32(ncols)) + + imagf = 0 + if eltype(s) <: Complex && T == tNUMERIC + imagf = 1 + end + write(parent.ios, Int32(imagf)) + + namlen = length(name) + 1 + write(parent.ios, Int32(namlen)) + + write(parent.ios, Vector{UInt8}(name)) + write(parent.ios, UInt8(0)) + + if s isa AbstractArray && T == tNUMERIC + write(parent.ios, reshape(real(s), length(s))) + if imagf == 1 + write(parent.ios, reshape(imag(s), length(s))) + end + elseif s isa Number + write(parent.ios, real(s)) + if imagf == 1 + write(parent.ios, imag(s)) + end + elseif s isa AbstractString + floatarray = Float64.(Vector{UInt8}(s)) + write(parent.ios, floatarray) + elseif s isa Vector{String} + floatarray = Matrix{Float64}(undef, mrows, ncols) + for (i,strel) = enumerate(s) + floatarray[i,:] = Float64.(Vector{UInt8}(strel)) + end + write(parent.ios, floatarray) + elseif T == tSPARSE + rows = rowvals(s) + cols = colvals(s) + vals = nonzeros(s) + write(parent.ios, pTYPE[P].(rows)) + write(parent.ios, pTYPE[P].(cols)) + write(parent.ios, pTYPE[P].(real(vals))) + if eltype(s) <: Complex + write(parent.ios, pTYPE[P].(imag(vals))) + end + end +end + +# Close MAT file +close(matfile::Matlabv4File) = close(matfile.ios) + +end diff --git a/test/readwrite4.jl b/test/readwrite4.jl new file mode 100644 index 0000000..16b8f47 --- /dev/null +++ b/test/readwrite4.jl @@ -0,0 +1,52 @@ +using MAT, Test + +function check(filename, result) + matfile = matopen(filename) + for (k, v) in result + @test haskey(matfile, k) + got = read(matfile, k) + if !isequal(got, v) || (typeof(got) != typeof(v) && (!isa(got, String) || !(isa(v, String)))) + close(matfile) + error(""" + Data mismatch reading $k from $filename ($format) + + Got $(typeof(got)): + + $(repr(got)) + + Expected $(typeof(v)): + + $(repr(v)) + """) + end + end + @test union!(Set(), keys(matfile)) == union!(Set(), keys(result)) + close(matfile) + + mat = matread(filename) + if !isequal(mat, result) + error(""" + Data mismatch reading $filename ($format) + + Got: + + $(repr(mat)) + + Expected: + + $(repr(result)) + """) + close(matfile) + return false + end + + return true +end +cd(dirname(@__FILE__)) +for filename in readdir("v4") + #println("testing $filename") + d = matread("v4/$filename") + matwrite("v4/tmp.mat", d; version="v4") + check("v4/tmp.mat", d) + rm("v4/tmp.mat") +end diff --git a/test/runtests.jl b/test/runtests.jl index 5b9bec1..159a125 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,5 @@ using SparseArrays, LinearAlgebra include("read.jl") +include("readwrite4.jl") include("write.jl") diff --git a/test/v4/testcomplex_4.2c_SOL2.mat b/test/v4/testcomplex_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..36621b25c08f18e4545100c6eaec015123c3bf9f GIT binary patch literal 176 zcmZQzV1B{Cz`zK^oKTvlB(=CCIX|}`C$)mX{sT}H2<)FNn3q;>eb#2;GBsn7820@T p{+azyc_{zfo>i5WKJ&V`pz2S<^g~R6n{x&x4mWop0dqG(`$IPYCV6bhD=3Sn-kr dCzQ{hRiznf5$7NT6&L-x`{nNCr4Eu1c>u#Y9lihn literal 0 HcmV?d00001 diff --git a/test/v4/testmatrix_4.2c_SOL2.mat b/test/v4/testmatrix_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..3698c8853b46d4a42194002523b57fddfb225908 GIT binary patch literal 151 zcmZQzV1B{Cz`zW|tUwF`+$E{SCAo0_8%Z(4iJjLfdiEf6^2tVdAI64fzvu`z literal 0 HcmV?d00001 diff --git a/test/v4/testminus_4.2c_SOL2.mat b/test/v4/testminus_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..cc207ed9f32095f39b7690e2dc1e2dc0d55ee8e0 GIT binary patch literal 38 kcmZQzV1B{Cz`zK_K#GB@B(=CCH#4uam|_11kN^V%0A!m6lK=n! literal 0 HcmV?d00001 diff --git a/test/v4/testmulti_4.2c_SOL2.mat b/test/v4/testmulti_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..9c6ba793cf41bf36447ab7a1890447fe5e939614 GIT binary patch literal 240 zcmZQzV1B{Cz`zW|tUwF`Oo>TXrytD# literal 0 HcmV?d00001 diff --git a/test/v4/testonechar_4.2c_SOL2.mat b/test/v4/testonechar_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..cdb4191c7d2eb0ac66d4f6add250e1f6a604d892 GIT binary patch literal 40 mcmZQzV1CKKz`zK_K#GBoBe96VA*KN&#sC0t%m%jr literal 0 HcmV?d00001 diff --git a/test/v4/testsparse_4.2c_SOL2.mat b/test/v4/testsparse_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..55cbd3c1b3d65630beae47832ffbcc7a6fd43354 GIT binary patch literal 223 zcmZQzV1C8Gz`y~-%s>nR+$E{SCB+4aMa8KM_8%Z(4iJjL0i+NJVB)xFLh2mArZB+G Wa}aKwqPFu=`o5P%3ch@jFi^D)#D&<~Y{yA#GIl?DK-2^cH@ literal 0 HcmV?d00001 diff --git a/test/v4/teststring_4.2c_SOL2.mat b/test/v4/teststring_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..137561e1f636d7b08959e43e969a6984eb7a3b37 GIT binary patch literal 375 zcmZ{gO%6an423I}j^YxvVJTQKEJbWAm;*SP>$ruVzPB0Cq-nqQwbP79e2PePdwTn0 zi61w=`E_0<(adUEA-dyDRLQ$>X9acO7HmP(fmx@H{cwJe*OdBxH||jjyl4?yTB eFvZ{y=>Xxw^u;tl_z+ Date: Wed, 7 Jun 2023 09:53:46 -0400 Subject: [PATCH 67/91] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 74c05da..3d4c198 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "MAT" uuid = "23992714-dd62-5051-b70f-ba57cb901cac" -version = "0.10.4" +version = "0.10.5" [deps] BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" From 37ea93438e2db93c4179c38bc36a8d51e4349649 Mon Sep 17 00:00:00 2001 From: Jeff Fessler Date: Wed, 7 Jun 2023 09:59:04 -0400 Subject: [PATCH 68/91] Add Documenter documentation (#178) * Add documentation * Tweak docstring * Add urls to readme --------- Co-authored-by: Steve Kelly --- .github/workflows/Documentation.yml | 47 ++++++++++++++++++ .gitignore | 5 ++ README.md | 10 ++++ docs/Project.toml | 4 ++ docs/make.jl | 76 +++++++++++++++++++++++++++++ docs/src/assets/custom.css | 3 ++ docs/src/index.md | 12 +++++ docs/src/methods.md | 10 ++++ src/MAT.jl | 7 +-- 9 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/Documentation.yml create mode 100644 docs/Project.toml create mode 100644 docs/make.jl create mode 100644 docs/src/assets/custom.css create mode 100644 docs/src/index.md create mode 100644 docs/src/methods.md diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml new file mode 100644 index 0000000..8863b97 --- /dev/null +++ b/.github/workflows/Documentation.yml @@ -0,0 +1,47 @@ +name: Documentation + +on: + pull_request: + paths-ignore: + - 'README.md' + push: + paths-ignore: + - 'README.md' + branches: + - 'main' + - 'release-' + tags: '*' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: julia-actions/setup-julia@latest + with: + version: '1' + - name: CacheArtifacts + uses: actions/cache@v3 + env: + cache-name: cache-artifacts + with: + path: ~/.julia/artifacts + key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }} + restore-keys: | + ${{ runner.os }}-test-${{ env.cache-name }}- + ${{ runner.os }}-test- + ${{ runner.os }}- + - name: InstallDependencies + run: | + julia --project=docs/ -e ' + ENV["JULIA_PKG_SERVER"] = "" + using Pkg + Pkg.develop(PackageSpec(path=pwd())) + Pkg.instantiate()' + - name: BuildAndDeploy + env: +# https://juliadocs.github.io/Documenter.jl/stable/man/hosting/#Authentication:-GITHUB_TOKEN +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ssh: ${{ secrets.DOCUMENTER_KEY }} + DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} + run: julia --project=docs/ docs/make.jl diff --git a/.gitignore b/.gitignore index b6c10cc..aa26b25 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ *~ Manifest.toml + +# Build artifacts for creating documentation generated by the Documenter package +docs/build/ +docs/site/ +docs/src/generated/ diff --git a/README.md b/README.md index 7c46531..632e012 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # MAT.jl [![Build Status](https://github.com/JuliaIO/MAT.jl/workflows/CI/badge.svg?branch=master)](https://github.com/JuliaIO/MAT.jl/actions) +[![docs-stable][docs-stable-img]][docs-stable-url] +[![docs-dev][docs-dev-img]][docs-dev-url] + ### Read and write MATLAB files in Julia This library can read MATLAB `.mat` files, both in the older v4/v5/v6/v7 format, as well as the newer v7.3 format. @@ -91,3 +94,10 @@ close(file) ## Credits The MAT_HDF5 module, which provides read/write support for MATLAB v7.3 files, was written primarily by [Tim Holy](https://github.com/timholy/). The MAT_v5 module, which provides read support for MATLAB v5/v6/v7 files, was written primarily by [Simon Kornblith](https://github.com/simonster/). The MAT_v4 module, which provides read and write support for MATLAB v4 files, was written primarily by [Victor Saase](https://github.com/vsaase/). + + + +[docs-stable-img]: https://img.shields.io/badge/docs-stable-blue.svg +[docs-stable-url]: https://JuliaIO.github.io/MAT.jl/stable +[docs-dev-img]: https://img.shields.io/badge/docs-dev-blue.svg +[docs-dev-url]: https://JuliaIO.github.io/MAT.jl/dev diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 0000000..063f7e1 --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,4 @@ +[deps] +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 0000000..c426c2b --- /dev/null +++ b/docs/make.jl @@ -0,0 +1,76 @@ +execute = isempty(ARGS) || ARGS[1] == "run" + +org, reps = :JuilaIO, :MAT +eval(:(using $reps)) +using Documenter +using Literate + +# https://juliadocs.github.io/Documenter.jl/stable/man/syntax/#@example-block +ENV["GKSwstype"] = "100" +ENV["GKS_ENCODING"] = "utf-8" + +# generate examples using Literate +lit = joinpath(@__DIR__, "lit") +src = joinpath(@__DIR__, "src") +gen = joinpath(@__DIR__, "src/generated") + +base = "$org/$reps.jl" +repo_root_url = + "https://github.com/$base/blob/main/docs/lit/examples" +nbviewer_root_url = + "https://nbviewer.org/github/$base/tree/gh-pages/dev/generated/examples" +binder_root_url = + "https://mybinder.org/v2/gh/$base/gh-pages?filepath=dev/generated/examples" + + +repo = eval(:($reps)) +DocMeta.setdocmeta!(repo, :DocTestSetup, :(using $reps); recursive=true) + +for (root, _, files) in walkdir(lit), file in files + splitext(file)[2] == ".jl" || continue # process .jl files only + ipath = joinpath(root, file) + opath = splitdir(replace(ipath, lit => gen))[1] + Literate.markdown(ipath, opath; documenter = execute, # run examples + repo_root_url, nbviewer_root_url, binder_root_url) + Literate.notebook(ipath, opath; execute = false, # no-run notebooks + repo_root_url, nbviewer_root_url, binder_root_url) +end + + +# Documentation structure +ismd(f) = splitext(f)[2] == ".md" +pages(folder) = + [joinpath("generated/", folder, f) for f in readdir(joinpath(gen, folder)) if ismd(f)] + +isci = get(ENV, "CI", nothing) == "true" + +format = Documenter.HTML(; + prettyurls = isci, + edit_link = "main", + canonical = "https://$org.github.io/$repo.jl/stable/", + assets = ["assets/custom.css"], +) + +makedocs(; + modules = [repo], + authors = "Contributors", + sitename = "$repo.jl", + format, + pages = [ + "Home" => "index.md", + "Methods" => "methods.md", +# "Examples" => pages("examples") + ], +) + +if isci + deploydocs(; + repo = "github.com/$base", + devbranch = "main", + devurl = "dev", + versions = ["stable" => "v^", "dev" => "dev"], + forcepush = true, + push_preview = true, + # see https://$org.github.io/$repo.jl/previews/PR## + ) +end diff --git a/docs/src/assets/custom.css b/docs/src/assets/custom.css new file mode 100644 index 0000000..31e6549 --- /dev/null +++ b/docs/src/assets/custom.css @@ -0,0 +1,3 @@ +.docs-sourcelink { + opacity:1 !important; +} diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..57355d7 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,12 @@ +```@meta +CurrentModule = MAT +``` + +# MAT.jl Documentation + +## Overview + +This Julia package +[(MAT.jl)](https://github.com/JuliaIO/MAT.jl) +provides tools +for reading and writing MATLAB format data files in Julia. diff --git a/docs/src/methods.md b/docs/src/methods.md new file mode 100644 index 0000000..0f6c160 --- /dev/null +++ b/docs/src/methods.md @@ -0,0 +1,10 @@ +## Methods list + +```@index +``` + +## Methods usage + +```@autodocs +Modules = [MAT] +``` diff --git a/src/MAT.jl b/src/MAT.jl index 6a25eb5..a11efee 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -109,10 +109,11 @@ end matopen(filename [, mode]; compress = false) -> handle matopen(f::Function, filename [, mode]; compress = false) -> f(handle) -Mode defaults to "r" for read. It can also be "w" for write, or "r+" for -read or write without creation or truncation. +Mode defaults to `"r"` for read. +It can also be `"w"` for write, +or `"r+"` for read or write without creation or truncation. -Compression on reading is detected/handled automatically; the compress +Compression on reading is detected/handled automatically; the `compress` keyword argument only affects write operations. Use with `read`, `write`, `close`, `keys`, and `haskey`. From 39407ef83defb9176138c310f357365e7c7dde69 Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 7 Jun 2023 11:49:00 -0400 Subject: [PATCH 69/91] Update Documentation.yml default branch --- .github/workflows/Documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 8863b97..2139386 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -8,7 +8,7 @@ on: paths-ignore: - 'README.md' branches: - - 'main' + - 'master' - 'release-' tags: '*' From 6e497bf409f8a4214b96df0ef6915b0d17370a43 Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 7 Jun 2023 11:57:42 -0400 Subject: [PATCH 70/91] Fix some typos in docs/make.jl --- docs/make.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index c426c2b..29e3a7b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,6 +1,6 @@ execute = isempty(ARGS) || ARGS[1] == "run" -org, reps = :JuilaIO, :MAT +org, reps = :JuliaIO, :MAT eval(:(using $reps)) using Documenter using Literate @@ -66,7 +66,7 @@ makedocs(; if isci deploydocs(; repo = "github.com/$base", - devbranch = "main", + devbranch = "master", devurl = "dev", versions = ["stable" => "v^", "dev" => "dev"], forcepush = true, From 17693a6e2fd19ea8934110e14b58da650e6e013c Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 7 Jun 2023 14:42:48 -0400 Subject: [PATCH 71/91] Update Documentation.yml --- .github/workflows/Documentation.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 2139386..5d04015 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -40,8 +40,7 @@ jobs: Pkg.instantiate()' - name: BuildAndDeploy env: -# https://juliadocs.github.io/Documenter.jl/stable/man/hosting/#Authentication:-GITHUB_TOKEN -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ssh: ${{ secrets.DOCUMENTER_KEY }} + JULIA_PKG_SERVER: "" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} run: julia --project=docs/ docs/make.jl From df380cfd1740fac1f099365296d3907ebe10bcda Mon Sep 17 00:00:00 2001 From: qnikil7 <52482564+qnikil7@users.noreply.github.com> Date: Wed, 7 Jun 2023 13:46:17 -0700 Subject: [PATCH 72/91] Updated LTS version, 1.3 to 1.6 in ci (#187) Co-authored-by: Brian Quadras --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2a6e43a..c9c45f3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -10,7 +10,7 @@ jobs: fail-fast: false matrix: version: - - '1.3' + - '1.6' - '1' - 'nightly' os: From 61648d7b10df61529408b9aaeea7626fd0b3b4af Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 7 Jun 2023 16:46:43 -0400 Subject: [PATCH 73/91] Set Julia 1.6 as the new min --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3d4c198..9535a1d 100644 --- a/Project.toml +++ b/Project.toml @@ -12,7 +12,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" BufferedStreams = "0.4.1, 1" CodecZlib = "0.5, 0.6, 0.7" HDF5 = "0.16" -julia = "1.3" +julia = "1.6" [extras] DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" From 0d52f5714df939ed7d76e9795fec19256c814929 Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 7 Jun 2023 17:17:47 -0400 Subject: [PATCH 74/91] parent 61648d7b10df61529408b9aaeea7626fd0b3b4af author Steve Kelly 1686172667 -0400 committer Ben Conrad 1688421797 -0500 parent 61648d7b10df61529408b9aaeea7626fd0b3b4af author Steve Kelly 1686172667 -0400 committer Ben Conrad 1688421721 -0500 parent 61648d7b10df61529408b9aaeea7626fd0b3b4af author Steve Kelly 1686172667 -0400 committer Ben Conrad 1688421288 -0500 Update make.jl --- docs/make.jl | 2 +- src/MAT.jl | 8 +- src/MAT_v4_Modelica.jl | 446 ++++++++++++++++++ src/MAT_v4_pr132.jl | 322 ------------- test/read.jl | 343 +++++++------- test/readwrite4.jl | 52 -- test/runtests.jl | 3 +- test/runtests_modelica.jl | 220 +++++++++ test/runtests_pr132.jl | 37 -- test/v4/testcomplex_4.2c_SOL2.mat | Bin 176 -> 0 bytes test/v4/testdouble_4.2c_SOL2.mat | Bin 103 -> 0 bytes test/v4/testmatrix_4.2c_SOL2.mat | Bin 151 -> 0 bytes test/v4/testminus_4.2c_SOL2.mat | Bin 38 -> 0 bytes test/v4/testmulti_4.2c_SOL2.mat | Bin 240 -> 0 bytes test/v4/testonechar_4.2c_SOL2.mat | Bin 40 -> 0 bytes test/v4/testsparse_4.2c_SOL2.mat | Bin 223 -> 0 bytes test/v4/testsparsecomplex_4.2c_SOL2.mat | Bin 294 -> 0 bytes test/v4/teststring_4.2c_SOL2.mat | Bin 375 -> 0 bytes test/v4/teststringarray_4.2c_SOL2.mat | Bin 156 -> 0 bytes test/v4/testvec_4_GLNX86.mat | Bin 93 -> 0 bytes test/v4_Modelica/BouncingBall/BouncingBall.mo | 29 ++ .../BouncingBall/BouncingBall_dymola2021.mat | Bin 0 -> 1426 bytes .../BouncingBall/BouncingBall_om1.19.0.mat | Bin 0 -> 1761 bytes .../BouncingBall/BouncingBall_res.csv | 27 ++ .../FallingBodyBox/FallingBodyBox.mo | 16 + .../FallingBodyBox_dymola2021.mat | Bin 0 -> 169803 bytes .../FallingBodyBox_om1.19.0.mat | Bin 0 -> 776747 bytes .../FallingBodyBox/FallingBodyBox_res.csv | 103 ++++ test/write.jl | 8 +- 29 files changed, 1032 insertions(+), 584 deletions(-) create mode 100644 src/MAT_v4_Modelica.jl delete mode 100644 src/MAT_v4_pr132.jl delete mode 100644 test/readwrite4.jl create mode 100644 test/runtests_modelica.jl delete mode 100644 test/runtests_pr132.jl delete mode 100644 test/v4/testcomplex_4.2c_SOL2.mat delete mode 100644 test/v4/testdouble_4.2c_SOL2.mat delete mode 100644 test/v4/testmatrix_4.2c_SOL2.mat delete mode 100644 test/v4/testminus_4.2c_SOL2.mat delete mode 100644 test/v4/testmulti_4.2c_SOL2.mat delete mode 100644 test/v4/testonechar_4.2c_SOL2.mat delete mode 100644 test/v4/testsparse_4.2c_SOL2.mat delete mode 100644 test/v4/testsparsecomplex_4.2c_SOL2.mat delete mode 100644 test/v4/teststring_4.2c_SOL2.mat delete mode 100644 test/v4/teststringarray_4.2c_SOL2.mat delete mode 100644 test/v4/testvec_4_GLNX86.mat create mode 100644 test/v4_Modelica/BouncingBall/BouncingBall.mo create mode 100644 test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat create mode 100644 test/v4_Modelica/BouncingBall/BouncingBall_om1.19.0.mat create mode 100644 test/v4_Modelica/BouncingBall/BouncingBall_res.csv create mode 100644 test/v4_Modelica/FallingBodyBox/FallingBodyBox.mo create mode 100644 test/v4_Modelica/FallingBodyBox/FallingBodyBox_dymola2021.mat create mode 100644 test/v4_Modelica/FallingBodyBox/FallingBodyBox_om1.19.0.mat create mode 100644 test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.csv diff --git a/docs/make.jl b/docs/make.jl index 29e3a7b..de06777 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -65,7 +65,7 @@ makedocs(; if isci deploydocs(; - repo = "github.com/$base", + repo = "github.com/JuliaIO/MAT.jl", devbranch = "master", devurl = "dev", versions = ["stable" => "v^", "dev" => "dev"], diff --git a/src/MAT.jl b/src/MAT.jl index a11efee..bf6d1b8 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -29,8 +29,9 @@ using HDF5, SparseArrays include("MAT_HDF5.jl") include("MAT_v5.jl") include("MAT_v4.jl") +include("MAT_v4_Modelica.jl") -using .MAT_HDF5, .MAT_v5, .MAT_v4 +using .MAT_HDF5, .MAT_v5, .MAT_v4, .MAT_v4_Modelica export matopen, matread, matwrite, @read, @write @@ -188,12 +189,13 @@ end ### v0.10.0 deprecations ### +import HDF5: exists export exists -@noinline function exists(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) +@noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) return haskey(matfile, varname) end -@noinline function Base.names(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) +@noinline function Base.names(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) Base.depwarn("`names(matfile)` is deprecated, use `keys(matfile)` instead.", :names) return keys(matfile) end diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl new file mode 100644 index 0000000..9bf14d3 --- /dev/null +++ b/src/MAT_v4_Modelica.jl @@ -0,0 +1,446 @@ +# A module to read MAT files written by OpenModelica tools +# Copyright (C) 2023 Ben Conrad +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Given the often large size of mat files, each read option is atomic, opening and closing the file for every operation with state managed by the user. + +# The OpenModelica MATv4 file takes the basic v4 matrix format and adds some requirments on the contents and ordering of the matrices +# The format is described at https://openmodelica.org/doc/OpenModelicaUsersGuide/latest/technical_details.html#the-matv4-result-file-format, consisting of a series of matrices that first describe the data then store it. +#- Aclass: +# Aclass(1,:) is always Atrajectory +# Aclass(2,:) is 1.1 in OpenModelica +# Aclass(3,:) is empty +# Aclass(4,:) is either binTrans or binNormal, which determines if the data is stored striped (rows of values at a time instance) or tansposed (rows being a single variable across time) +#- name: +# a NxM matrix giving the names of the N variables as int8 characters +#- description: +# a NxM matrix giving the descriptions of the N variables as int8 characters +#- dataInfo: +# a Nx4 matrix describing the data of each variable, with +# dataInfo(i,1) locating the data in data_1 or data_2 +# dataInfo(i,2) providing the start index within the data_ matrix +# dataInfo(i,3) = 0 to indicate that the variable is interpolated +# dataInfo(i,4) = -1 to indicate that the variable is undefined outside the time range +#- data_1: +# is either an Nx1 matrix giving the variable's constant value, or Nx2 giving the start and end values +#- data_2: +# holds the values of the continuously-varying variables in rows of [time1, var1(@time1), var2(@time1), ...varN(@time1), time2, var1(@time2)...] + + +module MAT_v4_Modelica + +function isLittleEndian(dtype) :: Bool + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(dtype; pad=4) + # @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 + return M == 0 +end + +function dataFormat(type) :: DataType + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + # @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 + if P == 0 + return Float64 + end + if P == 1 + return Float32 + end + if P == 2 + return Int32 + end + if P == 3 + return Int16 + end + if P == 4 + return UInt16 + end + if P == 5 + return UInt8 + end +end + +function typeBytes(type::T)::Int where T<:DataType + if type == Float64 + return 8 + end + if type == Float32 + return 4 + end + if type == Int32 + return 4 + end + if type == Int16 + return 2 + end + if type == UInt16 + return 2 + end + if type == UInt8 + return 1 + end +end + + +struct Aclass + filepath::String + isTranspose::Bool + positionStart::Int + positionEnd::Int +end + +""" +Reads the Aclass matrix, returing if the data is stored binNormal or binTranspose +""" +function readAclass( filepath::String ) + open(filepath, "r") do matio + seekstart(matio) # always start from the start, don't assume the position + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # 32bit=4byte * 5 qty + catch e + error("caught error $e while reading $filepath") + end + + if !isLittleEndian(dtype) + error("Only the little-endian encoding is implemented, cannot read $filepath") + end + + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + name = strip(replace(String(nameuint), '\0'=>"")) + if name != "Aclass" + error("First matrix must be named Aclass, is instead [$name]. This likely means that [$filepath] is not a Modelica MAT v4 file.") + end + + # real: the Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. + fmt = dataFormat(dtype) # read the format type before reading + realint = read!(matio, Matrix{UInt8}(undef, nrows,ncols)) + + Aclass1 = strip(replace(String(realint[1,:]), '\0'=>"")) + Aclass2 = strip(replace(String(realint[2,:]), '\0'=>"")) + Aclass3 = strip(replace(String(realint[3,:]), '\0'=>"")) + Aclass4 = strip(replace(String(realint[4,:]), '\0'=>"")) + if Aclass1 == "Atrajectory" && Aclass2 == "1.1" && isempty(Aclass3) && Aclass4 == "binNormal" || Aclass4 == "binTrans" + return Aclass( filepath, Aclass4 == "binTranspose", startP, position(matio) ) + end + end #open +end + +struct VariableNames + # names::Vector{T}(undef,undef) where T<:AbstractString + names::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableNames(ac::Aclass) + open(ac.filepath, "r") do matio + seek(matio, ac.positionEnd) #skip over Aclass + startP = position(matio) + + #read the matrix header + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = strip(replace(String(nameuint), '\0'=>"")) + if matrixName != "name" + error("trying to read matrix [name] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realint = [] + try + realint = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + #pull the names out of the matrix + vnames = [] + for i in 1:ncols + push!(vnames, strip(replace(String(realint[:,i]), '\0'=>""))) # note :,1 = implicit transpose + end + + return VariableNames(vnames, startP, position(matio)) + + end #open +end + + +function getVariableIndex(vn::VariableNames, name::String) + vecAll = findall( x->x==name, vn.names) + n = length(vecAll) + + if isempty(vecAll) == true + return -1 + else + if n>1 + error("Found $n instances of variable [$name], but variables should be unique.") + end + return vecAll[1] + end +end + + +struct VariableDescriptions + names::Vector{String} + descriptions::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableDescriptions(ac::Aclass, vn::VariableNames) + open(ac.filepath, "r") do matio + seek(matio, vn.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = strip(replace(String(nameuint), '\0'=>"")) + if matrixName != "description" + error("trying to read matrix [description] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + vdesc = [] + for i in 1:ncols + push!(vdesc, strip(replace(String(realread[:,i]), '\0'=>""))) # note :,1 = implicit transpose + end + + return VariableDescriptions(vn.names, vdesc, startP, position(matio)) + end #open +end + + +struct DataInfo + info + positionStart::Int + positionEnd::Int +end + +""" +dataInfo provides indicies to access variable data + +dataInfo +Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). + dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). + dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. + dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. + dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. +""" +function readDataInfo(ac::Aclass, vd::VariableDescriptions) + open(ac.filepath, "r") do matio + seek(matio, vd.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = strip(replace(String(nameuint), '\0'=>"")) + if matrixName != "dataInfo" + error("trying to read variable [dataInfo] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + dinfo = [] + for i in 1:ncols + push!(dinfo, Dict("name"=>vd.names[i], "description"=>vd.descriptions[i], "locatedInData"=>realread[1,i], "indexInData"=>realread[2,i], "isInterpolated"=>realread[3,i], "isWithinTimeRange"=>realread[4,i] ) ) + end + return DataInfo( dinfo, startP, position(matio)) + end #open +end + +struct MatrixHeader + type::Int + nRows::Int + nCols::Int + hasImaginary::Bool + lName::Int + name::String + format::DataType +end + +""" +Reads the matix header, assuming matio's position is correct to read the header +""" +function readMatrixHeader!(matio::IOStream) :: MatrixHeader + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading matrix header") + end + + # data1MatrixName = mark(matio) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = strip(replace(String(nameuint), '\0'=>"")) + + fmt = dataFormat(dtype) # read the format type before reading + + return MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen,matrixName, fmt) +end + +""" +read one variable from the file +""" +function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) + open(ac.filepath, "r") do matio + seek(matio, di.positionEnd) #this follows the VariableNames matrix + + # read data1 header: + mh1 = readMatrixHeader!(matio) + @assert mh1.name == "data_1" "trying to read matrix [data_1] but read $matrixName" + + data1MatrixStart = mark(matio) + try + toskip = mh1.nRows*mh1.nCols*typeBytes(mh1.format) #817*2*8 = 13072, 488197+20+7+13072 = 501296 + skip(matio, toskip ) + catch e + throw( ErrorException("Caught error $e while reading $ac.filepath") ) + end + + # read data2 header: + mh2 = readMatrixHeader!(matio) + + @assert mh2.name == "data_2" "trying to read matrix [data_2] but read $(mh2.name)" + data2MatrixStart = mark(matio) + + #with the positions marked, read the desired variable + varInd = getVariableIndex(vn, name) + if varInd < 1 + throw( ArgumentError("Variable [$name] not found in file [$(ac.filepath)]") ) + end + + if di.info[varInd]["locatedInData"] == 1 #data_1 + #read the matrix data_1 + # dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. + if di.info[varInd]["isWithinTimeRange"]== 0 #linear interpolation + #data format is: time(tInitial), var1(tI), ... varN(tI), time(tFinal), var1(tF), ... varN(tF) + readns = Vector{mh1.format}(undef, mh1.nCols) + for ind = 1:mh1.nCols + seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*mh1.nRows*typeBytes(mh1.format)) ) + readns[ind] = read(matio, mh1.format) + end + return readns + end + + elseif name == "Time" || name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 + #read the matrix data_2 + if ac.isTranspose == false + # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... + readns = Vector{mh2.format}(undef, mh2.nCols) + for ind = 1:mh2.nCols + seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh2.format) + ((ind-1)*mh2.nRows*typeBytes(mh2.format)) ) + readns[ind] = read(matio, mh2.format) + end + return readns + else + throw(ErrorException("reading binTranspose not implemented, lack test data") ) + end + else + throw(ErrorException("variable [$name] is located in an unknown location") ) + end + end #open +end + +""" +Determine whether the `filepath` is a mat file in the Modelica format. +""" +function isMatV4Modelica(filepath::String) + #first check the extension + ret = splitext(filepath)[2] == ".mat" || splitext(filepath)[2] == ".MAT" + + #now we need to interrogate the contents, by ensuring that all of the internal functions return correctly + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + ret &= typeof(ac) == Aclass + ret &= typeof(vn) == VariableNames + ret &= typeof(vd) == VariableDescriptions + ret &= typeof(di) == DataInfo + return ret; +end + +end #MAT_v4_Modelica diff --git a/src/MAT_v4_pr132.jl b/src/MAT_v4_pr132.jl deleted file mode 100644 index 1952779..0000000 --- a/src/MAT_v4_pr132.jl +++ /dev/null @@ -1,322 +0,0 @@ -# Copied from pr132 of https://github.com/JuliaIO/MAT.jl/pull/132 - -# MAT_v4.jl -# Tools for reading MATLAB v4 files in Julia -# -# Copyright (C) 2012 Simon Kornblith -# Copyright (C) 2019 Victor Saase (modified from MAT_v5.jl) -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -# MATLAB's file format documentation can be found at -# http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf - -module MAT_v4_pr132 -using BufferedStreams, HDF5, SparseArrays -import Base: read, write, close -import HDF5: names - -round_uint8(data) = round.(UInt8, data) -complex_array(a, b) = complex.(a, b) - -mutable struct Matlabv4File <: HDF5.H5DataStore - ios::IOStream - swap_bytes::Bool - varnames::Dict{String, Int64} - - Matlabv4File(ios, swap_bytes) = new(ios, swap_bytes) -end - -const mLITTLE_ENDIAN = 0 -const mBIG_ENDIAN = 1 -const mVAX_DFLOAT = 2 -const mVAX_GFLOAT = 3 -const mGRAY = 4 - -const pTYPE = Dict( - 0 => Float64, - 1 => Float32, - 2 => Int32, - 3 => Int16, - 4 => UInt16, - 5 => UInt8 -) - -const tNUMERIC = 0 -const tTEXT = 1 -const tSPARSE = 2 - -const imagfREAL = 0 -const imagfCOMPLEX = 1 - -read_bswap(f::IO, swap_bytes::Bool, ::Type{T}) where T = swap_bytes ? bswap(read(f, T)) : read(f, T) - -function read_bswap(f::IO, swap_bytes::Bool, ::Type{T}, dim::Union{Int, Tuple{Vararg{Int}}}) where T - d = read!(f, Array{T}(undef, dim)) - if swap_bytes - for i = 1:length(d) - @inbounds d[i] = bswap(d[i]) - end - end - d -end - -function read_bswap(f::IO, swap_bytes::Bool, d::AbstractArray{T}) where T - readbytes!(f, reinterpret(UInt8, d)) - if swap_bytes - for i = 1:length(d) - @inbounds d[i] = bswap(d[i]) - end - end - d -end - -function checkv4(f::IO) - M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, false) - if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 - swap_bytes = false - return (true, swap_bytes) - else - seek(f, 0) - M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, true) - if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 - swap_bytes = true - return (true, swap_bytes) - end - end - return (false, false) -end - -# Read data type and number of bytes at the start of a data element -function read_header(f::IO, swap_bytes::Bool) - dtype = read_bswap(f, swap_bytes, Int32) - - M = div(rem(dtype, 10000), 1000) - O = div(rem(dtype, 1000), 100) - P = div(rem(dtype, 100), 10) - T = div(rem(dtype, 10), 1) - - mrows = read_bswap(f, swap_bytes, Int32) - ncols = read_bswap(f, swap_bytes, Int32) - imagf = read_bswap(f, swap_bytes, Int32) - namlen = read_bswap(f, swap_bytes, Int32) - - M, O, P, T, mrows, ncols, imagf, namlen -end - -# Read matrix data -function read_matrix(f::IO, swap_bytes::Bool) - M, O, P, T, mrows, ncols, imagf, namlen = read_header(f, swap_bytes) - if ncols == 0 || mrows == 0 - # If one creates a cell array using - # y = cell(m, n) - # then MATLAB will save the empty cells as zero-byte matrices. If one creates a - # empty cells using - # a = {[], [], []} - # then MATLAB does not save the empty cells as zero-byte matrices. To avoid - # surprises, we produce an empty array in both cases. - return ("", Matrix{Union{}}(undef, 0, 0)) - end - name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) - if T == tNUMERIC || T == tSPARSE - real_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) - if T == tNUMERIC && imagf == imagfCOMPLEX - imag_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) - data = complex.(real_data, imag_data) - else - data = real_data - end - datamat = reshape(data, Int(mrows), Int(ncols)) - if T == tNUMERIC - return (name, datamat) - elseif T == tSPARSE - if size(datamat,2) == 3 - return (name, sparse(datamat[:,1], datamat[:,2], datamat[:,3])) - else - return (name, sparse(datamat[:,1], datamat[:,2], complex.(datamat[:,3], datamat[:,4]))) - end - end - elseif T == tTEXT - if mrows > 1 - charvec = UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows))) - charmat = reshape(charvec, Int(mrows), Int(ncols)) - data = [String(charmat[i,:]) for i in 1:mrows] - else - data = String(UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols)))) - end - return (name, data) - end -end - -# Open MAT file for reading -matopen(ios::IOStream, endian_indicator::Bool) = Matlabv4File(ios, endian_indicator) - -# Read whole MAT file -function read(matfile::Matlabv4File) - seek(matfile.ios, 0) - vars = Dict{String, Any}() - while !eof(matfile.ios) - (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) - vars[name] = data - end - vars -end - -# Read only variable names -function getvarnames(matfile::Matlabv4File) - if !isdefined(matfile, :varnames) - seek(matfile.ios, 0) - matfile.varnames = varnames = Dict{String, Int64}() - while !eof(matfile.ios) - offset = position(matfile.ios) - M, O, P, T, mrows, ncols, imagf, namlen = read_header(matfile.ios, matfile.swap_bytes) - f = matfile.ios - - name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) - varnames[name] = offset - imag_offset = 0 - skip(f, mrows*ncols*sizeof(pTYPE[P])) - if imagf == imagfCOMPLEX - skip(f, mrows*ncols*sizeof(pTYPE[P])) - end - end - end - matfile.varnames -end - -names(matfile::Matlabv4File) = keys(getvarnames(matfile)) - -# Read a variable from a MAT file -function read(matfile::Matlabv4File, varname::String) - varnames = getvarnames(matfile) - if !haskey(varnames, varname) - error("no variable $varname in file") - end - seek(matfile.ios, varnames[varname]) - (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) - data -end - -function colvals(A::AbstractSparseMatrix) - rows = rowvals(A) - cols = similar(rows) - m,n = size(A) - for i=1:n - for j in nzrange(A,i) - cols[j] = i - end - end - cols -end - -function write(parent::Matlabv4File, name::String, s) - M = Int(parent.swap_bytes) - O = 0 - P = 0 - for p=keys(pTYPE) - if eltype(s) == pTYPE[p] || eltype(s) == Complex{pTYPE[p]} - P = p - end - end - if pTYPE[P] != eltype(s) && Complex{pTYPE[P]} != eltype(s) && - !(s isa AbstractString && pTYPE[P] == Float64) && - !(s isa Vector{String} && pTYPE[P] == Float64) - error("invalid value type when writing v4 file") - end - if s isa AbstractSparseMatrix - T = tSPARSE - elseif s isa AbstractString || s isa Vector{String} - T = tTEXT - else - T = tNUMERIC - end - write(parent.ios, Int32(1000*M + 100*O + 10*P + T)) - - mrows = 1 - ncols = 1 - if s isa AbstractVector && !(s isa Vector{String}) - ncols = length(s) - elseif s isa AbstractMatrix - if s isa AbstractSparseMatrix - mrows = nnz(s) - ncols = 3 - if eltype(s) <: Complex - ncols = 4 - end - else - mrows, ncols = size(s) - end - elseif s isa Vector{String} - ncols = length(s[1]) - mrows = length(s) - elseif s isa AbstractString - ncols = length(s) - end - write(parent.ios, Int32(mrows)) - write(parent.ios, Int32(ncols)) - - imagf = 0 - if eltype(s) <: Complex && T == tNUMERIC - imagf = 1 - end - write(parent.ios, Int32(imagf)) - - namlen = length(name) + 1 - write(parent.ios, Int32(namlen)) - - write(parent.ios, Vector{UInt8}(name)) - write(parent.ios, UInt8(0)) - - if s isa AbstractArray && T == tNUMERIC - write(parent.ios, reshape(real(s), length(s))) - if imagf == 1 - write(parent.ios, reshape(imag(s), length(s))) - end - elseif s isa Number - write(parent.ios, real(s)) - if imagf == 1 - write(parent.ios, imag(s)) - end - elseif s isa AbstractString - floatarray = Float64.(Vector{UInt8}(s)) - write(parent.ios, floatarray) - elseif s isa Vector{String} - floatarray = Matrix{Float64}(undef, mrows, ncols) - for (i,strel) = enumerate(s) - floatarray[i,:] = Float64.(Vector{UInt8}(strel)) - end - write(parent.ios, floatarray) - elseif T == tSPARSE - rows = rowvals(s) - cols = colvals(s) - vals = nonzeros(s) - write(parent.ios, pTYPE[P].(rows)) - write(parent.ios, pTYPE[P].(cols)) - write(parent.ios, pTYPE[P].(real(vals))) - if eltype(s) <: Complex - write(parent.ios, pTYPE[P].(imag(vals))) - end - end -end - -# Close MAT file -close(matfile::Matlabv4File) = close(matfile.ios) - -end diff --git a/test/read.jl b/test/read.jl index 67e62ab..c742cd6 100644 --- a/test/read.jl +++ b/test/read.jl @@ -45,188 +45,201 @@ end global format for _format in ["v6", "v7", "v7.3"] - global format = _format - cd(joinpath(dirname(@__FILE__), format)) - - result = Dict( - "int8" => Int8(1), - "uint8" => UInt8(1), - "int16" => Int16(1), - "uint16" => UInt16(1), - "int32" => Int32(1), - "uint32" => UInt32(1), - "int64" => Int64(1), - "uint64" => UInt64(1), - "single" => Float32(1), - "double" => Float64(1), - "logical" => true - ) - check("simple.mat", result) - matfile = matopen("simple.mat") - mat = read(matfile) - close(matfile) - for (k, v) in result - if(typeof(mat[k]) != typeof(v)) - error("Types for $k didn't match (expected $(typeof(v)), got $(typeof(mat[k])))") + @testset "testing $_format" begin + global format = _format + cd(joinpath(dirname(@__FILE__), format)) + + result = Dict( + "int8" => Int8(1), + "uint8" => UInt8(1), + "int16" => Int16(1), + "uint16" => UInt16(1), + "int32" => Int32(1), + "uint32" => UInt32(1), + "int64" => Int64(1), + "uint64" => UInt64(1), + "single" => Float32(1), + "double" => Float64(1), + "logical" => true + ) + check("simple.mat", result) + matfile = matopen("simple.mat") + mat = read(matfile) + close(matfile) + for (k, v) in result + if(typeof(mat[k]) != typeof(v)) + error("Types for $k didn't match (expected $(typeof(v)), got $(typeof(mat[k])))") + end end - end - - result = Dict( - "imaginary" => ComplexF64[1 -1 1+im 1-im -1+im -1-im im] - ) - check("complex.mat", result) - result = Dict( - "simple_string" => "the quick brown fox", - "accented_string" => "thé qüîck browñ fòx", - "concatenated_strings" => String["this is a string", "this is another string"], - "cell_strings" => Any["this is a string" "this is another string"], - "empty_string" => "" - ) - check("string.mat", result) - - result = Dict( - "a1x2" => [1.0 2.0], - "a2x1" => zeros(2, 1)+[1.0, 2.0], - "a2x2" => [1.0 3.0; 4.0 2.0], - "a2x2x2" => cat([1.0 3.0; 4.0 2.0], [1.0 2.0; 3.0 4.0]; dims=3), - "empty" => zeros(0, 0), - "string" => "string" - ) - check("array.mat", result) - - result = Dict( - "cell" => Any[v for _ in 1:1, - v in (1.0, 2.01, "string", Any["string1" "string2"])] - ) - check("cell.mat", result) - - result = Dict( - "s" => Dict{String,Any}( - "a" => 1.0, - "b" => [1.0 2.0], - "c" => [1.0 2.0 3.0] - ), - "s2" => Dict{String,Any}("a" => Any[1.0 2.0]) - ) - check("struct.mat", result) - - result = Dict( - "logical" => false, - "logical_mat" => [ - true false false - false true false - true false false - ] - ) - check("logical.mat", result) + result = Dict( + "imaginary" => ComplexF64[1 -1 1+im 1-im -1+im -1-im im] + ) + check("complex.mat", result) + + result = Dict( + "simple_string" => "the quick brown fox", + "accented_string" => "thé qüîck browñ fòx", + "concatenated_strings" => String["this is a string", "this is another string"], + "cell_strings" => Any["this is a string" "this is another string"], + "empty_string" => "" + ) + check("string.mat", result) + + result = Dict( + "a1x2" => [1.0 2.0], + "a2x1" => zeros(2, 1)+[1.0, 2.0], + "a2x2" => [1.0 3.0; 4.0 2.0], + "a2x2x2" => cat([1.0 3.0; 4.0 2.0], [1.0 2.0; 3.0 4.0]; dims=3), + "empty" => zeros(0, 0), + "string" => "string" + ) + check("array.mat", result) + + result = Dict( + "cell" => Any[v for _ in 1:1, + v in (1.0, 2.01, "string", Any["string1" "string2"])] + ) + check("cell.mat", result) + + result = Dict( + "s" => Dict{String,Any}( + "a" => 1.0, + "b" => [1.0 2.0], + "c" => [1.0 2.0 3.0] + ), + "s2" => Dict{String,Any}("a" => Any[1.0 2.0]) + ) + check("struct.mat", result) + + result = Dict( + "logical" => false, + "logical_mat" => [ + true false false + false true false + true false false + ] + ) + check("logical.mat", result) + + result = Dict( + "empty_cells" => Any[v for _ in 1:1, v in (zeros(0, 0), "test", zeros(0, 0))] + ) + check("empty_cells.mat", result) + + result = Dict( + "sparse_empty" => sparse(Matrix{Float64}(undef, 0, 0)), + "sparse_eye" => sparse(1.0I, 20, 20), + "sparse_logical" => SparseMatrixCSC{Bool,Int}(5, 5, [1:6;], [1:5;], fill(true, 5)), + "sparse_random" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]), + "sparse_complex" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]*(1. + 1.0im)), + "sparse_zeros" => SparseMatrixCSC(20, 20, ones(Int, 21), Int[], Float64[]) + ) + check("sparse.mat", result) + + matfile = matopen("partial.mat") + var1 = read(matfile, "var1") + @assert var1[28, 33] == 5 + var2 = read(matfile, "var2") + @assert var2[27, 90] == 10 + close(matfile) - result = Dict( - "empty_cells" => Any[v for _ in 1:1, v in (zeros(0, 0), "test", zeros(0, 0))] - ) - check("empty_cells.mat", result) + end +end +@testset "testing bigEndian" begin result = Dict( - "sparse_empty" => sparse(Matrix{Float64}(undef, 0, 0)), - "sparse_eye" => sparse(1.0I, 20, 20), - "sparse_logical" => SparseMatrixCSC{Bool,Int}(5, 5, [1:6;], [1:5;], fill(true, 5)), - "sparse_random" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]), - "sparse_complex" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]*(1. + 1.0im)), - "sparse_zeros" => SparseMatrixCSC(20, 20, ones(Int, 21), Int[], Float64[]) + "index" => [8.8604784000000000e+04 9.8707212000000000e+04 1.0394035200000000e+05 1.1429712000000000e+05 1.5474923999999999e+05 1.5475435200000001e+05 1.5501074400000001e+05 1.5505315200000000e+05 1.5505718400000001e+05 1.5506168400000001e+05 1.5506524799999999e+05 5.4945741599999997e+05 5.6345896799999999e+05 5.9956981200000003e+05 7.0691623199999996e+05 7.9063657200000004e+05 8.4311938800000004e+05 9.2225131200000003e+05 1.1248994160000000e+06 1.2508148520000000e+06 1.4164141320000000e+06 1.4275988280000000e+06 1.4744331000000001e+06 1.4982212879999999e+06 1.5549058440000000e+06 1.5870300840000000e+06 1.6192005120000001e+06 1.6766071560000000e+06 1.9386816839999999e+06 1.9969427879999999e+06 2.0021861880000001e+06 2.3272494120000000e+06 2.5309351080000000e+06 2.6743788720000000e+06], + "spikes" => [ + -3.9146236245031032e+00 -6.7657651330021364e+00 -1.0780027188484372e+01 -1.4345619557780790e+01 -1.5488781013877338e+01 -1.3241531877846004e+01 -8.6339302778751907e+00 -4.1571900578409995e+00 -1.4845719040296610e+00 2.3147400250828232e-01 2.8917910181412778e+00 6.4067867244186800e+00 8.3368575385567603e+00 7.0732985406218223e+00 4.4095174940268036e+00 3.8495932342509342e+00 7.0605464919276546e+00 1.2892731012948772e+01 1.8593404980539656e+01 2.1332908128411184e+01 2.0142332517120792e+01 1.6740473413471157e+01 1.3650330377340575e+01 1.1913871749214691e+01 1.0804794411826084e+01 8.8366401987297127e+00 5.1092331749990514e+00 5.1218216653980408e-01 -2.9327647633922682e+00 -4.4870896208146753e+00 -5.0598199463728655e+00 -4.8330524336350118e+00 -2.8556000012645000e+00 2.9794817723619027e-01 1.8265416505730325e+00 -8.6155940979615875e-02 -3.9623352473810947e+00 -6.9070013227561047e+00 -7.3941131196997647e+00 -5.7411207637544166e+00 -3.2366812420300106e+00 -1.1460492068000723e+00 1.2381260731009580e-01 1.0930145325605314e+00 2.1927876983540933e+00 2.6570284430776856e+00 1.3381366125210661e+00 -1.2539624260623763e+00 -3.3642620416729994e+00 -4.1849749207505456e+00 -3.8760400918509301e+00 -2.6869552030388291e+00 -1.6718246062697015e+00 -2.3709942853677934e+00 -4.6623835517993664e+00 -6.6575320887201714e+00 -6.9891263747717174e+00 -5.7017039068420186e+00 -3.4759011423153079e+00 -1.7092931352045238e+00 -2.3854494206243695e+00 -5.8068462168496913e+00 -9.1001745572212531e+00 -8.8479323560036516e+00 + -5.0745139212223540e+00 -9.6338046242625506e+00 -1.3472006614220170e+01 -1.5245910823426385e+01 -1.5914718584705716e+01 -1.6397777086548157e+01 -1.5688652912166024e+01 -1.2863641020969972e+01 -9.5044110719151487e+00 -8.3463890698794305e+00 -1.0177024989874276e+01 -1.3445743563344164e+01 -1.6692449999817043e+01 -1.9680052373752346e+01 -2.0121942799899024e+01 -8.3700572911264981e+00 3.0032951243048100e+01 9.4582543108269547e+01 1.5377989075611117e+02 1.7130866838277802e+02 1.4748486763605970e+02 1.0947277486760775e+02 7.2684596239613015e+01 3.8193102601464858e+01 1.0540246012312117e+01 -3.8091287155762972e+00 -5.9643997244651556e+00 -3.0829122045048947e+00 -9.3969579584981044e-01 -4.1011214330806744e-01 1.1485354724913588e-01 8.3209939369519947e-01 1.7132389429717065e-03 -3.6057351605809611e+00 -8.8203434292989460e+00 -1.2692989286632969e+01 -1.3074091545751195e+01 -1.0580269175201330e+01 -7.7262110976707312e+00 -6.1406117115548504e+00 -5.0818589866084025e+00 -3.3150901769832091e+00 -1.7731765969545847e+00 -2.5627661695922046e+00 -5.6404917366725886e+00 -8.3842617579066747e+00 -8.5769172149145199e+00 -6.8248629640086911e+00 -5.7747794329330571e+00 -7.1063437611930578e+00 -9.7162880960954556e+00 -1.1170428842658552e+01 -1.0378119546974839e+01 -8.3419883744685741e+00 -6.9209209230224635e+00 -7.4145555946251820e+00 -9.6713314325255784e+00 -1.2062946758371979e+01 -1.2965017154593347e+01 -1.2408773058757985e+01 -1.1624355346583785e+01 -1.1127849526328186e+01 -1.0048811168388408e+01 -7.6327226475166228e+00 + -3.6172800577681850e+00 -3.0496107525626650e+00 -4.0450961689646379e+00 -4.9841853952779340e+00 -4.3137243411079638e+00 -2.4211699563520748e+00 -9.3469136675988018e-01 -9.3834475312754062e-01 -2.3043613016166664e+00 -3.9905808684096256e+00 -4.6842071022425413e+00 -3.8310453992051836e+00 -2.8818230979207713e+00 -5.2038837297130556e+00 -1.1725055393804819e+01 -1.3208091586845086e+01 9.2482123494256854e+00 6.3181823866266974e+01 1.2177486062412244e+02 1.4070171740815530e+02 1.0609880427064739e+02 4.8491974791356185e+01 5.8467389218168400e+00 -9.6802170255950291e+00 -8.6605173391900614e+00 -5.1555616199626577e+00 -5.4237382663488169e+00 -8.2575373220146044e+00 -1.0156241701964554e+01 -9.1574620977201118e+00 -5.6665700132115431e+00 -1.5504659563007614e+00 1.1334383183944798e+00 1.2640431009400714e+00 -6.6794751018025034e-01 -2.9543901520038176e+00 -4.1963329340429310e+00 -4.3642482086638417e+00 -4.0042244694146101e+00 -3.1394295409389494e+00 -1.8179756080601912e+00 -9.3024527470805374e-01 -1.4736656790371958e+00 -3.4645773489091209e+00 -5.8588062538620047e+00 -7.0734948204842896e+00 -6.0481893429370261e+00 -3.3344016972431088e+00 -4.8650896484328832e-01 1.6052150099271567e+00 2.9838901666777322e+00 3.7836445807114574e+00 3.9345735120816623e+00 3.1644840267225094e+00 1.0070686684300534e+00 -2.4713922778659478e+00 -5.9984216012494951e+00 -7.8885829262850233e+00 -7.5154376078825695e+00 -5.8326333185237758e+00 -4.5003931071444478e+00 -4.2328547992043450e+00 -4.1140676583157889e+00 -3.1460951038002301e+00 + -3.6781701228839154e+01 -3.7303084834807457e+01 -3.8884510028175633e+01 -4.1148035399962872e+01 -4.3316242853459642e+01 -4.4384384580854572e+01 -4.4790855343692897e+01 -4.7038755365884079e+01 -5.2501365276546458e+01 -5.6812084874163638e+01 -4.9570438054352273e+01 -2.3981805280442412e+01 1.1015352613446286e+01 3.7025927055247365e+01 4.6196840812364755e+01 4.5435239856395953e+01 4.4108776047185387e+01 4.5957015883875258e+01 4.9338962205718175e+01 5.0907715343642117e+01 4.9487314118838199e+01 4.6376644839165742e+01 4.2907993314061748e+01 3.9175485818078528e+01 3.4646364450142663e+01 2.9438295855526434e+01 2.5290001942034166e+01 2.4585557992414284e+01 2.7869881512937130e+01 3.2856199330415350e+01 3.6123557386279117e+01 3.5905020129260436e+01 3.3291843240497656e+01 3.0657151735220140e+01 2.9399978478943620e+01 2.9238216866099457e+01 2.8701738303043562e+01 2.6077460100630137e+01 2.0689205683518288e+01 1.3779281580459998e+01 8.1712538443203133e+00 6.6925061080176569e+00 1.0147005424138726e+01 1.6509827576474752e+01 2.2276297357800129e+01 2.4765444980789738e+01 2.3565808601878054e+01 2.0365852060539318e+01 1.7482427576467405e+01 1.5922922611993604e+01 1.4534460949429915e+01 1.1655908219543699e+01 7.5216074902940893e+00 3.9334852970087364e+00 1.7985868168036778e+00 3.9751100517608062e-01 -9.6689135481901367e-01 -1.8291316756437888e+00 -1.2108732556384698e+00 1.1422814255806824e+00 4.0827052431935931e+00 5.6696392298883893e+00 4.7889899662105178e+00 1.9874261259261794e+00 + -1.1686614851500112e+00 9.8124871505894706e-01 2.4682927864975457e+00 2.9222372863110442e+00 1.6794130795786746e+00 -6.8820557107116076e-01 -2.4491510090825823e+00 -2.5573541785944220e+00 -1.5657335653505560e+00 -6.2326635393824792e-01 7.5542615186640072e-03 1.2160520506156904e+00 3.7959025732449647e+00 7.1695274203178112e+00 9.8085862745466752e+00 1.1024667458560756e+01 1.1849030121520164e+01 1.3645183550697372e+01 1.6153045543872672e+01 1.7653460112285362e+01 1.6917192047304358e+01 1.4287778990065465e+01 1.1181854556421472e+01 9.2813981144484536e+00 9.6980182244576554e+00 1.1975656236851366e+01 1.4218362205978007e+01 1.4823189770395057e+01 1.3782281414778801e+01 1.2224714159620135e+01 1.1308817315730568e+01 1.1413207645545857e+01 1.1802812942398777e+01 1.1304006120772028e+01 9.4673823725009267e+00 6.7674104407290443e+00 3.9034391463718467e+00 1.0696510964445158e+00 -2.2265622627917740e+00 -6.1619053880194183e+00 -9.4895708918063448e+00 -1.0511848180773416e+01 -9.2016379963305752e+00 -7.3655072801953869e+00 -6.6664600174160604e+00 -7.2291846775841488e+00 -8.2107481715781105e+00 -9.1021811818757321e+00 -1.0311278259980540e+01 -1.2741511638678721e+01 -1.6437776102727412e+01 -1.9514979330779195e+01 -1.9610190285680936e+01 -1.6644297020966704e+01 -1.2800100439249650e+01 -9.9046719354109900e+00 -8.2923627062655303e+00 -7.8966764067275435e+00 -8.8257663696547084e+00 -1.0735616318501446e+01 -1.2599202496900540e+01 -1.3220431400502429e+01 -1.1920594647006027e+01 -9.3084512934042376e+00 + 1.1099942135772691e+01 1.0097197127580243e+01 1.1065906114342178e+01 1.3374123078938322e+01 1.6449533911253752e+01 2.0017355902687680e+01 2.3638247141687746e+01 2.6292880854006093e+01 2.6783333166190332e+01 2.5183008648660202e+01 2.3296988595385344e+01 2.2810464758877668e+01 2.3624918756652875e+01 2.4607374587222779e+01 2.5393398749862719e+01 2.6769469521678889e+01 2.9281995275221178e+01 3.2213436799025317e+01 3.4285343581139983e+01 3.4785296465751173e+01 3.3883822298572078e+01 3.2386332385252516e+01 3.1231943637716320e+01 3.0932840342758819e+01 3.1246878043151604e+01 3.1160302181567783e+01 2.9550070933834700e+01 2.6540966708675843e+01 2.3785330200047358e+01 2.2611886801417548e+01 2.2531515536471172e+01 2.2340044415310590e+01 2.1822535590221452e+01 2.1399364443416083e+01 2.0755992097134378e+01 1.8948315967557182e+01 1.5769917402888659e+01 1.2205733819672014e+01 9.4099541981525228e+00 7.7046512181258402e+00 6.5719664417242711e+00 5.2343013227680553e+00 3.3389338216778657e+00 1.2573655827507051e+00 -5.0199990120897509e-01 -1.8987019795142805e+00 -2.9172592054133535e+00 -3.2152176994632509e+00 -2.9689210401896537e+00 -3.2720957493420673e+00 -4.8582690799070640e+00 -7.1594797906170751e+00 -9.1803890906391956e+00 -1.0495134027231670e+01 -1.1056911353417380e+01 -1.0726534970818260e+01 -9.3753223680626547e+00 -7.2907274381158134e+00 -5.1694262729300959e+00 -3.7312476583791785e+00 -3.6141234953544066e+00 -5.1662008322483448e+00 -7.7776079749577836e+00 -9.8010587078412321e+00 + -1.4685530727984641e+01 -1.0292328330297664e+01 -6.2040173116488546e+00 -2.3375015307800595e+00 1.3923917658511158e+00 5.0564769748901313e+00 8.6976966359851104e+00 1.2323245348083425e+01 1.5903913125353689e+01 1.9380777962659089e+01 2.2677898949762564e+01 2.5718454097742246e+01 2.8441073995486732e+01 3.0813082951470030e+01 3.2837981366611231e+01 3.4555661605993883e+01 3.6035311542317494e+01 3.7362431137561401e+01 3.8622571934083787e+01 3.9885068827368393e+01 4.1190035284914742e+01 4.2541236867236606e+01 4.3906275503545629e+01 4.5224046329587310e+01 4.6417965861852949e+01 4.7412309197387181e+01 4.8148368395523121e+01 4.8597180088665191e+01 4.8766260494684808e+01 4.8698991553936125e+01 4.8466780378761598e+01 4.8155568154983840e+01 4.7849401563031734e+01 4.7614370946271123e+01 4.7486145888939269e+01 4.7463615344086584e+01 4.7509911501785595e+01 4.7560611227134473e+01 4.7537464942025593e+01 4.7364890774312840e+01 4.6985915393397903e+01 4.6374354157541411e+01 4.5540779674504876e+01 4.4531077464674446e+01 4.3417878743397615e+01 4.2286593389902350e+01 4.1218852636871070e+01 4.0276692303289153e+01 3.9490658602725233e+01 3.8854229591844380e+01 3.8325674875439489e+01 3.7836979823323738e+01 3.7308039311521902e+01 3.6663265807716996e+01 3.5847270787923392e+01 3.4836464766174224e+01 3.3644242194420237e+01 3.2318708010392434e+01 3.0933403069436942e+01 2.9572894236760884e+01 2.8316128161485977e+01 2.7220897815058663e+01 2.6312547181929837e+01 2.5579187011419549e+01 + 1.5763956152315169e+01 1.6523243082892250e+01 1.7337427631570872e+01 1.8120936638484590e+01 1.8792445157811912e+01 1.9291006994381139e+01 1.9588077541817789e+01 1.9693009891213325e+01 1.9650918197573489e+01 1.9533337877188348e+01 1.9423561429962543e+01 1.9399599967723205e+01 1.9518198338118580e+01 1.9803119911795868e+01 2.0240058237539227e+01 2.0779199633821964e+01 2.1344921568275133e+01 2.1850676042659646e+01 2.2216063986224665e+01 2.2382665762479274e+01 2.2325443040524132e+01 2.2057418172501748e+01 2.1626689639781716e+01 2.1106384004760407e+01 2.0579565930487874e+01 2.0122142297473431e+01 1.9787200170220586e+01 1.9593930129327656e+01 1.9523364223698717e+01 1.9521786868823959e+01 1.9511132956806740e+01 1.9404280892123200e+01 1.9122163091585080e+01 1.8609252789360340e+01 1.7844318015586811e+01 1.6844290518050197e+01 1.5660474424296631e+01 1.4367827117720619e+01 1.3049394428891683e+01 1.1778975433290963e+01 1.0605636123380648e+01 9.5436591203415890e+00 8.5705174399266060e+00 7.6329046830511684e+00 6.6569045386579333e+00 5.5557424579780292e+00 4.2330232952205966e+00 2.5948387638090815e+00 6.0010139846456756e-01 -1.6403240489988979e+00 -3.7983903715983196e+00 -5.5143675763583140e+00 -7.0197817000212250e+00 -9.2991809829904906e+00 -1.2710108289977208e+01 -1.5773321560596763e+01 -1.6492611312664831e+01 -1.5291065260668367e+01 -1.5553348127486492e+01 -1.9596399167644933e+01 -2.5711586491024381e+01 -3.1294712703031326e+01 -3.6140274982880157e+01 -4.0736526948594026e+01 + -7.5455089032603496e+01 -8.8091850484650791e+01 -7.6894847494814442e+01 -5.1931618057453484e+01 -2.8833831644552824e+01 -6.8895988088626297e+00 2.1168348264522379e+01 4.6693994244353583e+01 5.3777387223823958e+01 4.4603130335345831e+01 3.3530654869558120e+01 2.3587826739136968e+01 8.4238787409792160e+00 -9.2318222698507917e+00 -1.4713136414898660e+01 1.9717165915666754e+00 3.1968641163889899e+01 5.7269930354848498e+01 7.0152688867705734e+01 7.3409072049238773e+01 7.0166235593951257e+01 6.1829204326045371e+01 4.6696134605544373e+01 1.8622054473547621e+01 -2.2829591964997910e+01 -6.2715671170700787e+01 -8.2653742502672856e+01 -8.1088328703707077e+01 -7.2961806812740548e+01 -7.3632394853923415e+01 -8.9841480609702060e+01 -1.1962228131535032e+02 -1.5066572610100792e+02 -1.6038121312642346e+02 -1.2981471355911390e+02 -6.3653163261418783e+01 9.3811209125033130e+00 5.8695741367623484e+01 7.0437047869761585e+01 4.9630793312944938e+01 1.6599415269056415e+01 -2.5402904227425278e+00 4.0559101821793178e+00 2.4429205550104435e+01 3.9021358713588448e+01 3.8520783430674051e+01 1.9952697815867261e+01 -2.1877257859243720e+01 -8.3907527060881065e+01 -1.4250639755111564e+02 -1.6619398657243511e+02 -1.3819218483975862e+02 -6.6785267226843359e+01 7.8497452778842174e+00 2.7663034892382331e+01 -1.9252281399357905e+01 -5.9646303432603780e+01 -1.8852103558667594e+01 6.7639555235119275e+01 9.6932460076734856e+01 4.1262889176984515e+01 -3.3610639804651925e+01 -7.8423083296187542e+01 -1.0839727633754131e+02 + 4.7736577112711117e+01 4.5700337234538978e+01 4.5322631494156710e+01 4.9171444949072850e+01 5.3757473474671947e+01 5.4141589236927501e+01 4.8790940853843097e+01 4.1857477273482019e+01 4.0754533608850998e+01 4.8608027316167508e+01 6.0100216178867996e+01 6.8180296544356494e+01 7.1421072557515430e+01 7.0807306032014466e+01 6.6514973934144194e+01 6.2945074970931898e+01 6.8196514720480167e+01 8.5012727237677268e+01 1.0720726826115141e+02 1.1957256746876999e+02 9.9138254538272335e+01 3.8069276174166163e+01 -2.7233524821672358e+01 -3.9334632563706222e+01 1.3886634524961096e+01 7.3584276961424322e+01 6.3628518198466388e+01 -2.9054572516934122e+01 -1.4209888641029568e+02 -2.0910522987863314e+02 -2.1415046392049794e+02 -1.7586701353513934e+02 -1.1844102599759782e+02 -6.1431963885049129e+01 -1.4806030025628601e+01 1.4027944301627443e+01 1.0652586257860358e+01 -3.0054086554043813e+01 -8.5072361204847368e+01 -1.2046643138190960e+02 -1.2106054250546094e+02 -9.5841527604366775e+01 -6.6055322458778861e+01 -4.4458770387691487e+01 -2.4336370991880798e+01 4.5840797349366902e-01 2.1040288319943066e+01 2.3611095638912182e+01 1.3051021255932143e+00 -2.8851154140515209e+01 -3.7619486391456242e+01 -2.9915658771811749e+01 -3.0708973922309450e+01 -3.1924567737368655e+01 -1.0202022943830535e+01 2.9706495601613049e+01 5.9067239546813376e+01 5.4702088800913174e+01 2.8343487643024787e+01 1.5951389347675942e+01 2.2837902575396686e+01 1.2908948645547047e+01 -3.3255358730535995e+01 -8.7746125812653432e+01 + -8.2377418542911354e+00 -3.4972931824407372e+00 -7.7569626244497876e+00 -1.5166206734460895e+01 -1.5445771078633909e+01 -5.2413228186791176e+00 8.4387175092257500e+00 1.5431757026108208e+01 1.2399978335869392e+01 7.4095099729504046e+00 1.0679579816928053e+01 1.8701188652942292e+01 1.7701213689196198e+01 4.8431711642004913e+00 -4.7779662604481938e+00 3.1723005655630221e+00 2.4531952072999687e+01 4.5698045473740862e+01 5.9257083049327122e+01 6.2627426268528140e+01 5.7411981420461260e+01 5.2570470576359099e+01 5.2813113343401156e+01 5.0453507704104304e+01 3.9691119012785791e+01 2.6528767918502940e+01 1.8741299694240368e+01 1.7724441294399568e+01 2.0821175647900695e+01 2.4417659444952996e+01 2.5870167533339281e+01 2.4983092349575369e+01 2.2998436116876093e+01 2.0558711472598521e+01 1.7502822279045098e+01 1.3739438787271743e+01 9.9370928813924273e+00 7.9696531198819107e+00 9.3237074864808100e+00 1.2496048137913172e+01 1.4202138161523894e+01 1.3776871923186704e+01 1.4057294551750237e+01 1.6919973545359703e+01 2.0190481439159552e+01 2.0371692873037446e+01 1.6567959095411027e+01 1.0520230640011906e+01 4.5794135735479564e+00 8.7710329733688541e-01 8.9182399282027713e-01 4.3463285075996909e+00 8.7315858121978973e+00 1.0903936631346470e+01 1.0288756426506817e+01 1.0049725699443105e+01 1.3054576528907717e+01 1.6854021523448839e+01 1.6041035800270304e+01 1.0367866836597134e+01 6.4265096090680434e+00 9.4949675891987368e+00 1.6836520945082036e+01 2.1325105933919993e+01 + -4.1679158148256432e+00 -5.2810404688221357e+00 -5.9038977834292563e+00 -6.2634044553555412e+00 -5.9749855839469195e+00 -4.3218694766614334e+00 -1.1788772851158797e+00 2.4374543533437842e+00 4.8050829080472326e+00 4.8908091608309068e+00 3.3699448624872437e+00 1.8764385857350361e+00 1.0658937699825455e+00 -2.7882253437288551e-01 -3.1356733669314134e+00 -4.1284337659893531e+00 4.9241012162347957e+00 2.8218865342091348e+01 5.5056325524296241e+01 6.5099843979883218e+01 4.9609447627592324e+01 2.1423693795580775e+01 2.5132863903596458e-01 -5.8604600478907667e+00 -2.3380828579082129e+00 1.9600991106694594e+00 2.5915747007003080e+00 2.0190047306038350e-01 -2.8577160575959448e+00 -5.2869354732703258e+00 -6.9099491413979388e+00 -7.7559496509908179e+00 -8.0339926236897785e+00 -8.1760789963289717e+00 -7.9859360445805283e+00 -6.3703713820186918e+00 -3.0321001946270005e+00 -2.8201117047924162e-02 -7.8351721876626979e-02 -3.2229219827610613e+00 -6.6022607253341592e+00 -7.6428337012652356e+00 -6.3520480710327867e+00 -4.5468067925800151e+00 -3.6792489626697655e+00 -3.7265752054412307e+00 -3.8104694479464527e+00 -3.1502606288908388e+00 -1.5302095841423975e+00 2.6517264914271876e-01 7.6568764912213050e-01 -5.6131323023144386e-01 -2.6412705089170849e+00 -4.2923899829303380e+00 -5.1769894068210993e+00 -4.9614565332821146e+00 -3.2859163281387294e+00 -1.1703534382204590e+00 -7.6910847698287388e-01 -2.6122681986842187e+00 -4.2742848318737003e+00 -3.0739009488926641e+00 6.8120601334287323e-01 3.9336266256502612e+00 + -4.6484775115089541e+00 -8.3581561181698660e+00 -1.0902142569692860e+01 -1.1511797760460409e+01 -9.8276834132977005e+00 -6.2404161549022161e+00 -1.8432175810231679e+00 1.9347492334298952e+00 4.2938226787082971e+00 5.6566425031547753e+00 6.9370739014200060e+00 8.7323889596425897e+00 1.1151007876295226e+01 1.3469254774199312e+01 1.4378076971481226e+01 1.3628707243729355e+01 1.2810890536991595e+01 1.3603571552449427e+01 1.5792969414511386e+01 1.7425203016009618e+01 1.6493276434801135e+01 1.2586445678344095e+01 7.3401463249477024e+00 3.2087842017129540e+00 1.5030100447830739e+00 1.3898465906994972e+00 8.8198090743093560e-01 -1.0493063115005812e+00 -3.7608545130075726e+00 -6.3949540211998350e+00 -9.0066585358720026e+00 -1.1745588278397154e+01 -1.3842632605375675e+01 -1.4123365855042982e+01 -1.2335802612795424e+01 -9.4739532170207532e+00 -6.3782197090918142e+00 -2.8832164731098446e+00 4.8447063694123349e-01 1.6128314119847449e+00 -4.1774621162461167e-01 -3.3629973208103769e+00 -4.5025431650456200e+00 -3.9290643942165806e+00 -3.7022642550448306e+00 -4.6991587651781321e+00 -5.6909794907882132e+00 -5.2906700399814426e+00 -3.7661436808309654e+00 -2.5302504318642383e+00 -2.0969878128246422e+00 -1.3265870686252779e+00 7.0961290345922357e-01 3.0385814819877943e+00 3.9548508946305954e+00 2.9954312845408939e+00 9.9988805713822959e-01 -9.2502312206375614e-01 -2.0384940259267319e+00 -2.2297243620823197e+00 -2.0769146009998161e+00 -2.3170471037592764e+00 -2.8506427004880215e+00 -2.6777747842245225e+00 + -7.7992155184377432e-01 8.4208602530413512e-01 2.4887283142722940e+00 2.4338293029048339e+00 8.3831805409641036e-01 -5.6099977991794492e-01 -7.1266715703072170e-01 -2.3092906084045711e-01 1.6955881483329160e-01 8.8504105274391720e-01 2.2423969436547071e+00 3.3863034262368776e+00 3.6616215908416012e+00 4.2752627336864073e+00 7.0581057720790223e+00 1.1758833658720096e+01 1.6047422019307188e+01 1.8405075233788665e+01 1.9427396781993370e+01 1.9879444229356857e+01 1.9348574654086402e+01 1.7629540788742673e+01 1.5792875449818970e+01 1.4872230427632566e+01 1.4471792816861928e+01 1.3426898347317799e+01 1.1319456367524939e+01 8.8575138135993203e+00 7.0846315948436089e+00 6.5127944804059057e+00 6.6781793515550252e+00 6.2758575263602134e+00 4.2213211609500574e+00 1.0187928511802555e+00 -1.3365602410322484e+00 -1.2247622425200726e+00 1.1481723220280773e+00 4.3321910406078903e+00 6.9648334152330520e+00 8.0154771868425438e+00 6.5719188712588483e+00 2.4474218319276826e+00 -3.0491523253799624e+00 -7.4154828913997903e+00 -8.5953677824666350e+00 -6.7813390775209372e+00 -4.2068257455106703e+00 -2.7309337921106196e+00 -2.0348235578019351e+00 -6.2767747718278888e-01 1.7379756336568186e+00 3.5393101620046572e+00 3.5444884355037840e+00 2.3746522750550279e+00 1.3500592448824880e+00 6.9976097822180139e-01 -1.5618921435797500e-01 -1.3162226599662721e+00 -2.4845798158016903e+00 -3.7797798352858396e+00 -5.2368125011457316e+00 -5.9486138818233414e+00 -5.1354371684134463e+00 -3.7528582973371494e+00 + -3.0447537175403006e+00 -1.7667805638958940e+00 -1.9778229750617011e+00 -2.2515604403315699e+00 -1.3449261643785633e+00 -1.0662626484421048e-01 -5.7657486030100791e-02 -7.4315041221388778e-01 -3.2337810363445102e-01 1.8577438758015754e+00 4.8425765941254983e+00 7.8123978114524020e+00 1.1105854493054094e+01 1.4841903991300153e+01 1.7468871344145363e+01 1.7574512951317725e+01 1.6451820522767886e+01 1.6628323973953727e+01 1.8301434258000356e+01 1.8931382166939795e+01 1.6217394592510296e+01 1.0559530299242045e+01 4.4576293834236687e+00 -1.1822470014800246e-02 -2.5727563770885062e+00 -3.7547505719161225e+00 -3.5081536657220189e+00 -1.4835065248806825e+00 1.8247576781703743e+00 4.8275703093820033e+00 5.7228574725963268e+00 3.8558888609578723e+00 4.0990146070366057e-01 -2.4574645653496310e+00 -3.1102243476353593e+00 -1.4474225309731716e+00 7.4127875680854416e-01 1.0383069562131020e+00 -1.0150232301722759e+00 -3.4198210424706135e+00 -4.3088303898183238e+00 -3.9165170232756070e+00 -3.6605958183355347e+00 -4.2469618414480417e+00 -5.2237494007048566e+00 -5.9420620236257076e+00 -6.1698135259889568e+00 -5.7089733374935268e+00 -4.0492707065696969e+00 -8.0849384208890562e-01 3.3216805308527921e+00 6.2990262694136261e+00 6.1812329879916819e+00 3.1266169114028388e+00 -4.8505368345761951e-01 -2.2625885070785667e+00 -2.0371523965480165e+00 -1.4033783831712108e+00 -1.2651726669536290e+00 -8.7131680693218405e-01 5.0106877171388586e-01 1.8406747295741790e+00 1.3085085083594870e+00 -1.5293730324086736e+00 + 2.3513445326289961e+00 1.7384880477187443e+00 2.2184897775735659e+00 5.2051223974837342e+00 1.0452816121087638e+01 1.5377084182445486e+01 1.7198975360259148e+01 1.6104713320124159e+01 1.4833420837246866e+01 1.5100670860303905e+01 1.6073099350257220e+01 1.6269109488250638e+01 1.5637757524959511e+01 1.5476499170789715e+01 1.6604024256326596e+01 1.8317676582762779e+01 1.9829075755286041e+01 2.1816151151864332e+01 2.5055347925633036e+01 2.7333907737059032e+01 2.2427805378565086e+01 3.7141556286936721e+00 -2.8648138173116212e+01 -6.3843957225821207e+01 -8.7089860614432553e+01 -9.1923311472937954e+01 -8.3938126428996938e+01 -7.2885847777427841e+01 -6.4297677965088411e+01 -5.7914366258401401e+01 -5.0470379597131561e+01 -3.9499120538780375e+01 -2.5751695307781368e+01 -1.1778187088014317e+01 1.0189820693630494e+00 1.1960524761686607e+01 1.8733303083800365e+01 1.8356414318508428e+01 1.0668647686854321e+01 -1.0075011878329772e+00 -1.2633619248121462e+01 -2.1404683668367543e+01 -2.5602377289037388e+01 -2.4560926814716566e+01 -1.8847733387633440e+01 -9.8404336731293878e+00 3.5966958558048301e-01 9.0662935627188972e+00 1.4335829915263089e+01 1.6036694595613060e+01 1.5184337823870287e+01 1.2469246394512929e+01 7.6677145828817306e+00 8.5165822752424969e-01 -5.8806346340252293e+00 -9.1084583818696743e+00 -7.2999534121027425e+00 -2.4813921667292815e+00 1.7215656085634032e+00 3.0197896060174911e+00 1.4579843578258131e+00 -1.6420102258369735e+00 -4.8732983588159042e+00 -6.9044980772313806e+00 + -7.1748224092297468e+00 -3.8936102456845245e+00 1.0743831294909865e+00 4.8816301487736782e+00 5.4189196440465226e+00 2.8421806913201926e+00 -7.8193972997551731e-01 -3.3953855854612938e+00 -3.9985877326731809e+00 -2.4284915455410818e+00 3.3095855413398589e-01 2.1860065858780535e+00 1.8280988855495470e+00 3.4048799490454618e-01 3.8790118108287441e-01 3.8556647578240639e+00 9.9718322097927121e+00 1.5912297294502212e+01 1.9511397698602284e+01 2.0651366980982488e+01 2.0134282655984631e+01 1.8560445836154724e+01 1.6287447146155721e+01 1.3561600277652611e+01 1.0795264078502715e+01 8.6030785401577266e+00 7.0022103875078896e+00 5.1888911547538754e+00 2.7145704050730384e+00 1.4143845469113936e-01 -1.9107456843838713e+00 -3.4559243419646326e+00 -4.2223452047916581e+00 -3.1130434517080205e+00 1.7183260785086762e-01 3.7869557639525033e+00 5.3380153379310666e+00 4.2352058165010487e+00 1.6573488592368961e+00 -8.5887188564406203e-01 -2.1681503131724709e+00 -2.1279908375294667e+00 -2.0792749208808359e+00 -3.3997834567720187e+00 -5.3580052019440032e+00 -5.9792374734377622e+00 -4.8403202446726707e+00 -3.3487890303481347e+00 -2.6600981462061033e+00 -2.7228934357253367e+00 -3.1308053901450172e+00 -3.8963449071942557e+00 -5.2729284759196471e+00 -7.2764195946828067e+00 -9.3283947520693822e+00 -1.0227824562298673e+01 -9.0739885142293417e+00 -6.5681575524140818e+00 -4.8038541028704778e+00 -5.3349275900632449e+00 -7.6025577687023391e+00 -9.3130362450711317e+00 -8.3152888687476345e+00 -4.2484255376825795e+00 + -1.2094820634316039e+00 -1.2706817164948352e+00 -1.2775018026388452e+00 -2.3357176552894954e-01 1.8928499829725349e+00 4.4677863928736414e+00 6.9513585826945681e+00 9.0204123867514259e+00 1.0482452392061710e+01 1.1455309752506835e+01 1.2237810752650413e+01 1.2688637793112969e+01 1.2235313891126804e+01 1.1100297213735251e+01 1.0773414547739115e+01 1.2217993905654721e+01 1.4701466325393794e+01 1.7319490559451381e+01 1.9808972202344144e+01 2.1257689253929254e+01 2.0368628657184544e+01 1.7524563363309646e+01 1.4843567969750833e+01 1.3863574192388871e+01 1.4031045614008303e+01 1.3693669004482535e+01 1.2122436338523961e+01 9.9467781708780620e+00 7.8498090003437868e+00 5.7967683127528629e+00 3.7206536395890204e+00 2.3267777177674382e+00 2.6303789001713365e+00 4.7445695138569235e+00 7.6683738268993942e+00 9.9717476628589878e+00 1.0271279571889663e+01 8.1958760665984958e+00 5.3998213275104048e+00 4.2899793853628481e+00 5.5462911375305124e+00 7.6595253723593695e+00 8.4981448223169700e+00 7.2294579833209385e+00 5.1143564806110922e+00 3.8260968045635870e+00 3.3065755059745818e+00 2.5815464286120089e+00 1.7488042411202225e+00 1.5184709880337026e+00 1.6370045591241176e+00 1.1531897091681369e+00 -1.5863929796862442e-01 -1.5759013113892080e+00 -2.3911490323759002e+00 -2.4147368159491429e+00 -1.8749121657426793e+00 -1.4086065112641286e+00 -1.6573036686621481e+00 -2.4019595691049194e+00 -2.7197582423545761e+00 -2.3228923158464436e+00 -1.8987417992229161e+00 -1.9762436025825927e+00 + -2.1788608089091870e+00 -2.3721017929273520e+00 -2.2248744539174794e+00 -1.3562719882732486e+00 1.9303974836178317e-01 1.3110471548593794e+00 1.2797866409946126e+00 1.1446512790093115e+00 2.4218364442454297e+00 5.3568199699566073e+00 9.1928444896243846e+00 1.2911855868338753e+01 1.5131303558516208e+01 1.4796675429288374e+01 1.2747818159896775e+01 1.1384776546745211e+01 1.2304499348204516e+01 1.5012766622841385e+01 1.7706586949808607e+01 1.8615946254339946e+01 1.7088985921306737e+01 1.4118105664819083e+01 1.1547667913836323e+01 1.0268505344072013e+01 9.5426651513383796e+00 8.3936752159092531e+00 6.7898835952875585e+00 4.9190137058105190e+00 2.3035160443938620e+00 -1.4454604882529436e+00 -5.5578141560543060e+00 -8.6285337833508056e+00 -9.8768639485094845e+00 -9.3497459396540972e+00 -7.4531759065479113e+00 -4.8343191161214989e+00 -2.2707557414183874e+00 -2.1273932165940168e-01 1.4763518504563440e+00 2.9929246806770484e+00 3.6484933755646152e+00 1.9940019916984792e+00 -2.3622776610960083e+00 -7.2558689942915287e+00 -9.3484617741468909e+00 -7.4954744905729234e+00 -3.9544592514927057e+00 -1.7075406868672076e+00 -1.4165546781975049e+00 -1.6221832630448796e+00 -1.0260840386708110e+00 1.6403282116014173e-01 6.0805828587662303e-01 -7.8558049118458273e-01 -3.6478240721938118e+00 -6.4046749238136291e+00 -7.7290809125845756e+00 -7.3840184127203816e+00 -5.7754694634749093e+00 -3.6798105136804447e+00 -2.5013142267663810e+00 -3.3639333122560284e+00 -5.7371424805126106e+00 -8.0051890648261459e+00 + -1.5740146351038696e+01 -1.5127188568935047e+01 -1.6157259030112712e+01 -1.7330034352768784e+01 -1.7263465597740716e+01 -1.6336957355980775e+01 -1.5946556228923413e+01 -1.6451747846316870e+01 -1.6476134055902989e+01 -1.4472580313892262e+01 -1.0935457247727644e+01 -7.9876368021814939e+00 -6.0265186692348891e+00 -2.0146808151690361e+00 8.0163390797194936e+00 2.5505537996776390e+01 4.7787314568418353e+01 6.8639540131510500e+01 8.1758985020608336e+01 8.5294009078541549e+01 8.2283381938562812e+01 7.6550409062053944e+01 6.9344678720567757e+01 6.0023044834365038e+01 4.8849934461625729e+01 3.8040291720134526e+01 2.9945383165919644e+01 2.5344624124046817e+01 2.3679402477458130e+01 2.3454441703713346e+01 2.2281261219059516e+01 1.7989994660541914e+01 1.0436193819072196e+01 2.1102305275848425e+00 -3.6577185659374600e+00 -5.5145847757485971e+00 -4.5324408328651318e+00 -1.8554008253732013e+00 2.7089378387782199e+00 9.2187555916040793e+00 1.5945566856263504e+01 2.0472163602212458e+01 2.2294516323769198e+01 2.2881411820375533e+01 2.2911237311157720e+01 2.1364859320014958e+01 1.7602082925628398e+01 1.2398805245993104e+01 6.8726800470759422e+00 1.6538594259187884e+00 -2.9437120155973195e+00 -6.5367981004048668e+00 -8.6437256178931250e+00 -9.1231101796868952e+00 -8.6648085339554566e+00 -8.4735847323575335e+00 -8.9963274994615468e+00 -9.3198443907122499e+00 -8.2504681130406006e+00 -5.5321797455350028e+00 -1.9690195798275383e+00 9.0573194795780632e-01 1.6386927935969133e+00 3.9409445444748448e-01 + -1.2097853394633344e+00 -2.3372661136012782e+00 -4.5868126352335468e+00 -7.7696106024369840e+00 -9.8847492594673341e+00 -8.4351485269365991e+00 -3.4815504396923176e+00 2.0318425367749775e+00 5.4688409099903446e+00 6.8155210844128202e+00 7.5289034993747936e+00 8.2470533843717533e+00 8.5005207895890198e+00 8.1730893089208134e+00 8.1217939375154362e+00 9.2472099093868128e+00 1.1682275429968531e+01 1.4843432519914167e+01 1.7625325319637877e+01 1.8827596749646286e+01 1.8262944365019820e+01 1.7257770010849043e+01 1.7139858491940043e+01 1.7319082568288064e+01 1.5832240015066093e+01 1.1889468934637401e+01 7.0260166885118247e+00 3.4084227701886709e+00 1.5903858359584313e+00 6.4628466394257855e-01 2.9208281149091886e-01 1.3680522046018677e+00 3.7148191371537109e+00 5.3471095100773640e+00 4.5999975157196396e+00 1.8618036691603201e+00 -1.1675467460557312e+00 -3.2014095470489683e+00 -4.1751621005792883e+00 -4.4441528031142692e+00 -3.7587484122597536e+00 -2.0282422842477690e+00 -4.8339177039881087e-01 -2.6711860399748077e-01 -3.8790549777032224e-01 8.5572500658487782e-01 3.0975713952792994e+00 4.3374149891908393e+00 3.2920941855788466e+00 3.9295487513328070e-01 -2.4783937608256150e+00 -3.2055601186298843e+00 -1.7160184560799154e+00 -6.3702741131976026e-01 -2.2383884758776662e+00 -5.5570116369254112e+00 -8.2084983887639105e+00 -9.5917739048230395e+00 -1.0331039070410487e+01 -1.0417944274770905e+01 -9.6648501662164410e+00 -8.3293893029282344e+00 -6.5549338022834966e+00 -4.3174230553074713e+00 + -1.9521686822118689e+01 -2.0828890952194818e+01 -2.1026487257169343e+01 -2.0644194340000958e+01 -2.0356661177644074e+01 -2.0486033910028034e+01 -2.0592315338305198e+01 -1.9336484142295909e+01 -1.5938473018577051e+01 -1.1921626681007268e+01 -9.9587671774048481e+00 -1.0450212320468967e+01 -1.0318268307692627e+01 -5.6239283444522234e+00 5.5321151308175374e+00 2.2417493712423962e+01 4.2078909484239603e+01 5.9973160754273493e+01 7.1702694488382562e+01 7.5072713982579657e+01 7.0924560394362075e+01 6.2663339872630232e+01 5.4356911122207862e+01 4.7989283986803173e+01 4.2765855626087045e+01 3.7486699590135927e+01 3.2734101027576685e+01 2.9993606241210717e+01 2.9069222310304095e+01 2.7645584279325117e+01 2.3767330134444784e+01 1.7501891196670105e+01 1.0097030989509490e+01 3.3105933448286935e+00 -4.4358692407392453e-01 4.2309373559202790e-01 4.6272362980750925e+00 9.2452802627883770e+00 1.2881829396441868e+01 1.6139200491488932e+01 1.9696338650752981e+01 2.2945254570047432e+01 2.4052713164028159e+01 2.1836262493236529e+01 1.7755371912217502e+01 1.4544324719156007e+01 1.3076166058655325e+01 1.2023365019266210e+01 9.9330468323536198e+00 6.5642773658552329e+00 2.6211689892425389e+00 -1.2463040312411424e+00 -4.6931152521918520e+00 -7.0610915480926959e+00 -7.8896098442295468e+00 -8.1377876309837323e+00 -9.2879431013150882e+00 -1.1118321847684978e+01 -1.1512230767538767e+01 -8.6570795004612968e+00 -3.0565564637606264e+00 2.6614202613936517e+00 5.7492933678780842e+00 5.1870636768943843e+00 + -1.2768528401242900e+01 -1.7338863074172309e+01 -2.0237569092156448e+01 -2.0576492513490894e+01 -1.9431123286614632e+01 -1.8641772316619317e+01 -1.8943170420368556e+01 -1.9167043655793719e+01 -1.7528873877656416e+01 -1.3999909814005159e+01 -1.0805366953545310e+01 -9.6142597372311247e+00 -8.9149462386606366e+00 -5.4471587606889269e+00 2.2906924892770721e+00 1.2948039993449356e+01 2.3895472387004183e+01 3.2785154492570712e+01 3.8224124421928316e+01 3.9996932598102731e+01 3.8917325110890907e+01 3.6404969952831500e+01 3.3603549356134771e+01 3.0482131529017348e+01 2.6418551514199979e+01 2.1753968035179085e+01 1.7865996396778279e+01 1.5605745867809237e+01 1.4445507979298107e+01 1.3294236667868901e+01 1.1519440091973062e+01 9.2364945825762419e+00 7.2532314388618229e+00 6.2602214003637382e+00 5.4987881290564387e+00 3.2320466512581985e+00 -9.0506248164303926e-01 -4.9456160363826012e+00 -6.3608335770462485e+00 -4.2734547891318062e+00 4.6309862256935785e-01 6.7021945441538548e+00 1.3111777285636606e+01 1.7514480785911530e+01 1.8248810290360364e+01 1.5766174142459235e+01 1.1757377659322158e+01 7.9733313460406592e+00 6.0274343310982506e+00 6.1770944745629279e+00 6.2936748428494491e+00 4.0368463533344734e+00 -4.2685170771143977e-02 -3.1865409383399417e+00 -3.8901205566515773e+00 -2.9576974863775090e+00 -1.4738094422502874e+00 4.0118819835881747e-01 2.6525695143418675e+00 4.6014650691595245e+00 5.1565376735843209e+00 4.0636905672697523e+00 2.6453975037372648e+00 2.3284476158012066e+00 + -1.3007839852110759e+01 -1.1025538159980380e+01 -1.0686371074924564e+01 -1.2744092667984233e+01 -1.5642265829843563e+01 -1.7140214084438682e+01 -1.6617034625505930e+01 -1.5300909110451405e+01 -1.4577809836556332e+01 -1.4955079002970214e+01 -1.5654258710135920e+01 -1.4407648124599390e+01 -9.0954740513659118e+00 -6.2982400317265430e-02 1.0332127909943164e+01 2.0193720207165487e+01 2.8972473785793127e+01 3.6317935037933722e+01 4.1210257687869564e+01 4.2631382536124164e+01 4.0972150948544574e+01 3.8156297398596216e+01 3.5717588235143509e+01 3.3207917004761363e+01 2.9304640994990272e+01 2.4112801189469973e+01 1.9353557646954098e+01 1.6308754098888908e+01 1.4516954476871140e+01 1.2392183920492100e+01 8.3965380716467521e+00 2.2326430594848903e+00 -4.3996278411324354e+00 -8.8062298459598374e+00 -9.2226405800100828e+00 -5.6733723455263254e+00 3.7975785593939992e-02 5.0496912837583618e+00 7.5061362215685907e+00 8.1297047935707027e+00 8.9880765155299258e+00 1.0960262021995606e+01 1.3066019970002715e+01 1.3738034632375317e+01 1.1896051602413694e+01 7.5099861905659733e+00 2.1853151873444245e+00 -1.4107524690116966e+00 -1.7374936024760426e+00 4.7474258642688838e-02 1.2777176662851293e+00 5.9151627918367655e-01 -9.4200465373474518e-01 -1.3835697125582160e+00 -1.1439767142326973e-02 2.0431818226269547e+00 2.8931387751496276e+00 1.7217225031925740e+00 -3.6057387583206468e-01 -1.4188595163897433e+00 -5.3401986141825453e-01 1.7331384545428845e+00 4.1028021413802733e+00 5.1530807234519207e+00 + -5.9423907462970762e+00 -6.6393370296983978e+00 -7.7903948031970467e+00 -9.0887616470108981e+00 -9.0730958856624682e+00 -7.3051960638397269e+00 -5.1092125692791504e+00 -4.1499019811620803e+00 -5.1825095304735012e+00 -7.4650822660744725e+00 -8.7858341576439987e+00 -6.9165862119378128e+00 -1.1152780425762092e+00 7.4456360751231196e+00 1.5504355240213883e+01 1.9891995742575336e+01 2.1880531164099967e+01 2.5705009499507383e+01 3.1274011045582068e+01 3.4010306181075244e+01 3.2770325333513270e+01 3.0948804502035770e+01 2.9539194246932787e+01 2.6197352223405726e+01 2.0544443634266784e+01 1.4590436095771173e+01 9.4132762585289544e+00 5.0726802461989733e+00 1.8845604581530462e+00 -2.0830751870538844e-01 -2.0248221543626945e+00 -3.9513021238798549e+00 -5.3937857883630116e+00 -5.7622214722998990e+00 -5.0964393510051575e+00 -3.6685468807689410e+00 -1.9925824037801600e+00 -1.3116773229001528e+00 -2.6369224209795075e+00 -4.8818299037998507e+00 -5.7575190263162872e+00 -4.9151597170028811e+00 -4.2580797535905948e+00 -5.3162000432783687e+00 -8.0260447395650854e+00 -1.1124603514599908e+01 -1.2372738496954803e+01 -9.9327640511974646e+00 -4.8837906274471230e+00 -8.7396451836104794e-01 -1.7270469647056230e-01 -1.5776571699924549e+00 -2.7982572746004415e+00 -3.1062409337303580e+00 -3.0845221509902201e+00 -3.0432684148630136e+00 -2.5011943998532136e+00 -1.1481969001182208e+00 3.2261647132979526e-01 7.8134574976169557e-01 -1.2641763242828769e-01 -1.4193228153058111e+00 -1.7047633076684265e+00 -8.4627935364228413e-01 + -3.3511583497758619e+00 -5.1854891048629845e+00 -8.2045548190539002e+00 -1.1321927344458468e+01 -1.3232156516018623e+01 -1.2891455905593750e+01 -1.0308983110741357e+01 -7.2770589395120400e+00 -6.3230641518260899e+00 -7.7668924711145673e+00 -8.5533116261484974e+00 -5.1754472720840532e+00 2.5464912688278725e+00 1.1150146477409665e+01 1.6749765390457195e+01 1.8459480819125350e+01 1.8853443625394785e+01 2.0968383227964456e+01 2.4745356436846293e+01 2.6900729147509413e+01 2.4586028405507879e+01 1.8423893823872788e+01 1.1687464461034605e+01 7.4694246602111161e+00 6.5063740610776568e+00 6.6399254866014452e+00 4.8727847407542608e+00 6.2948434274342469e-01 -3.8180925201354374e+00 -5.9745650071051903e+00 -5.0248037653094384e+00 -1.7240345099049890e+00 2.4707226972860665e+00 6.1789893211512830e+00 8.4606300098802265e+00 9.1837056122684562e+00 9.4175247215558571e+00 1.0361111194478536e+01 1.1355825197106681e+01 1.0443694556801294e+01 7.3275805488596522e+00 4.0729400267880624e+00 2.7849489919865249e+00 4.0449011920910980e+00 6.8903062423979726e+00 8.8579270913084418e+00 7.2409153007186635e+00 1.9815538314749910e+00 -3.4963983686766915e+00 -5.7025552070066450e+00 -4.6316183670653945e+00 -2.8570944089319230e+00 -2.1945685959883381e+00 -2.7295179820568904e+00 -4.1440094951781976e+00 -6.1739578627827907e+00 -7.9441831061071788e+00 -8.3008782660488869e+00 -7.1697557563545278e+00 -5.6642787357607363e+00 -4.5049090776747240e+00 -3.1919541675350729e+00 -9.9748944534117390e-01 1.8822644573222371e+00 + 1.6022978144703672e+00 -1.4311786252489094e+00 -4.9990745496272506e+00 -7.1229734861916816e+00 -6.0298488474363783e+00 -1.8843154305695582e+00 2.8057312397445893e+00 5.3045500670547181e+00 5.2217607939007324e+00 4.6015143428315151e+00 5.3985890315998715e+00 7.6627280386075034e+00 1.0581969356003244e+01 1.3680704869130523e+01 1.6133038629367640e+01 1.6836134617835810e+01 1.5985757935422852e+01 1.5071121283266066e+01 1.5015214850097710e+01 1.5311877473734373e+01 1.4952636894507775e+01 1.3509895518873545e+01 1.1287529167684998e+01 8.8944719667341001e+00 6.8056307961237392e+00 5.1599082109694479e+00 3.9096368310365088e+00 3.0884796567205659e+00 2.8027781380901557e+00 2.7427378963657629e+00 1.8080112317158328e+00 -7.2093739358622477e-01 -3.2256888875692162e+00 -2.5485135905025946e+00 2.1938957914977939e+00 7.8096722519713868e+00 1.0108587362109473e+01 8.0119639048454889e+00 3.7875674256494771e+00 -1.7909623605085379e-01 -3.6408942410859053e+00 -7.2043954289116119e+00 -1.0036996806105844e+01 -1.0480191286127010e+01 -8.6104715538045582e+00 -6.5737607267403781e+00 -6.4789888225111065e+00 -8.9832745466307422e+00 -1.3098938079216277e+01 -1.6488189562785699e+01 -1.7072074337181476e+01 -1.4675862164708811e+01 -1.0393673234405423e+01 -5.7706460525680994e+00 -3.2783034075657795e+00 -4.8699351422351356e+00 -9.0537498769047904e+00 -1.1681066334269010e+01 -1.0244235531230570e+01 -5.9719523689580347e+00 -1.9644630163354910e+00 -6.8837039873644690e-01 -2.7600482002062674e+00 -6.6765535392302047e+00 + -2.6793902875688205e+00 4.3700049391893547e-01 5.2207839165959946e-01 -2.5289383815408999e+00 -6.4357928708025778e+00 -8.6801895727348093e+00 -8.6519420551714941e+00 -7.5157946794804973e+00 -6.9229481363763350e+00 -7.7994153444953156e+00 -9.3675453247907754e+00 -1.0006058503692298e+01 -9.5066143246051311e+00 -9.1193248923592503e+00 -9.0487355120456403e+00 -6.9972012375818347e+00 -1.6948530860574240e-01 1.0752688787226244e+01 2.0728963668504598e+01 2.4824092328193505e+01 2.2702194691381209e+01 1.8103111981612606e+01 1.5186220180215653e+01 1.5435411462559298e+01 1.6851176785915300e+01 1.6256911281706046e+01 1.2853830842714240e+01 8.7992906000849906e+00 6.3848237032232804e+00 6.1798137367968202e+00 7.8154545356284144e+00 1.0467840599577846e+01 1.2480989360982822e+01 1.2582337684046703e+01 1.1590145096205708e+01 1.1337803770903093e+01 1.2054473739609797e+01 1.2250402949861966e+01 1.0983877637654345e+01 8.6551288071765491e+00 5.8174031977868585e+00 2.9188587271482644e+00 8.3930412496566609e-01 2.3622649323786860e-01 7.1444423031664739e-01 1.2655853456754595e+00 1.3229336834759118e+00 1.3578720434847311e+00 2.1076821170226498e+00 3.0728270343869779e+00 2.7843524321077324e+00 9.0852714062149698e-01 -1.0017936404451253e+00 -1.3974751167516584e+00 -6.4988794902308711e-01 -5.0807738982896855e-01 -2.0685431131427148e+00 -4.6510643108399936e+00 -6.4865750445095642e+00 -6.4858500824649585e+00 -5.1351447719989922e+00 -3.4051840546128562e+00 -1.7184951490663052e+00 -5.0407691071619776e-01 + -3.6981497476642828e+00 -3.0390710048817726e+00 -1.4989922553639727e+00 1.0284003810800462e+00 3.6622719317496890e+00 4.7174258831055269e+00 3.5121017080473034e+00 2.1663525068616294e+00 3.8933497703894613e+00 8.9756839635313099e+00 1.4369540972251285e+01 1.7517537513748934e+01 1.8367043951422612e+01 1.7487527428352760e+01 1.4910602011410518e+01 1.1788546361447585e+01 1.0695666486764603e+01 1.2768873027185609e+01 1.6000928788239836e+01 1.7360605935279679e+01 1.5930200170467559e+01 1.3260280784655849e+01 1.0849461771852765e+01 8.4797732256321510e+00 5.3784098602866708e+00 1.7165101724011995e+00 -1.4089618767998704e+00 -2.7952639837436752e+00 -2.2276522503149407e+00 -9.5316276326730420e-01 -5.3299313777074875e-01 -1.5019739358173170e+00 -3.5515201972803432e+00 -6.3187612986855708e+00 -9.3137337609793054e+00 -1.1318424950944941e+01 -1.0816559293228659e+01 -7.7508782461822854e+00 -4.2600195254335116e+00 -2.8712335676010809e+00 -4.2724290569942225e+00 -6.8899881431600578e+00 -8.1742447353742449e+00 -6.8082430068650392e+00 -3.9299615464428408e+00 -1.4896688083453651e+00 -4.1155002553450060e-02 6.2487647569787685e-01 -2.9020287195242966e-01 -3.8853316012394776e+00 -9.2125322820605025e+00 -1.3050926297558480e+01 -1.2737108773295276e+01 -8.7818443279864002e+00 -4.3127261410662650e+00 -2.3302722768023965e+00 -3.5325752167188584e+00 -6.2805892811261916e+00 -8.5016614679139195e+00 -9.4759820546177966e+00 -9.7204496390574704e+00 -9.7546714698047463e+00 -9.5752380341344967e+00 -9.3036539221877597e+00 + -1.7414985024478632e+01 -1.7710392269365567e+01 -1.8830345987045778e+01 -2.0905808395741552e+01 -2.3089781413981267e+01 -2.3899680760146289e+01 -2.2409641713666709e+01 -1.8930485800816182e+01 -1.4601034805586801e+01 -1.0520204067883871e+01 -7.1082264501151942e+00 -4.1771499408845667e+00 -1.2985932549142798e+00 2.5888198678848857e+00 9.5645053119594277e+00 2.1215332248500019e+01 3.6152478620496041e+01 5.0021402817940384e+01 5.8774208038896376e+01 6.1292360127806575e+01 5.8550762166546200e+01 5.1882091757242364e+01 4.2968986888033839e+01 3.3918424522864342e+01 2.6125731850668569e+01 1.9638134139516286e+01 1.3840450777647449e+01 8.3996525004490294e+00 3.8021795817073860e+00 1.1190628236490561e+00 7.5962516407305214e-01 1.4058807055741134e+00 8.2163118862213358e-01 -2.0188943881722592e+00 -6.0499550558624495e+00 -9.4755634193659208e+00 -1.1248638187248371e+01 -1.0876954767354995e+01 -7.8849814096440030e+00 -2.2277330312359691e+00 5.4138464272246747e+00 1.3796881454212489e+01 2.0984396320719696e+01 2.4893133374375935e+01 2.4942654855570076e+01 2.2326104353812248e+01 1.8202892776374178e+01 1.2706268587571685e+01 5.8859262076676249e+00 -1.1947559660524161e+00 -6.5689254319385437e+00 -8.7772879515580264e+00 -8.1899879887551386e+00 -6.5832072140625550e+00 -5.4688753261733805e+00 -4.9175403512819100e+00 -3.8699212831075047e+00 -1.6574438279084263e+00 8.5455765389835547e-01 2.0911729100665450e+00 1.8202474430112003e+00 1.6089416985684442e+00 2.9743808071641795e+00 5.8948563383564005e+00 + -3.3338514474187440e+00 -4.1130389374748058e+00 -5.1547704706642552e+00 -5.4193898987702704e+00 -4.0201652371671948e+00 -1.8076212696539353e+00 -8.9324938220319039e-01 -1.9868709786288514e+00 -3.3105995821020633e+00 -2.6515298234057667e+00 5.1903435078819071e-01 4.9231535819829615e+00 8.5469168923530585e+00 1.0071331008104004e+01 9.9283440319809362e+00 9.9281856288240444e+00 1.1689028944723646e+01 1.5180332000597723e+01 1.8495089144282854e+01 1.9468749633612084e+01 1.7774035163293483e+01 1.4795935112020807e+01 1.1632060118366278e+01 8.3107388002809923e+00 4.7969367455220766e+00 1.5228719476515731e+00 -1.2245839851149136e+00 -3.6724142837818849e+00 -6.1258400070447641e+00 -8.3043113945111795e+00 -9.2382296924864278e+00 -8.2818369343941640e+00 -6.3024540208842872e+00 -5.1153884354297716e+00 -5.4012598664097871e+00 -5.9086039260782988e+00 -5.2469498739198066e+00 -3.8701397078727497e+00 -3.4699836914403539e+00 -4.6999263838482115e+00 -6.4704904874754252e+00 -7.1253409419073517e+00 -5.1911313857217234e+00 -5.8248621615308416e-02 6.3735411041921815e+00 9.9842526040059774e+00 8.0901302020184040e+00 2.0765046689514737e+00 -4.0814624682782448e+00 -6.9335386550902367e+00 -5.3396830806898752e+00 -6.1569207664081205e-01 4.2371402928715165e+00 6.1453027828107594e+00 4.0729416305299182e+00 -5.6892224917549727e-01 -5.7266782214867291e+00 -9.4817569691134302e+00 -9.4466837493039364e+00 -4.4716815460577326e+00 2.5480231258059796e+00 6.3862725120871939e+00 4.7626827206150235e+00 2.7250354238558483e-01 + -6.6519531318265823e+00 -5.1307688760840637e+00 -1.5990450796308475e+00 3.2882196698155921e+00 7.5434887899979923e+00 9.8601744151731392e+00 1.0431338476778441e+01 1.0132207096708374e+01 9.8633193310657052e+00 1.0117849589142009e+01 1.0556320935364319e+01 1.0529237565608828e+01 1.0064399849178292e+01 9.8902096970315743e+00 1.0802538876610429e+01 1.3065613777540369e+01 1.5904751019353327e+01 1.7996878960824926e+01 1.8887262226773334e+01 1.9137392198836288e+01 1.8914272666242024e+01 1.7510508054314936e+01 1.4350155185280446e+01 9.5073777371400769e+00 3.7350791719348706e+00 -9.6044902572999469e-01 -2.1523757668680674e+00 1.2726446256922297e-01 2.7975425794223376e+00 3.1310049165812357e+00 1.4400885075259127e+00 -9.4699453877260531e-03 5.2252907539724913e-01 2.9730000242572210e+00 5.8714424054021102e+00 7.9092364851498598e+00 8.7344188791480644e+00 7.9030314193993458e+00 4.5624392185691374e+00 -5.8583551307959691e-01 -4.3527827486539321e+00 -3.8542689679266942e+00 -3.3407572539104535e-02 3.0190256438646816e+00 2.5671043830395353e+00 -2.7379787916712028e-01 -2.8159393124422332e+00 -3.9496120934673513e+00 -4.3869096765280995e+00 -4.8326892938827388e+00 -4.8327167767960031e+00 -3.6048592531173460e+00 -1.4049856452797012e+00 6.1244691414035868e-01 1.4045344432842721e+00 4.0378165896259843e-01 -2.2594911404427971e+00 -5.2758916592334257e+00 -6.6711436023807975e+00 -5.4988956630405870e+00 -2.7595297745975564e+00 -4.3083978002099643e-01 1.2310669289941012e-02 -1.8074724020788719e+00 + -2.5417468582312877e+00 -1.9298735695592950e-01 1.4524591906329190e+00 1.4111623962585536e+00 -3.3194621049731687e-01 -2.5702905387021753e+00 -3.3145376224302021e+00 -1.6531002526600589e+00 1.5085470936791965e+00 4.9384825610490894e+00 8.1158180601966112e+00 1.0592317046651424e+01 1.1720352844683582e+01 1.1731547138555802e+01 1.1880590058094510e+01 1.2779413370052000e+01 1.4075646883094780e+01 1.5918248275430194e+01 1.8326489365386571e+01 1.9316206123129387e+01 1.6690629155310674e+01 1.1661681239753394e+01 8.0053968635282153e+00 7.5533291045585287e+00 8.4805002524471416e+00 7.9431009917907147e+00 4.7893096294793169e+00 -2.9205326061448478e-01 -5.9299905729305458e+00 -1.0889025474342041e+01 -1.4253802911511571e+01 -1.5424425634403272e+01 -1.4219613607718578e+01 -1.0988129270893715e+01 -6.7271231240914489e+00 -2.8803629271713325e+00 -5.3221218228894118e-01 -1.4819897860306691e-01 -2.2309622192950278e+00 -6.7555441266626337e+00 -1.1288796495269558e+01 -1.1879369455774969e+01 -7.6695223775198915e+00 -2.7345250210875287e+00 -1.2464522929563033e+00 -2.5982520939275595e+00 -3.2907753012277650e+00 -2.0946248103194671e+00 -1.2670396502483379e+00 -2.9734875330694628e+00 -6.1944576306095183e+00 -8.0259110770747917e+00 -7.0406747672630017e+00 -4.3818830819913117e+00 -2.2337026899490038e+00 -1.7757039249321303e+00 -2.1690903031406541e+00 -1.7112483834128271e+00 -2.2443726961269805e-01 7.8312271984712389e-01 7.2340304513709708e-01 9.8143716696324113e-01 2.5456848227724063e+00 4.1446278797649949e+00 + 5.9509298374097952e+00 5.0678810528340978e+00 4.6985100256137695e+00 5.6806474798139019e+00 7.1270807068985986e+00 7.3896907513926973e+00 6.1956015245970972e+00 5.5852197604153861e+00 7.9143806050507850e+00 1.2903336221013145e+01 1.7478615210727796e+01 1.8733526717119613e+01 1.6557524998442712e+01 1.3237833414525580e+01 1.1194587213855545e+01 1.1742137197718762e+01 1.5292487123411739e+01 2.1089872768798958e+01 2.6615090233197662e+01 2.9112067948790987e+01 2.8381112807191901e+01 2.6745309556602031e+01 2.6230622139582557e+01 2.6873626067912664e+01 2.7379954661291766e+01 2.6724918542001461e+01 2.5352283105591539e+01 2.4558243711441179e+01 2.4278527981980282e+01 2.2429473534878777e+01 1.7501264178786681e+01 1.0987541340332779e+01 5.6451288588220212e+00 1.5426875751835269e+00 -4.2209755312065607e+00 -1.3196792287091672e+01 -2.2788705236686901e+01 -2.9082273423343587e+01 -3.1121693514905303e+01 -3.0849786691440926e+01 -2.9901975701231901e+01 -2.8187902353798506e+01 -2.4897002861955549e+01 -1.9629404309588349e+01 -1.3381778061564034e+01 -8.8077830839680740e+00 -8.0332756560395637e+00 -1.0120926859300432e+01 -1.2004163217689651e+01 -1.1842485990658060e+01 -1.0575962516702482e+01 -1.0149295488895902e+01 -1.0971618377841839e+01 -1.1645676947992518e+01 -1.0824020132311130e+01 -8.7473035146007145e+00 -6.7816366706045397e+00 -5.8695256679524599e+00 -6.1984801392743734e+00 -7.8537905857434129e+00 -1.0416960930281348e+01 -1.2429989647191839e+01 -1.2650740393840184e+01 -1.1521198276477271e+01 + ] ) - check("sparse.mat", result) - - matfile = matopen("partial.mat") - var1 = read(matfile, "var1") - @assert var1[28, 33] == 5 - var2 = read(matfile, "var2") - @assert var2[27, 90] == 10 - close(matfile) - + check(joinpath(dirname(@__FILE__), "big_endian.mat"), result) end -result = Dict( - "index" => [8.8604784000000000e+04 9.8707212000000000e+04 1.0394035200000000e+05 1.1429712000000000e+05 1.5474923999999999e+05 1.5475435200000001e+05 1.5501074400000001e+05 1.5505315200000000e+05 1.5505718400000001e+05 1.5506168400000001e+05 1.5506524799999999e+05 5.4945741599999997e+05 5.6345896799999999e+05 5.9956981200000003e+05 7.0691623199999996e+05 7.9063657200000004e+05 8.4311938800000004e+05 9.2225131200000003e+05 1.1248994160000000e+06 1.2508148520000000e+06 1.4164141320000000e+06 1.4275988280000000e+06 1.4744331000000001e+06 1.4982212879999999e+06 1.5549058440000000e+06 1.5870300840000000e+06 1.6192005120000001e+06 1.6766071560000000e+06 1.9386816839999999e+06 1.9969427879999999e+06 2.0021861880000001e+06 2.3272494120000000e+06 2.5309351080000000e+06 2.6743788720000000e+06], - "spikes" => [ - -3.9146236245031032e+00 -6.7657651330021364e+00 -1.0780027188484372e+01 -1.4345619557780790e+01 -1.5488781013877338e+01 -1.3241531877846004e+01 -8.6339302778751907e+00 -4.1571900578409995e+00 -1.4845719040296610e+00 2.3147400250828232e-01 2.8917910181412778e+00 6.4067867244186800e+00 8.3368575385567603e+00 7.0732985406218223e+00 4.4095174940268036e+00 3.8495932342509342e+00 7.0605464919276546e+00 1.2892731012948772e+01 1.8593404980539656e+01 2.1332908128411184e+01 2.0142332517120792e+01 1.6740473413471157e+01 1.3650330377340575e+01 1.1913871749214691e+01 1.0804794411826084e+01 8.8366401987297127e+00 5.1092331749990514e+00 5.1218216653980408e-01 -2.9327647633922682e+00 -4.4870896208146753e+00 -5.0598199463728655e+00 -4.8330524336350118e+00 -2.8556000012645000e+00 2.9794817723619027e-01 1.8265416505730325e+00 -8.6155940979615875e-02 -3.9623352473810947e+00 -6.9070013227561047e+00 -7.3941131196997647e+00 -5.7411207637544166e+00 -3.2366812420300106e+00 -1.1460492068000723e+00 1.2381260731009580e-01 1.0930145325605314e+00 2.1927876983540933e+00 2.6570284430776856e+00 1.3381366125210661e+00 -1.2539624260623763e+00 -3.3642620416729994e+00 -4.1849749207505456e+00 -3.8760400918509301e+00 -2.6869552030388291e+00 -1.6718246062697015e+00 -2.3709942853677934e+00 -4.6623835517993664e+00 -6.6575320887201714e+00 -6.9891263747717174e+00 -5.7017039068420186e+00 -3.4759011423153079e+00 -1.7092931352045238e+00 -2.3854494206243695e+00 -5.8068462168496913e+00 -9.1001745572212531e+00 -8.8479323560036516e+00 - -5.0745139212223540e+00 -9.6338046242625506e+00 -1.3472006614220170e+01 -1.5245910823426385e+01 -1.5914718584705716e+01 -1.6397777086548157e+01 -1.5688652912166024e+01 -1.2863641020969972e+01 -9.5044110719151487e+00 -8.3463890698794305e+00 -1.0177024989874276e+01 -1.3445743563344164e+01 -1.6692449999817043e+01 -1.9680052373752346e+01 -2.0121942799899024e+01 -8.3700572911264981e+00 3.0032951243048100e+01 9.4582543108269547e+01 1.5377989075611117e+02 1.7130866838277802e+02 1.4748486763605970e+02 1.0947277486760775e+02 7.2684596239613015e+01 3.8193102601464858e+01 1.0540246012312117e+01 -3.8091287155762972e+00 -5.9643997244651556e+00 -3.0829122045048947e+00 -9.3969579584981044e-01 -4.1011214330806744e-01 1.1485354724913588e-01 8.3209939369519947e-01 1.7132389429717065e-03 -3.6057351605809611e+00 -8.8203434292989460e+00 -1.2692989286632969e+01 -1.3074091545751195e+01 -1.0580269175201330e+01 -7.7262110976707312e+00 -6.1406117115548504e+00 -5.0818589866084025e+00 -3.3150901769832091e+00 -1.7731765969545847e+00 -2.5627661695922046e+00 -5.6404917366725886e+00 -8.3842617579066747e+00 -8.5769172149145199e+00 -6.8248629640086911e+00 -5.7747794329330571e+00 -7.1063437611930578e+00 -9.7162880960954556e+00 -1.1170428842658552e+01 -1.0378119546974839e+01 -8.3419883744685741e+00 -6.9209209230224635e+00 -7.4145555946251820e+00 -9.6713314325255784e+00 -1.2062946758371979e+01 -1.2965017154593347e+01 -1.2408773058757985e+01 -1.1624355346583785e+01 -1.1127849526328186e+01 -1.0048811168388408e+01 -7.6327226475166228e+00 - -3.6172800577681850e+00 -3.0496107525626650e+00 -4.0450961689646379e+00 -4.9841853952779340e+00 -4.3137243411079638e+00 -2.4211699563520748e+00 -9.3469136675988018e-01 -9.3834475312754062e-01 -2.3043613016166664e+00 -3.9905808684096256e+00 -4.6842071022425413e+00 -3.8310453992051836e+00 -2.8818230979207713e+00 -5.2038837297130556e+00 -1.1725055393804819e+01 -1.3208091586845086e+01 9.2482123494256854e+00 6.3181823866266974e+01 1.2177486062412244e+02 1.4070171740815530e+02 1.0609880427064739e+02 4.8491974791356185e+01 5.8467389218168400e+00 -9.6802170255950291e+00 -8.6605173391900614e+00 -5.1555616199626577e+00 -5.4237382663488169e+00 -8.2575373220146044e+00 -1.0156241701964554e+01 -9.1574620977201118e+00 -5.6665700132115431e+00 -1.5504659563007614e+00 1.1334383183944798e+00 1.2640431009400714e+00 -6.6794751018025034e-01 -2.9543901520038176e+00 -4.1963329340429310e+00 -4.3642482086638417e+00 -4.0042244694146101e+00 -3.1394295409389494e+00 -1.8179756080601912e+00 -9.3024527470805374e-01 -1.4736656790371958e+00 -3.4645773489091209e+00 -5.8588062538620047e+00 -7.0734948204842896e+00 -6.0481893429370261e+00 -3.3344016972431088e+00 -4.8650896484328832e-01 1.6052150099271567e+00 2.9838901666777322e+00 3.7836445807114574e+00 3.9345735120816623e+00 3.1644840267225094e+00 1.0070686684300534e+00 -2.4713922778659478e+00 -5.9984216012494951e+00 -7.8885829262850233e+00 -7.5154376078825695e+00 -5.8326333185237758e+00 -4.5003931071444478e+00 -4.2328547992043450e+00 -4.1140676583157889e+00 -3.1460951038002301e+00 - -3.6781701228839154e+01 -3.7303084834807457e+01 -3.8884510028175633e+01 -4.1148035399962872e+01 -4.3316242853459642e+01 -4.4384384580854572e+01 -4.4790855343692897e+01 -4.7038755365884079e+01 -5.2501365276546458e+01 -5.6812084874163638e+01 -4.9570438054352273e+01 -2.3981805280442412e+01 1.1015352613446286e+01 3.7025927055247365e+01 4.6196840812364755e+01 4.5435239856395953e+01 4.4108776047185387e+01 4.5957015883875258e+01 4.9338962205718175e+01 5.0907715343642117e+01 4.9487314118838199e+01 4.6376644839165742e+01 4.2907993314061748e+01 3.9175485818078528e+01 3.4646364450142663e+01 2.9438295855526434e+01 2.5290001942034166e+01 2.4585557992414284e+01 2.7869881512937130e+01 3.2856199330415350e+01 3.6123557386279117e+01 3.5905020129260436e+01 3.3291843240497656e+01 3.0657151735220140e+01 2.9399978478943620e+01 2.9238216866099457e+01 2.8701738303043562e+01 2.6077460100630137e+01 2.0689205683518288e+01 1.3779281580459998e+01 8.1712538443203133e+00 6.6925061080176569e+00 1.0147005424138726e+01 1.6509827576474752e+01 2.2276297357800129e+01 2.4765444980789738e+01 2.3565808601878054e+01 2.0365852060539318e+01 1.7482427576467405e+01 1.5922922611993604e+01 1.4534460949429915e+01 1.1655908219543699e+01 7.5216074902940893e+00 3.9334852970087364e+00 1.7985868168036778e+00 3.9751100517608062e-01 -9.6689135481901367e-01 -1.8291316756437888e+00 -1.2108732556384698e+00 1.1422814255806824e+00 4.0827052431935931e+00 5.6696392298883893e+00 4.7889899662105178e+00 1.9874261259261794e+00 - -1.1686614851500112e+00 9.8124871505894706e-01 2.4682927864975457e+00 2.9222372863110442e+00 1.6794130795786746e+00 -6.8820557107116076e-01 -2.4491510090825823e+00 -2.5573541785944220e+00 -1.5657335653505560e+00 -6.2326635393824792e-01 7.5542615186640072e-03 1.2160520506156904e+00 3.7959025732449647e+00 7.1695274203178112e+00 9.8085862745466752e+00 1.1024667458560756e+01 1.1849030121520164e+01 1.3645183550697372e+01 1.6153045543872672e+01 1.7653460112285362e+01 1.6917192047304358e+01 1.4287778990065465e+01 1.1181854556421472e+01 9.2813981144484536e+00 9.6980182244576554e+00 1.1975656236851366e+01 1.4218362205978007e+01 1.4823189770395057e+01 1.3782281414778801e+01 1.2224714159620135e+01 1.1308817315730568e+01 1.1413207645545857e+01 1.1802812942398777e+01 1.1304006120772028e+01 9.4673823725009267e+00 6.7674104407290443e+00 3.9034391463718467e+00 1.0696510964445158e+00 -2.2265622627917740e+00 -6.1619053880194183e+00 -9.4895708918063448e+00 -1.0511848180773416e+01 -9.2016379963305752e+00 -7.3655072801953869e+00 -6.6664600174160604e+00 -7.2291846775841488e+00 -8.2107481715781105e+00 -9.1021811818757321e+00 -1.0311278259980540e+01 -1.2741511638678721e+01 -1.6437776102727412e+01 -1.9514979330779195e+01 -1.9610190285680936e+01 -1.6644297020966704e+01 -1.2800100439249650e+01 -9.9046719354109900e+00 -8.2923627062655303e+00 -7.8966764067275435e+00 -8.8257663696547084e+00 -1.0735616318501446e+01 -1.2599202496900540e+01 -1.3220431400502429e+01 -1.1920594647006027e+01 -9.3084512934042376e+00 - 1.1099942135772691e+01 1.0097197127580243e+01 1.1065906114342178e+01 1.3374123078938322e+01 1.6449533911253752e+01 2.0017355902687680e+01 2.3638247141687746e+01 2.6292880854006093e+01 2.6783333166190332e+01 2.5183008648660202e+01 2.3296988595385344e+01 2.2810464758877668e+01 2.3624918756652875e+01 2.4607374587222779e+01 2.5393398749862719e+01 2.6769469521678889e+01 2.9281995275221178e+01 3.2213436799025317e+01 3.4285343581139983e+01 3.4785296465751173e+01 3.3883822298572078e+01 3.2386332385252516e+01 3.1231943637716320e+01 3.0932840342758819e+01 3.1246878043151604e+01 3.1160302181567783e+01 2.9550070933834700e+01 2.6540966708675843e+01 2.3785330200047358e+01 2.2611886801417548e+01 2.2531515536471172e+01 2.2340044415310590e+01 2.1822535590221452e+01 2.1399364443416083e+01 2.0755992097134378e+01 1.8948315967557182e+01 1.5769917402888659e+01 1.2205733819672014e+01 9.4099541981525228e+00 7.7046512181258402e+00 6.5719664417242711e+00 5.2343013227680553e+00 3.3389338216778657e+00 1.2573655827507051e+00 -5.0199990120897509e-01 -1.8987019795142805e+00 -2.9172592054133535e+00 -3.2152176994632509e+00 -2.9689210401896537e+00 -3.2720957493420673e+00 -4.8582690799070640e+00 -7.1594797906170751e+00 -9.1803890906391956e+00 -1.0495134027231670e+01 -1.1056911353417380e+01 -1.0726534970818260e+01 -9.3753223680626547e+00 -7.2907274381158134e+00 -5.1694262729300959e+00 -3.7312476583791785e+00 -3.6141234953544066e+00 -5.1662008322483448e+00 -7.7776079749577836e+00 -9.8010587078412321e+00 - -1.4685530727984641e+01 -1.0292328330297664e+01 -6.2040173116488546e+00 -2.3375015307800595e+00 1.3923917658511158e+00 5.0564769748901313e+00 8.6976966359851104e+00 1.2323245348083425e+01 1.5903913125353689e+01 1.9380777962659089e+01 2.2677898949762564e+01 2.5718454097742246e+01 2.8441073995486732e+01 3.0813082951470030e+01 3.2837981366611231e+01 3.4555661605993883e+01 3.6035311542317494e+01 3.7362431137561401e+01 3.8622571934083787e+01 3.9885068827368393e+01 4.1190035284914742e+01 4.2541236867236606e+01 4.3906275503545629e+01 4.5224046329587310e+01 4.6417965861852949e+01 4.7412309197387181e+01 4.8148368395523121e+01 4.8597180088665191e+01 4.8766260494684808e+01 4.8698991553936125e+01 4.8466780378761598e+01 4.8155568154983840e+01 4.7849401563031734e+01 4.7614370946271123e+01 4.7486145888939269e+01 4.7463615344086584e+01 4.7509911501785595e+01 4.7560611227134473e+01 4.7537464942025593e+01 4.7364890774312840e+01 4.6985915393397903e+01 4.6374354157541411e+01 4.5540779674504876e+01 4.4531077464674446e+01 4.3417878743397615e+01 4.2286593389902350e+01 4.1218852636871070e+01 4.0276692303289153e+01 3.9490658602725233e+01 3.8854229591844380e+01 3.8325674875439489e+01 3.7836979823323738e+01 3.7308039311521902e+01 3.6663265807716996e+01 3.5847270787923392e+01 3.4836464766174224e+01 3.3644242194420237e+01 3.2318708010392434e+01 3.0933403069436942e+01 2.9572894236760884e+01 2.8316128161485977e+01 2.7220897815058663e+01 2.6312547181929837e+01 2.5579187011419549e+01 - 1.5763956152315169e+01 1.6523243082892250e+01 1.7337427631570872e+01 1.8120936638484590e+01 1.8792445157811912e+01 1.9291006994381139e+01 1.9588077541817789e+01 1.9693009891213325e+01 1.9650918197573489e+01 1.9533337877188348e+01 1.9423561429962543e+01 1.9399599967723205e+01 1.9518198338118580e+01 1.9803119911795868e+01 2.0240058237539227e+01 2.0779199633821964e+01 2.1344921568275133e+01 2.1850676042659646e+01 2.2216063986224665e+01 2.2382665762479274e+01 2.2325443040524132e+01 2.2057418172501748e+01 2.1626689639781716e+01 2.1106384004760407e+01 2.0579565930487874e+01 2.0122142297473431e+01 1.9787200170220586e+01 1.9593930129327656e+01 1.9523364223698717e+01 1.9521786868823959e+01 1.9511132956806740e+01 1.9404280892123200e+01 1.9122163091585080e+01 1.8609252789360340e+01 1.7844318015586811e+01 1.6844290518050197e+01 1.5660474424296631e+01 1.4367827117720619e+01 1.3049394428891683e+01 1.1778975433290963e+01 1.0605636123380648e+01 9.5436591203415890e+00 8.5705174399266060e+00 7.6329046830511684e+00 6.6569045386579333e+00 5.5557424579780292e+00 4.2330232952205966e+00 2.5948387638090815e+00 6.0010139846456756e-01 -1.6403240489988979e+00 -3.7983903715983196e+00 -5.5143675763583140e+00 -7.0197817000212250e+00 -9.2991809829904906e+00 -1.2710108289977208e+01 -1.5773321560596763e+01 -1.6492611312664831e+01 -1.5291065260668367e+01 -1.5553348127486492e+01 -1.9596399167644933e+01 -2.5711586491024381e+01 -3.1294712703031326e+01 -3.6140274982880157e+01 -4.0736526948594026e+01 - -7.5455089032603496e+01 -8.8091850484650791e+01 -7.6894847494814442e+01 -5.1931618057453484e+01 -2.8833831644552824e+01 -6.8895988088626297e+00 2.1168348264522379e+01 4.6693994244353583e+01 5.3777387223823958e+01 4.4603130335345831e+01 3.3530654869558120e+01 2.3587826739136968e+01 8.4238787409792160e+00 -9.2318222698507917e+00 -1.4713136414898660e+01 1.9717165915666754e+00 3.1968641163889899e+01 5.7269930354848498e+01 7.0152688867705734e+01 7.3409072049238773e+01 7.0166235593951257e+01 6.1829204326045371e+01 4.6696134605544373e+01 1.8622054473547621e+01 -2.2829591964997910e+01 -6.2715671170700787e+01 -8.2653742502672856e+01 -8.1088328703707077e+01 -7.2961806812740548e+01 -7.3632394853923415e+01 -8.9841480609702060e+01 -1.1962228131535032e+02 -1.5066572610100792e+02 -1.6038121312642346e+02 -1.2981471355911390e+02 -6.3653163261418783e+01 9.3811209125033130e+00 5.8695741367623484e+01 7.0437047869761585e+01 4.9630793312944938e+01 1.6599415269056415e+01 -2.5402904227425278e+00 4.0559101821793178e+00 2.4429205550104435e+01 3.9021358713588448e+01 3.8520783430674051e+01 1.9952697815867261e+01 -2.1877257859243720e+01 -8.3907527060881065e+01 -1.4250639755111564e+02 -1.6619398657243511e+02 -1.3819218483975862e+02 -6.6785267226843359e+01 7.8497452778842174e+00 2.7663034892382331e+01 -1.9252281399357905e+01 -5.9646303432603780e+01 -1.8852103558667594e+01 6.7639555235119275e+01 9.6932460076734856e+01 4.1262889176984515e+01 -3.3610639804651925e+01 -7.8423083296187542e+01 -1.0839727633754131e+02 - 4.7736577112711117e+01 4.5700337234538978e+01 4.5322631494156710e+01 4.9171444949072850e+01 5.3757473474671947e+01 5.4141589236927501e+01 4.8790940853843097e+01 4.1857477273482019e+01 4.0754533608850998e+01 4.8608027316167508e+01 6.0100216178867996e+01 6.8180296544356494e+01 7.1421072557515430e+01 7.0807306032014466e+01 6.6514973934144194e+01 6.2945074970931898e+01 6.8196514720480167e+01 8.5012727237677268e+01 1.0720726826115141e+02 1.1957256746876999e+02 9.9138254538272335e+01 3.8069276174166163e+01 -2.7233524821672358e+01 -3.9334632563706222e+01 1.3886634524961096e+01 7.3584276961424322e+01 6.3628518198466388e+01 -2.9054572516934122e+01 -1.4209888641029568e+02 -2.0910522987863314e+02 -2.1415046392049794e+02 -1.7586701353513934e+02 -1.1844102599759782e+02 -6.1431963885049129e+01 -1.4806030025628601e+01 1.4027944301627443e+01 1.0652586257860358e+01 -3.0054086554043813e+01 -8.5072361204847368e+01 -1.2046643138190960e+02 -1.2106054250546094e+02 -9.5841527604366775e+01 -6.6055322458778861e+01 -4.4458770387691487e+01 -2.4336370991880798e+01 4.5840797349366902e-01 2.1040288319943066e+01 2.3611095638912182e+01 1.3051021255932143e+00 -2.8851154140515209e+01 -3.7619486391456242e+01 -2.9915658771811749e+01 -3.0708973922309450e+01 -3.1924567737368655e+01 -1.0202022943830535e+01 2.9706495601613049e+01 5.9067239546813376e+01 5.4702088800913174e+01 2.8343487643024787e+01 1.5951389347675942e+01 2.2837902575396686e+01 1.2908948645547047e+01 -3.3255358730535995e+01 -8.7746125812653432e+01 - -8.2377418542911354e+00 -3.4972931824407372e+00 -7.7569626244497876e+00 -1.5166206734460895e+01 -1.5445771078633909e+01 -5.2413228186791176e+00 8.4387175092257500e+00 1.5431757026108208e+01 1.2399978335869392e+01 7.4095099729504046e+00 1.0679579816928053e+01 1.8701188652942292e+01 1.7701213689196198e+01 4.8431711642004913e+00 -4.7779662604481938e+00 3.1723005655630221e+00 2.4531952072999687e+01 4.5698045473740862e+01 5.9257083049327122e+01 6.2627426268528140e+01 5.7411981420461260e+01 5.2570470576359099e+01 5.2813113343401156e+01 5.0453507704104304e+01 3.9691119012785791e+01 2.6528767918502940e+01 1.8741299694240368e+01 1.7724441294399568e+01 2.0821175647900695e+01 2.4417659444952996e+01 2.5870167533339281e+01 2.4983092349575369e+01 2.2998436116876093e+01 2.0558711472598521e+01 1.7502822279045098e+01 1.3739438787271743e+01 9.9370928813924273e+00 7.9696531198819107e+00 9.3237074864808100e+00 1.2496048137913172e+01 1.4202138161523894e+01 1.3776871923186704e+01 1.4057294551750237e+01 1.6919973545359703e+01 2.0190481439159552e+01 2.0371692873037446e+01 1.6567959095411027e+01 1.0520230640011906e+01 4.5794135735479564e+00 8.7710329733688541e-01 8.9182399282027713e-01 4.3463285075996909e+00 8.7315858121978973e+00 1.0903936631346470e+01 1.0288756426506817e+01 1.0049725699443105e+01 1.3054576528907717e+01 1.6854021523448839e+01 1.6041035800270304e+01 1.0367866836597134e+01 6.4265096090680434e+00 9.4949675891987368e+00 1.6836520945082036e+01 2.1325105933919993e+01 - -4.1679158148256432e+00 -5.2810404688221357e+00 -5.9038977834292563e+00 -6.2634044553555412e+00 -5.9749855839469195e+00 -4.3218694766614334e+00 -1.1788772851158797e+00 2.4374543533437842e+00 4.8050829080472326e+00 4.8908091608309068e+00 3.3699448624872437e+00 1.8764385857350361e+00 1.0658937699825455e+00 -2.7882253437288551e-01 -3.1356733669314134e+00 -4.1284337659893531e+00 4.9241012162347957e+00 2.8218865342091348e+01 5.5056325524296241e+01 6.5099843979883218e+01 4.9609447627592324e+01 2.1423693795580775e+01 2.5132863903596458e-01 -5.8604600478907667e+00 -2.3380828579082129e+00 1.9600991106694594e+00 2.5915747007003080e+00 2.0190047306038350e-01 -2.8577160575959448e+00 -5.2869354732703258e+00 -6.9099491413979388e+00 -7.7559496509908179e+00 -8.0339926236897785e+00 -8.1760789963289717e+00 -7.9859360445805283e+00 -6.3703713820186918e+00 -3.0321001946270005e+00 -2.8201117047924162e-02 -7.8351721876626979e-02 -3.2229219827610613e+00 -6.6022607253341592e+00 -7.6428337012652356e+00 -6.3520480710327867e+00 -4.5468067925800151e+00 -3.6792489626697655e+00 -3.7265752054412307e+00 -3.8104694479464527e+00 -3.1502606288908388e+00 -1.5302095841423975e+00 2.6517264914271876e-01 7.6568764912213050e-01 -5.6131323023144386e-01 -2.6412705089170849e+00 -4.2923899829303380e+00 -5.1769894068210993e+00 -4.9614565332821146e+00 -3.2859163281387294e+00 -1.1703534382204590e+00 -7.6910847698287388e-01 -2.6122681986842187e+00 -4.2742848318737003e+00 -3.0739009488926641e+00 6.8120601334287323e-01 3.9336266256502612e+00 - -4.6484775115089541e+00 -8.3581561181698660e+00 -1.0902142569692860e+01 -1.1511797760460409e+01 -9.8276834132977005e+00 -6.2404161549022161e+00 -1.8432175810231679e+00 1.9347492334298952e+00 4.2938226787082971e+00 5.6566425031547753e+00 6.9370739014200060e+00 8.7323889596425897e+00 1.1151007876295226e+01 1.3469254774199312e+01 1.4378076971481226e+01 1.3628707243729355e+01 1.2810890536991595e+01 1.3603571552449427e+01 1.5792969414511386e+01 1.7425203016009618e+01 1.6493276434801135e+01 1.2586445678344095e+01 7.3401463249477024e+00 3.2087842017129540e+00 1.5030100447830739e+00 1.3898465906994972e+00 8.8198090743093560e-01 -1.0493063115005812e+00 -3.7608545130075726e+00 -6.3949540211998350e+00 -9.0066585358720026e+00 -1.1745588278397154e+01 -1.3842632605375675e+01 -1.4123365855042982e+01 -1.2335802612795424e+01 -9.4739532170207532e+00 -6.3782197090918142e+00 -2.8832164731098446e+00 4.8447063694123349e-01 1.6128314119847449e+00 -4.1774621162461167e-01 -3.3629973208103769e+00 -4.5025431650456200e+00 -3.9290643942165806e+00 -3.7022642550448306e+00 -4.6991587651781321e+00 -5.6909794907882132e+00 -5.2906700399814426e+00 -3.7661436808309654e+00 -2.5302504318642383e+00 -2.0969878128246422e+00 -1.3265870686252779e+00 7.0961290345922357e-01 3.0385814819877943e+00 3.9548508946305954e+00 2.9954312845408939e+00 9.9988805713822959e-01 -9.2502312206375614e-01 -2.0384940259267319e+00 -2.2297243620823197e+00 -2.0769146009998161e+00 -2.3170471037592764e+00 -2.8506427004880215e+00 -2.6777747842245225e+00 - -7.7992155184377432e-01 8.4208602530413512e-01 2.4887283142722940e+00 2.4338293029048339e+00 8.3831805409641036e-01 -5.6099977991794492e-01 -7.1266715703072170e-01 -2.3092906084045711e-01 1.6955881483329160e-01 8.8504105274391720e-01 2.2423969436547071e+00 3.3863034262368776e+00 3.6616215908416012e+00 4.2752627336864073e+00 7.0581057720790223e+00 1.1758833658720096e+01 1.6047422019307188e+01 1.8405075233788665e+01 1.9427396781993370e+01 1.9879444229356857e+01 1.9348574654086402e+01 1.7629540788742673e+01 1.5792875449818970e+01 1.4872230427632566e+01 1.4471792816861928e+01 1.3426898347317799e+01 1.1319456367524939e+01 8.8575138135993203e+00 7.0846315948436089e+00 6.5127944804059057e+00 6.6781793515550252e+00 6.2758575263602134e+00 4.2213211609500574e+00 1.0187928511802555e+00 -1.3365602410322484e+00 -1.2247622425200726e+00 1.1481723220280773e+00 4.3321910406078903e+00 6.9648334152330520e+00 8.0154771868425438e+00 6.5719188712588483e+00 2.4474218319276826e+00 -3.0491523253799624e+00 -7.4154828913997903e+00 -8.5953677824666350e+00 -6.7813390775209372e+00 -4.2068257455106703e+00 -2.7309337921106196e+00 -2.0348235578019351e+00 -6.2767747718278888e-01 1.7379756336568186e+00 3.5393101620046572e+00 3.5444884355037840e+00 2.3746522750550279e+00 1.3500592448824880e+00 6.9976097822180139e-01 -1.5618921435797500e-01 -1.3162226599662721e+00 -2.4845798158016903e+00 -3.7797798352858396e+00 -5.2368125011457316e+00 -5.9486138818233414e+00 -5.1354371684134463e+00 -3.7528582973371494e+00 - -3.0447537175403006e+00 -1.7667805638958940e+00 -1.9778229750617011e+00 -2.2515604403315699e+00 -1.3449261643785633e+00 -1.0662626484421048e-01 -5.7657486030100791e-02 -7.4315041221388778e-01 -3.2337810363445102e-01 1.8577438758015754e+00 4.8425765941254983e+00 7.8123978114524020e+00 1.1105854493054094e+01 1.4841903991300153e+01 1.7468871344145363e+01 1.7574512951317725e+01 1.6451820522767886e+01 1.6628323973953727e+01 1.8301434258000356e+01 1.8931382166939795e+01 1.6217394592510296e+01 1.0559530299242045e+01 4.4576293834236687e+00 -1.1822470014800246e-02 -2.5727563770885062e+00 -3.7547505719161225e+00 -3.5081536657220189e+00 -1.4835065248806825e+00 1.8247576781703743e+00 4.8275703093820033e+00 5.7228574725963268e+00 3.8558888609578723e+00 4.0990146070366057e-01 -2.4574645653496310e+00 -3.1102243476353593e+00 -1.4474225309731716e+00 7.4127875680854416e-01 1.0383069562131020e+00 -1.0150232301722759e+00 -3.4198210424706135e+00 -4.3088303898183238e+00 -3.9165170232756070e+00 -3.6605958183355347e+00 -4.2469618414480417e+00 -5.2237494007048566e+00 -5.9420620236257076e+00 -6.1698135259889568e+00 -5.7089733374935268e+00 -4.0492707065696969e+00 -8.0849384208890562e-01 3.3216805308527921e+00 6.2990262694136261e+00 6.1812329879916819e+00 3.1266169114028388e+00 -4.8505368345761951e-01 -2.2625885070785667e+00 -2.0371523965480165e+00 -1.4033783831712108e+00 -1.2651726669536290e+00 -8.7131680693218405e-01 5.0106877171388586e-01 1.8406747295741790e+00 1.3085085083594870e+00 -1.5293730324086736e+00 - 2.3513445326289961e+00 1.7384880477187443e+00 2.2184897775735659e+00 5.2051223974837342e+00 1.0452816121087638e+01 1.5377084182445486e+01 1.7198975360259148e+01 1.6104713320124159e+01 1.4833420837246866e+01 1.5100670860303905e+01 1.6073099350257220e+01 1.6269109488250638e+01 1.5637757524959511e+01 1.5476499170789715e+01 1.6604024256326596e+01 1.8317676582762779e+01 1.9829075755286041e+01 2.1816151151864332e+01 2.5055347925633036e+01 2.7333907737059032e+01 2.2427805378565086e+01 3.7141556286936721e+00 -2.8648138173116212e+01 -6.3843957225821207e+01 -8.7089860614432553e+01 -9.1923311472937954e+01 -8.3938126428996938e+01 -7.2885847777427841e+01 -6.4297677965088411e+01 -5.7914366258401401e+01 -5.0470379597131561e+01 -3.9499120538780375e+01 -2.5751695307781368e+01 -1.1778187088014317e+01 1.0189820693630494e+00 1.1960524761686607e+01 1.8733303083800365e+01 1.8356414318508428e+01 1.0668647686854321e+01 -1.0075011878329772e+00 -1.2633619248121462e+01 -2.1404683668367543e+01 -2.5602377289037388e+01 -2.4560926814716566e+01 -1.8847733387633440e+01 -9.8404336731293878e+00 3.5966958558048301e-01 9.0662935627188972e+00 1.4335829915263089e+01 1.6036694595613060e+01 1.5184337823870287e+01 1.2469246394512929e+01 7.6677145828817306e+00 8.5165822752424969e-01 -5.8806346340252293e+00 -9.1084583818696743e+00 -7.2999534121027425e+00 -2.4813921667292815e+00 1.7215656085634032e+00 3.0197896060174911e+00 1.4579843578258131e+00 -1.6420102258369735e+00 -4.8732983588159042e+00 -6.9044980772313806e+00 - -7.1748224092297468e+00 -3.8936102456845245e+00 1.0743831294909865e+00 4.8816301487736782e+00 5.4189196440465226e+00 2.8421806913201926e+00 -7.8193972997551731e-01 -3.3953855854612938e+00 -3.9985877326731809e+00 -2.4284915455410818e+00 3.3095855413398589e-01 2.1860065858780535e+00 1.8280988855495470e+00 3.4048799490454618e-01 3.8790118108287441e-01 3.8556647578240639e+00 9.9718322097927121e+00 1.5912297294502212e+01 1.9511397698602284e+01 2.0651366980982488e+01 2.0134282655984631e+01 1.8560445836154724e+01 1.6287447146155721e+01 1.3561600277652611e+01 1.0795264078502715e+01 8.6030785401577266e+00 7.0022103875078896e+00 5.1888911547538754e+00 2.7145704050730384e+00 1.4143845469113936e-01 -1.9107456843838713e+00 -3.4559243419646326e+00 -4.2223452047916581e+00 -3.1130434517080205e+00 1.7183260785086762e-01 3.7869557639525033e+00 5.3380153379310666e+00 4.2352058165010487e+00 1.6573488592368961e+00 -8.5887188564406203e-01 -2.1681503131724709e+00 -2.1279908375294667e+00 -2.0792749208808359e+00 -3.3997834567720187e+00 -5.3580052019440032e+00 -5.9792374734377622e+00 -4.8403202446726707e+00 -3.3487890303481347e+00 -2.6600981462061033e+00 -2.7228934357253367e+00 -3.1308053901450172e+00 -3.8963449071942557e+00 -5.2729284759196471e+00 -7.2764195946828067e+00 -9.3283947520693822e+00 -1.0227824562298673e+01 -9.0739885142293417e+00 -6.5681575524140818e+00 -4.8038541028704778e+00 -5.3349275900632449e+00 -7.6025577687023391e+00 -9.3130362450711317e+00 -8.3152888687476345e+00 -4.2484255376825795e+00 - -1.2094820634316039e+00 -1.2706817164948352e+00 -1.2775018026388452e+00 -2.3357176552894954e-01 1.8928499829725349e+00 4.4677863928736414e+00 6.9513585826945681e+00 9.0204123867514259e+00 1.0482452392061710e+01 1.1455309752506835e+01 1.2237810752650413e+01 1.2688637793112969e+01 1.2235313891126804e+01 1.1100297213735251e+01 1.0773414547739115e+01 1.2217993905654721e+01 1.4701466325393794e+01 1.7319490559451381e+01 1.9808972202344144e+01 2.1257689253929254e+01 2.0368628657184544e+01 1.7524563363309646e+01 1.4843567969750833e+01 1.3863574192388871e+01 1.4031045614008303e+01 1.3693669004482535e+01 1.2122436338523961e+01 9.9467781708780620e+00 7.8498090003437868e+00 5.7967683127528629e+00 3.7206536395890204e+00 2.3267777177674382e+00 2.6303789001713365e+00 4.7445695138569235e+00 7.6683738268993942e+00 9.9717476628589878e+00 1.0271279571889663e+01 8.1958760665984958e+00 5.3998213275104048e+00 4.2899793853628481e+00 5.5462911375305124e+00 7.6595253723593695e+00 8.4981448223169700e+00 7.2294579833209385e+00 5.1143564806110922e+00 3.8260968045635870e+00 3.3065755059745818e+00 2.5815464286120089e+00 1.7488042411202225e+00 1.5184709880337026e+00 1.6370045591241176e+00 1.1531897091681369e+00 -1.5863929796862442e-01 -1.5759013113892080e+00 -2.3911490323759002e+00 -2.4147368159491429e+00 -1.8749121657426793e+00 -1.4086065112641286e+00 -1.6573036686621481e+00 -2.4019595691049194e+00 -2.7197582423545761e+00 -2.3228923158464436e+00 -1.8987417992229161e+00 -1.9762436025825927e+00 - -2.1788608089091870e+00 -2.3721017929273520e+00 -2.2248744539174794e+00 -1.3562719882732486e+00 1.9303974836178317e-01 1.3110471548593794e+00 1.2797866409946126e+00 1.1446512790093115e+00 2.4218364442454297e+00 5.3568199699566073e+00 9.1928444896243846e+00 1.2911855868338753e+01 1.5131303558516208e+01 1.4796675429288374e+01 1.2747818159896775e+01 1.1384776546745211e+01 1.2304499348204516e+01 1.5012766622841385e+01 1.7706586949808607e+01 1.8615946254339946e+01 1.7088985921306737e+01 1.4118105664819083e+01 1.1547667913836323e+01 1.0268505344072013e+01 9.5426651513383796e+00 8.3936752159092531e+00 6.7898835952875585e+00 4.9190137058105190e+00 2.3035160443938620e+00 -1.4454604882529436e+00 -5.5578141560543060e+00 -8.6285337833508056e+00 -9.8768639485094845e+00 -9.3497459396540972e+00 -7.4531759065479113e+00 -4.8343191161214989e+00 -2.2707557414183874e+00 -2.1273932165940168e-01 1.4763518504563440e+00 2.9929246806770484e+00 3.6484933755646152e+00 1.9940019916984792e+00 -2.3622776610960083e+00 -7.2558689942915287e+00 -9.3484617741468909e+00 -7.4954744905729234e+00 -3.9544592514927057e+00 -1.7075406868672076e+00 -1.4165546781975049e+00 -1.6221832630448796e+00 -1.0260840386708110e+00 1.6403282116014173e-01 6.0805828587662303e-01 -7.8558049118458273e-01 -3.6478240721938118e+00 -6.4046749238136291e+00 -7.7290809125845756e+00 -7.3840184127203816e+00 -5.7754694634749093e+00 -3.6798105136804447e+00 -2.5013142267663810e+00 -3.3639333122560284e+00 -5.7371424805126106e+00 -8.0051890648261459e+00 - -1.5740146351038696e+01 -1.5127188568935047e+01 -1.6157259030112712e+01 -1.7330034352768784e+01 -1.7263465597740716e+01 -1.6336957355980775e+01 -1.5946556228923413e+01 -1.6451747846316870e+01 -1.6476134055902989e+01 -1.4472580313892262e+01 -1.0935457247727644e+01 -7.9876368021814939e+00 -6.0265186692348891e+00 -2.0146808151690361e+00 8.0163390797194936e+00 2.5505537996776390e+01 4.7787314568418353e+01 6.8639540131510500e+01 8.1758985020608336e+01 8.5294009078541549e+01 8.2283381938562812e+01 7.6550409062053944e+01 6.9344678720567757e+01 6.0023044834365038e+01 4.8849934461625729e+01 3.8040291720134526e+01 2.9945383165919644e+01 2.5344624124046817e+01 2.3679402477458130e+01 2.3454441703713346e+01 2.2281261219059516e+01 1.7989994660541914e+01 1.0436193819072196e+01 2.1102305275848425e+00 -3.6577185659374600e+00 -5.5145847757485971e+00 -4.5324408328651318e+00 -1.8554008253732013e+00 2.7089378387782199e+00 9.2187555916040793e+00 1.5945566856263504e+01 2.0472163602212458e+01 2.2294516323769198e+01 2.2881411820375533e+01 2.2911237311157720e+01 2.1364859320014958e+01 1.7602082925628398e+01 1.2398805245993104e+01 6.8726800470759422e+00 1.6538594259187884e+00 -2.9437120155973195e+00 -6.5367981004048668e+00 -8.6437256178931250e+00 -9.1231101796868952e+00 -8.6648085339554566e+00 -8.4735847323575335e+00 -8.9963274994615468e+00 -9.3198443907122499e+00 -8.2504681130406006e+00 -5.5321797455350028e+00 -1.9690195798275383e+00 9.0573194795780632e-01 1.6386927935969133e+00 3.9409445444748448e-01 - -1.2097853394633344e+00 -2.3372661136012782e+00 -4.5868126352335468e+00 -7.7696106024369840e+00 -9.8847492594673341e+00 -8.4351485269365991e+00 -3.4815504396923176e+00 2.0318425367749775e+00 5.4688409099903446e+00 6.8155210844128202e+00 7.5289034993747936e+00 8.2470533843717533e+00 8.5005207895890198e+00 8.1730893089208134e+00 8.1217939375154362e+00 9.2472099093868128e+00 1.1682275429968531e+01 1.4843432519914167e+01 1.7625325319637877e+01 1.8827596749646286e+01 1.8262944365019820e+01 1.7257770010849043e+01 1.7139858491940043e+01 1.7319082568288064e+01 1.5832240015066093e+01 1.1889468934637401e+01 7.0260166885118247e+00 3.4084227701886709e+00 1.5903858359584313e+00 6.4628466394257855e-01 2.9208281149091886e-01 1.3680522046018677e+00 3.7148191371537109e+00 5.3471095100773640e+00 4.5999975157196396e+00 1.8618036691603201e+00 -1.1675467460557312e+00 -3.2014095470489683e+00 -4.1751621005792883e+00 -4.4441528031142692e+00 -3.7587484122597536e+00 -2.0282422842477690e+00 -4.8339177039881087e-01 -2.6711860399748077e-01 -3.8790549777032224e-01 8.5572500658487782e-01 3.0975713952792994e+00 4.3374149891908393e+00 3.2920941855788466e+00 3.9295487513328070e-01 -2.4783937608256150e+00 -3.2055601186298843e+00 -1.7160184560799154e+00 -6.3702741131976026e-01 -2.2383884758776662e+00 -5.5570116369254112e+00 -8.2084983887639105e+00 -9.5917739048230395e+00 -1.0331039070410487e+01 -1.0417944274770905e+01 -9.6648501662164410e+00 -8.3293893029282344e+00 -6.5549338022834966e+00 -4.3174230553074713e+00 - -1.9521686822118689e+01 -2.0828890952194818e+01 -2.1026487257169343e+01 -2.0644194340000958e+01 -2.0356661177644074e+01 -2.0486033910028034e+01 -2.0592315338305198e+01 -1.9336484142295909e+01 -1.5938473018577051e+01 -1.1921626681007268e+01 -9.9587671774048481e+00 -1.0450212320468967e+01 -1.0318268307692627e+01 -5.6239283444522234e+00 5.5321151308175374e+00 2.2417493712423962e+01 4.2078909484239603e+01 5.9973160754273493e+01 7.1702694488382562e+01 7.5072713982579657e+01 7.0924560394362075e+01 6.2663339872630232e+01 5.4356911122207862e+01 4.7989283986803173e+01 4.2765855626087045e+01 3.7486699590135927e+01 3.2734101027576685e+01 2.9993606241210717e+01 2.9069222310304095e+01 2.7645584279325117e+01 2.3767330134444784e+01 1.7501891196670105e+01 1.0097030989509490e+01 3.3105933448286935e+00 -4.4358692407392453e-01 4.2309373559202790e-01 4.6272362980750925e+00 9.2452802627883770e+00 1.2881829396441868e+01 1.6139200491488932e+01 1.9696338650752981e+01 2.2945254570047432e+01 2.4052713164028159e+01 2.1836262493236529e+01 1.7755371912217502e+01 1.4544324719156007e+01 1.3076166058655325e+01 1.2023365019266210e+01 9.9330468323536198e+00 6.5642773658552329e+00 2.6211689892425389e+00 -1.2463040312411424e+00 -4.6931152521918520e+00 -7.0610915480926959e+00 -7.8896098442295468e+00 -8.1377876309837323e+00 -9.2879431013150882e+00 -1.1118321847684978e+01 -1.1512230767538767e+01 -8.6570795004612968e+00 -3.0565564637606264e+00 2.6614202613936517e+00 5.7492933678780842e+00 5.1870636768943843e+00 - -1.2768528401242900e+01 -1.7338863074172309e+01 -2.0237569092156448e+01 -2.0576492513490894e+01 -1.9431123286614632e+01 -1.8641772316619317e+01 -1.8943170420368556e+01 -1.9167043655793719e+01 -1.7528873877656416e+01 -1.3999909814005159e+01 -1.0805366953545310e+01 -9.6142597372311247e+00 -8.9149462386606366e+00 -5.4471587606889269e+00 2.2906924892770721e+00 1.2948039993449356e+01 2.3895472387004183e+01 3.2785154492570712e+01 3.8224124421928316e+01 3.9996932598102731e+01 3.8917325110890907e+01 3.6404969952831500e+01 3.3603549356134771e+01 3.0482131529017348e+01 2.6418551514199979e+01 2.1753968035179085e+01 1.7865996396778279e+01 1.5605745867809237e+01 1.4445507979298107e+01 1.3294236667868901e+01 1.1519440091973062e+01 9.2364945825762419e+00 7.2532314388618229e+00 6.2602214003637382e+00 5.4987881290564387e+00 3.2320466512581985e+00 -9.0506248164303926e-01 -4.9456160363826012e+00 -6.3608335770462485e+00 -4.2734547891318062e+00 4.6309862256935785e-01 6.7021945441538548e+00 1.3111777285636606e+01 1.7514480785911530e+01 1.8248810290360364e+01 1.5766174142459235e+01 1.1757377659322158e+01 7.9733313460406592e+00 6.0274343310982506e+00 6.1770944745629279e+00 6.2936748428494491e+00 4.0368463533344734e+00 -4.2685170771143977e-02 -3.1865409383399417e+00 -3.8901205566515773e+00 -2.9576974863775090e+00 -1.4738094422502874e+00 4.0118819835881747e-01 2.6525695143418675e+00 4.6014650691595245e+00 5.1565376735843209e+00 4.0636905672697523e+00 2.6453975037372648e+00 2.3284476158012066e+00 - -1.3007839852110759e+01 -1.1025538159980380e+01 -1.0686371074924564e+01 -1.2744092667984233e+01 -1.5642265829843563e+01 -1.7140214084438682e+01 -1.6617034625505930e+01 -1.5300909110451405e+01 -1.4577809836556332e+01 -1.4955079002970214e+01 -1.5654258710135920e+01 -1.4407648124599390e+01 -9.0954740513659118e+00 -6.2982400317265430e-02 1.0332127909943164e+01 2.0193720207165487e+01 2.8972473785793127e+01 3.6317935037933722e+01 4.1210257687869564e+01 4.2631382536124164e+01 4.0972150948544574e+01 3.8156297398596216e+01 3.5717588235143509e+01 3.3207917004761363e+01 2.9304640994990272e+01 2.4112801189469973e+01 1.9353557646954098e+01 1.6308754098888908e+01 1.4516954476871140e+01 1.2392183920492100e+01 8.3965380716467521e+00 2.2326430594848903e+00 -4.3996278411324354e+00 -8.8062298459598374e+00 -9.2226405800100828e+00 -5.6733723455263254e+00 3.7975785593939992e-02 5.0496912837583618e+00 7.5061362215685907e+00 8.1297047935707027e+00 8.9880765155299258e+00 1.0960262021995606e+01 1.3066019970002715e+01 1.3738034632375317e+01 1.1896051602413694e+01 7.5099861905659733e+00 2.1853151873444245e+00 -1.4107524690116966e+00 -1.7374936024760426e+00 4.7474258642688838e-02 1.2777176662851293e+00 5.9151627918367655e-01 -9.4200465373474518e-01 -1.3835697125582160e+00 -1.1439767142326973e-02 2.0431818226269547e+00 2.8931387751496276e+00 1.7217225031925740e+00 -3.6057387583206468e-01 -1.4188595163897433e+00 -5.3401986141825453e-01 1.7331384545428845e+00 4.1028021413802733e+00 5.1530807234519207e+00 - -5.9423907462970762e+00 -6.6393370296983978e+00 -7.7903948031970467e+00 -9.0887616470108981e+00 -9.0730958856624682e+00 -7.3051960638397269e+00 -5.1092125692791504e+00 -4.1499019811620803e+00 -5.1825095304735012e+00 -7.4650822660744725e+00 -8.7858341576439987e+00 -6.9165862119378128e+00 -1.1152780425762092e+00 7.4456360751231196e+00 1.5504355240213883e+01 1.9891995742575336e+01 2.1880531164099967e+01 2.5705009499507383e+01 3.1274011045582068e+01 3.4010306181075244e+01 3.2770325333513270e+01 3.0948804502035770e+01 2.9539194246932787e+01 2.6197352223405726e+01 2.0544443634266784e+01 1.4590436095771173e+01 9.4132762585289544e+00 5.0726802461989733e+00 1.8845604581530462e+00 -2.0830751870538844e-01 -2.0248221543626945e+00 -3.9513021238798549e+00 -5.3937857883630116e+00 -5.7622214722998990e+00 -5.0964393510051575e+00 -3.6685468807689410e+00 -1.9925824037801600e+00 -1.3116773229001528e+00 -2.6369224209795075e+00 -4.8818299037998507e+00 -5.7575190263162872e+00 -4.9151597170028811e+00 -4.2580797535905948e+00 -5.3162000432783687e+00 -8.0260447395650854e+00 -1.1124603514599908e+01 -1.2372738496954803e+01 -9.9327640511974646e+00 -4.8837906274471230e+00 -8.7396451836104794e-01 -1.7270469647056230e-01 -1.5776571699924549e+00 -2.7982572746004415e+00 -3.1062409337303580e+00 -3.0845221509902201e+00 -3.0432684148630136e+00 -2.5011943998532136e+00 -1.1481969001182208e+00 3.2261647132979526e-01 7.8134574976169557e-01 -1.2641763242828769e-01 -1.4193228153058111e+00 -1.7047633076684265e+00 -8.4627935364228413e-01 - -3.3511583497758619e+00 -5.1854891048629845e+00 -8.2045548190539002e+00 -1.1321927344458468e+01 -1.3232156516018623e+01 -1.2891455905593750e+01 -1.0308983110741357e+01 -7.2770589395120400e+00 -6.3230641518260899e+00 -7.7668924711145673e+00 -8.5533116261484974e+00 -5.1754472720840532e+00 2.5464912688278725e+00 1.1150146477409665e+01 1.6749765390457195e+01 1.8459480819125350e+01 1.8853443625394785e+01 2.0968383227964456e+01 2.4745356436846293e+01 2.6900729147509413e+01 2.4586028405507879e+01 1.8423893823872788e+01 1.1687464461034605e+01 7.4694246602111161e+00 6.5063740610776568e+00 6.6399254866014452e+00 4.8727847407542608e+00 6.2948434274342469e-01 -3.8180925201354374e+00 -5.9745650071051903e+00 -5.0248037653094384e+00 -1.7240345099049890e+00 2.4707226972860665e+00 6.1789893211512830e+00 8.4606300098802265e+00 9.1837056122684562e+00 9.4175247215558571e+00 1.0361111194478536e+01 1.1355825197106681e+01 1.0443694556801294e+01 7.3275805488596522e+00 4.0729400267880624e+00 2.7849489919865249e+00 4.0449011920910980e+00 6.8903062423979726e+00 8.8579270913084418e+00 7.2409153007186635e+00 1.9815538314749910e+00 -3.4963983686766915e+00 -5.7025552070066450e+00 -4.6316183670653945e+00 -2.8570944089319230e+00 -2.1945685959883381e+00 -2.7295179820568904e+00 -4.1440094951781976e+00 -6.1739578627827907e+00 -7.9441831061071788e+00 -8.3008782660488869e+00 -7.1697557563545278e+00 -5.6642787357607363e+00 -4.5049090776747240e+00 -3.1919541675350729e+00 -9.9748944534117390e-01 1.8822644573222371e+00 - 1.6022978144703672e+00 -1.4311786252489094e+00 -4.9990745496272506e+00 -7.1229734861916816e+00 -6.0298488474363783e+00 -1.8843154305695582e+00 2.8057312397445893e+00 5.3045500670547181e+00 5.2217607939007324e+00 4.6015143428315151e+00 5.3985890315998715e+00 7.6627280386075034e+00 1.0581969356003244e+01 1.3680704869130523e+01 1.6133038629367640e+01 1.6836134617835810e+01 1.5985757935422852e+01 1.5071121283266066e+01 1.5015214850097710e+01 1.5311877473734373e+01 1.4952636894507775e+01 1.3509895518873545e+01 1.1287529167684998e+01 8.8944719667341001e+00 6.8056307961237392e+00 5.1599082109694479e+00 3.9096368310365088e+00 3.0884796567205659e+00 2.8027781380901557e+00 2.7427378963657629e+00 1.8080112317158328e+00 -7.2093739358622477e-01 -3.2256888875692162e+00 -2.5485135905025946e+00 2.1938957914977939e+00 7.8096722519713868e+00 1.0108587362109473e+01 8.0119639048454889e+00 3.7875674256494771e+00 -1.7909623605085379e-01 -3.6408942410859053e+00 -7.2043954289116119e+00 -1.0036996806105844e+01 -1.0480191286127010e+01 -8.6104715538045582e+00 -6.5737607267403781e+00 -6.4789888225111065e+00 -8.9832745466307422e+00 -1.3098938079216277e+01 -1.6488189562785699e+01 -1.7072074337181476e+01 -1.4675862164708811e+01 -1.0393673234405423e+01 -5.7706460525680994e+00 -3.2783034075657795e+00 -4.8699351422351356e+00 -9.0537498769047904e+00 -1.1681066334269010e+01 -1.0244235531230570e+01 -5.9719523689580347e+00 -1.9644630163354910e+00 -6.8837039873644690e-01 -2.7600482002062674e+00 -6.6765535392302047e+00 - -2.6793902875688205e+00 4.3700049391893547e-01 5.2207839165959946e-01 -2.5289383815408999e+00 -6.4357928708025778e+00 -8.6801895727348093e+00 -8.6519420551714941e+00 -7.5157946794804973e+00 -6.9229481363763350e+00 -7.7994153444953156e+00 -9.3675453247907754e+00 -1.0006058503692298e+01 -9.5066143246051311e+00 -9.1193248923592503e+00 -9.0487355120456403e+00 -6.9972012375818347e+00 -1.6948530860574240e-01 1.0752688787226244e+01 2.0728963668504598e+01 2.4824092328193505e+01 2.2702194691381209e+01 1.8103111981612606e+01 1.5186220180215653e+01 1.5435411462559298e+01 1.6851176785915300e+01 1.6256911281706046e+01 1.2853830842714240e+01 8.7992906000849906e+00 6.3848237032232804e+00 6.1798137367968202e+00 7.8154545356284144e+00 1.0467840599577846e+01 1.2480989360982822e+01 1.2582337684046703e+01 1.1590145096205708e+01 1.1337803770903093e+01 1.2054473739609797e+01 1.2250402949861966e+01 1.0983877637654345e+01 8.6551288071765491e+00 5.8174031977868585e+00 2.9188587271482644e+00 8.3930412496566609e-01 2.3622649323786860e-01 7.1444423031664739e-01 1.2655853456754595e+00 1.3229336834759118e+00 1.3578720434847311e+00 2.1076821170226498e+00 3.0728270343869779e+00 2.7843524321077324e+00 9.0852714062149698e-01 -1.0017936404451253e+00 -1.3974751167516584e+00 -6.4988794902308711e-01 -5.0807738982896855e-01 -2.0685431131427148e+00 -4.6510643108399936e+00 -6.4865750445095642e+00 -6.4858500824649585e+00 -5.1351447719989922e+00 -3.4051840546128562e+00 -1.7184951490663052e+00 -5.0407691071619776e-01 - -3.6981497476642828e+00 -3.0390710048817726e+00 -1.4989922553639727e+00 1.0284003810800462e+00 3.6622719317496890e+00 4.7174258831055269e+00 3.5121017080473034e+00 2.1663525068616294e+00 3.8933497703894613e+00 8.9756839635313099e+00 1.4369540972251285e+01 1.7517537513748934e+01 1.8367043951422612e+01 1.7487527428352760e+01 1.4910602011410518e+01 1.1788546361447585e+01 1.0695666486764603e+01 1.2768873027185609e+01 1.6000928788239836e+01 1.7360605935279679e+01 1.5930200170467559e+01 1.3260280784655849e+01 1.0849461771852765e+01 8.4797732256321510e+00 5.3784098602866708e+00 1.7165101724011995e+00 -1.4089618767998704e+00 -2.7952639837436752e+00 -2.2276522503149407e+00 -9.5316276326730420e-01 -5.3299313777074875e-01 -1.5019739358173170e+00 -3.5515201972803432e+00 -6.3187612986855708e+00 -9.3137337609793054e+00 -1.1318424950944941e+01 -1.0816559293228659e+01 -7.7508782461822854e+00 -4.2600195254335116e+00 -2.8712335676010809e+00 -4.2724290569942225e+00 -6.8899881431600578e+00 -8.1742447353742449e+00 -6.8082430068650392e+00 -3.9299615464428408e+00 -1.4896688083453651e+00 -4.1155002553450060e-02 6.2487647569787685e-01 -2.9020287195242966e-01 -3.8853316012394776e+00 -9.2125322820605025e+00 -1.3050926297558480e+01 -1.2737108773295276e+01 -8.7818443279864002e+00 -4.3127261410662650e+00 -2.3302722768023965e+00 -3.5325752167188584e+00 -6.2805892811261916e+00 -8.5016614679139195e+00 -9.4759820546177966e+00 -9.7204496390574704e+00 -9.7546714698047463e+00 -9.5752380341344967e+00 -9.3036539221877597e+00 - -1.7414985024478632e+01 -1.7710392269365567e+01 -1.8830345987045778e+01 -2.0905808395741552e+01 -2.3089781413981267e+01 -2.3899680760146289e+01 -2.2409641713666709e+01 -1.8930485800816182e+01 -1.4601034805586801e+01 -1.0520204067883871e+01 -7.1082264501151942e+00 -4.1771499408845667e+00 -1.2985932549142798e+00 2.5888198678848857e+00 9.5645053119594277e+00 2.1215332248500019e+01 3.6152478620496041e+01 5.0021402817940384e+01 5.8774208038896376e+01 6.1292360127806575e+01 5.8550762166546200e+01 5.1882091757242364e+01 4.2968986888033839e+01 3.3918424522864342e+01 2.6125731850668569e+01 1.9638134139516286e+01 1.3840450777647449e+01 8.3996525004490294e+00 3.8021795817073860e+00 1.1190628236490561e+00 7.5962516407305214e-01 1.4058807055741134e+00 8.2163118862213358e-01 -2.0188943881722592e+00 -6.0499550558624495e+00 -9.4755634193659208e+00 -1.1248638187248371e+01 -1.0876954767354995e+01 -7.8849814096440030e+00 -2.2277330312359691e+00 5.4138464272246747e+00 1.3796881454212489e+01 2.0984396320719696e+01 2.4893133374375935e+01 2.4942654855570076e+01 2.2326104353812248e+01 1.8202892776374178e+01 1.2706268587571685e+01 5.8859262076676249e+00 -1.1947559660524161e+00 -6.5689254319385437e+00 -8.7772879515580264e+00 -8.1899879887551386e+00 -6.5832072140625550e+00 -5.4688753261733805e+00 -4.9175403512819100e+00 -3.8699212831075047e+00 -1.6574438279084263e+00 8.5455765389835547e-01 2.0911729100665450e+00 1.8202474430112003e+00 1.6089416985684442e+00 2.9743808071641795e+00 5.8948563383564005e+00 - -3.3338514474187440e+00 -4.1130389374748058e+00 -5.1547704706642552e+00 -5.4193898987702704e+00 -4.0201652371671948e+00 -1.8076212696539353e+00 -8.9324938220319039e-01 -1.9868709786288514e+00 -3.3105995821020633e+00 -2.6515298234057667e+00 5.1903435078819071e-01 4.9231535819829615e+00 8.5469168923530585e+00 1.0071331008104004e+01 9.9283440319809362e+00 9.9281856288240444e+00 1.1689028944723646e+01 1.5180332000597723e+01 1.8495089144282854e+01 1.9468749633612084e+01 1.7774035163293483e+01 1.4795935112020807e+01 1.1632060118366278e+01 8.3107388002809923e+00 4.7969367455220766e+00 1.5228719476515731e+00 -1.2245839851149136e+00 -3.6724142837818849e+00 -6.1258400070447641e+00 -8.3043113945111795e+00 -9.2382296924864278e+00 -8.2818369343941640e+00 -6.3024540208842872e+00 -5.1153884354297716e+00 -5.4012598664097871e+00 -5.9086039260782988e+00 -5.2469498739198066e+00 -3.8701397078727497e+00 -3.4699836914403539e+00 -4.6999263838482115e+00 -6.4704904874754252e+00 -7.1253409419073517e+00 -5.1911313857217234e+00 -5.8248621615308416e-02 6.3735411041921815e+00 9.9842526040059774e+00 8.0901302020184040e+00 2.0765046689514737e+00 -4.0814624682782448e+00 -6.9335386550902367e+00 -5.3396830806898752e+00 -6.1569207664081205e-01 4.2371402928715165e+00 6.1453027828107594e+00 4.0729416305299182e+00 -5.6892224917549727e-01 -5.7266782214867291e+00 -9.4817569691134302e+00 -9.4466837493039364e+00 -4.4716815460577326e+00 2.5480231258059796e+00 6.3862725120871939e+00 4.7626827206150235e+00 2.7250354238558483e-01 - -6.6519531318265823e+00 -5.1307688760840637e+00 -1.5990450796308475e+00 3.2882196698155921e+00 7.5434887899979923e+00 9.8601744151731392e+00 1.0431338476778441e+01 1.0132207096708374e+01 9.8633193310657052e+00 1.0117849589142009e+01 1.0556320935364319e+01 1.0529237565608828e+01 1.0064399849178292e+01 9.8902096970315743e+00 1.0802538876610429e+01 1.3065613777540369e+01 1.5904751019353327e+01 1.7996878960824926e+01 1.8887262226773334e+01 1.9137392198836288e+01 1.8914272666242024e+01 1.7510508054314936e+01 1.4350155185280446e+01 9.5073777371400769e+00 3.7350791719348706e+00 -9.6044902572999469e-01 -2.1523757668680674e+00 1.2726446256922297e-01 2.7975425794223376e+00 3.1310049165812357e+00 1.4400885075259127e+00 -9.4699453877260531e-03 5.2252907539724913e-01 2.9730000242572210e+00 5.8714424054021102e+00 7.9092364851498598e+00 8.7344188791480644e+00 7.9030314193993458e+00 4.5624392185691374e+00 -5.8583551307959691e-01 -4.3527827486539321e+00 -3.8542689679266942e+00 -3.3407572539104535e-02 3.0190256438646816e+00 2.5671043830395353e+00 -2.7379787916712028e-01 -2.8159393124422332e+00 -3.9496120934673513e+00 -4.3869096765280995e+00 -4.8326892938827388e+00 -4.8327167767960031e+00 -3.6048592531173460e+00 -1.4049856452797012e+00 6.1244691414035868e-01 1.4045344432842721e+00 4.0378165896259843e-01 -2.2594911404427971e+00 -5.2758916592334257e+00 -6.6711436023807975e+00 -5.4988956630405870e+00 -2.7595297745975564e+00 -4.3083978002099643e-01 1.2310669289941012e-02 -1.8074724020788719e+00 - -2.5417468582312877e+00 -1.9298735695592950e-01 1.4524591906329190e+00 1.4111623962585536e+00 -3.3194621049731687e-01 -2.5702905387021753e+00 -3.3145376224302021e+00 -1.6531002526600589e+00 1.5085470936791965e+00 4.9384825610490894e+00 8.1158180601966112e+00 1.0592317046651424e+01 1.1720352844683582e+01 1.1731547138555802e+01 1.1880590058094510e+01 1.2779413370052000e+01 1.4075646883094780e+01 1.5918248275430194e+01 1.8326489365386571e+01 1.9316206123129387e+01 1.6690629155310674e+01 1.1661681239753394e+01 8.0053968635282153e+00 7.5533291045585287e+00 8.4805002524471416e+00 7.9431009917907147e+00 4.7893096294793169e+00 -2.9205326061448478e-01 -5.9299905729305458e+00 -1.0889025474342041e+01 -1.4253802911511571e+01 -1.5424425634403272e+01 -1.4219613607718578e+01 -1.0988129270893715e+01 -6.7271231240914489e+00 -2.8803629271713325e+00 -5.3221218228894118e-01 -1.4819897860306691e-01 -2.2309622192950278e+00 -6.7555441266626337e+00 -1.1288796495269558e+01 -1.1879369455774969e+01 -7.6695223775198915e+00 -2.7345250210875287e+00 -1.2464522929563033e+00 -2.5982520939275595e+00 -3.2907753012277650e+00 -2.0946248103194671e+00 -1.2670396502483379e+00 -2.9734875330694628e+00 -6.1944576306095183e+00 -8.0259110770747917e+00 -7.0406747672630017e+00 -4.3818830819913117e+00 -2.2337026899490038e+00 -1.7757039249321303e+00 -2.1690903031406541e+00 -1.7112483834128271e+00 -2.2443726961269805e-01 7.8312271984712389e-01 7.2340304513709708e-01 9.8143716696324113e-01 2.5456848227724063e+00 4.1446278797649949e+00 - 5.9509298374097952e+00 5.0678810528340978e+00 4.6985100256137695e+00 5.6806474798139019e+00 7.1270807068985986e+00 7.3896907513926973e+00 6.1956015245970972e+00 5.5852197604153861e+00 7.9143806050507850e+00 1.2903336221013145e+01 1.7478615210727796e+01 1.8733526717119613e+01 1.6557524998442712e+01 1.3237833414525580e+01 1.1194587213855545e+01 1.1742137197718762e+01 1.5292487123411739e+01 2.1089872768798958e+01 2.6615090233197662e+01 2.9112067948790987e+01 2.8381112807191901e+01 2.6745309556602031e+01 2.6230622139582557e+01 2.6873626067912664e+01 2.7379954661291766e+01 2.6724918542001461e+01 2.5352283105591539e+01 2.4558243711441179e+01 2.4278527981980282e+01 2.2429473534878777e+01 1.7501264178786681e+01 1.0987541340332779e+01 5.6451288588220212e+00 1.5426875751835269e+00 -4.2209755312065607e+00 -1.3196792287091672e+01 -2.2788705236686901e+01 -2.9082273423343587e+01 -3.1121693514905303e+01 -3.0849786691440926e+01 -2.9901975701231901e+01 -2.8187902353798506e+01 -2.4897002861955549e+01 -1.9629404309588349e+01 -1.3381778061564034e+01 -8.8077830839680740e+00 -8.0332756560395637e+00 -1.0120926859300432e+01 -1.2004163217689651e+01 -1.1842485990658060e+01 -1.0575962516702482e+01 -1.0149295488895902e+01 -1.0971618377841839e+01 -1.1645676947992518e+01 -1.0824020132311130e+01 -8.7473035146007145e+00 -6.7816366706045397e+00 -5.8695256679524599e+00 -6.1984801392743734e+00 -7.8537905857434129e+00 -1.0416960930281348e+01 -1.2429989647191839e+01 -1.2650740393840184e+01 -1.1521198276477271e+01 - ] -) -check(joinpath(dirname(@__FILE__), "big_endian.mat"), result) - -# test reading of mxOBJECT_CLASS (#57) -let objtestfile = "obj.mat" - vars = matread(joinpath(dirname(@__FILE__), objtestfile)) - # check if all variables were read - for key in ("A", "tm", "signal") - @test key in keys(vars) +@testset "test reading of mxOBJECT_CLASS (#57)" begin + let objtestfile = "obj.mat" + vars = matread(joinpath(dirname(@__FILE__), objtestfile)) + # check if all variables were read + for key in ("A", "tm", "signal") + @test key in keys(vars) + end + # check if class name was read correctly + @test vars["A"]["class"] == "Assoc" end - # check if class name was read correctly - @test vars["A"]["class"] == "Assoc" end -# test reading of empty struct -let objtestfile = "empty_struct.mat" - vars = matread(joinpath(dirname(@__FILE__), objtestfile)) - @test "a" in keys(vars) - @test vars["a"]["size"] == [] - @test vars["a"]["params"] == [] +@testset "test reading of empty struct" begin + let objtestfile = "empty_struct.mat" + vars = matread(joinpath(dirname(@__FILE__), objtestfile)) + @test "a" in keys(vars) + @test vars["a"]["size"] == [] + @test vars["a"]["params"] == [] + end end -# test reading of a Matlab figure -let objtestfile = "figure.fig" - vars = matread(joinpath(dirname(@__FILE__), objtestfile)) - @test "hgS_070000" in keys(vars) - @test vars["hgS_070000"]["handle"] == 1.0 - @test vars["hgS_070000"]["type"] == "figure" +@testset "test reading of a Matlab figure" begin + let objtestfile = "figure.fig" + vars = matread(joinpath(dirname(@__FILE__), objtestfile)) + @test "hgS_070000" in keys(vars) + @test vars["hgS_070000"]["handle"] == 1.0 + @test vars["hgS_070000"]["type"] == "figure" + end end + # test reading file containing Matlab function handle, table, and datetime objects # since we don't support these objects, just make sure that there are no errors # reading the file and that the variables are there and replaced with `missing` -let objtestfile = "function_handles.mat" - vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile)) - @test "sin" in keys(vars) - @test ismissing(vars["sin"]) - @test "anonymous" in keys(vars) - @test ismissing(vars["anonymous"]) +@testset "test reading unsupported function_handles" begin + let objtestfile = "function_handles.mat" + vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile)) + @test "sin" in keys(vars) + @test ismissing(vars["sin"]) + @test "anonymous" in keys(vars) + @test ismissing(vars["anonymous"]) + end end -let objtestfile = "struct_table_datetime.mat" - vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile))["s"] - @test "testTable" in keys(vars) - @test ismissing(vars["testTable"]) - @test "testDatetime" in keys(vars) - @test ismissing(vars["testDatetime"]) + +@testset "test reading unsupported struct, table, and datetime objects" begin + let objtestfile = "struct_table_datetime.mat" + vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile))["s"] + @test "testTable" in keys(vars) + @test ismissing(vars["testTable"]) + @test "testDatetime" in keys(vars) + @test ismissing(vars["testDatetime"]) + end end diff --git a/test/readwrite4.jl b/test/readwrite4.jl deleted file mode 100644 index 16b8f47..0000000 --- a/test/readwrite4.jl +++ /dev/null @@ -1,52 +0,0 @@ -using MAT, Test - -function check(filename, result) - matfile = matopen(filename) - for (k, v) in result - @test haskey(matfile, k) - got = read(matfile, k) - if !isequal(got, v) || (typeof(got) != typeof(v) && (!isa(got, String) || !(isa(v, String)))) - close(matfile) - error(""" - Data mismatch reading $k from $filename ($format) - - Got $(typeof(got)): - - $(repr(got)) - - Expected $(typeof(v)): - - $(repr(v)) - """) - end - end - @test union!(Set(), keys(matfile)) == union!(Set(), keys(result)) - close(matfile) - - mat = matread(filename) - if !isequal(mat, result) - error(""" - Data mismatch reading $filename ($format) - - Got: - - $(repr(mat)) - - Expected: - - $(repr(result)) - """) - close(matfile) - return false - end - - return true -end -cd(dirname(@__FILE__)) -for filename in readdir("v4") - #println("testing $filename") - d = matread("v4/$filename") - matwrite("v4/tmp.mat", d; version="v4") - check("v4/tmp.mat", d) - rm("v4/tmp.mat") -end diff --git a/test/runtests.jl b/test/runtests.jl index 159a125..e16c825 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,6 @@ using SparseArrays, LinearAlgebra include("read.jl") -include("readwrite4.jl") +# include("readwrite4.jl") include("write.jl") +include("runtests_modelica.jl") diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl new file mode 100644 index 0000000..4e22a5b --- /dev/null +++ b/test/runtests_modelica.jl @@ -0,0 +1,220 @@ + +# Test origins: +# test/v4_Modelica/BouncingBall/BouncingBall_res.mat - simulated with OpenModelica v1.19.0 +# test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat - simulated with Dymola v2021 +# test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with OpenModelica v1.19.0 +# test/v4_Modelica/FallingbodyBox/FallingBodyBox_dymola2021.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) +# These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents + +using Test, MAT + +#OpenModelica v1.19.0 +bbOM = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_om1.19.0.mat") +fbbOM = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_om1.19.0.mat") + +#Dymola v2021 +bbDy = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") +fbbDy = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_dymola2021.mat") + + +@testset "isLittleEndian" begin + @test MAT.MAT_v4_Modelica.isLittleEndian(0) == true + @test MAT.MAT_v4_Modelica.isLittleEndian(1000) == false + @test MAT.MAT_v4_Modelica.isLittleEndian(2000) == false + @test MAT.MAT_v4_Modelica.isLittleEndian(3000) == false +end + +@testset "dataFormat" begin + @test MAT.MAT_v4_Modelica.dataFormat(0000) <: Float64 + @test MAT.MAT_v4_Modelica.dataFormat(0020) <: Int32 + @test MAT.MAT_v4_Modelica.dataFormat(0030) <: Int16 + @test MAT.MAT_v4_Modelica.dataFormat(0040) <: UInt16 + @test MAT.MAT_v4_Modelica.dataFormat(0050) <: UInt8 +end + +@testset "typeBytes" begin + @test MAT.MAT_v4_Modelica.typeBytes(Int32) == 4 +end + +@testset "isModelicaFormat" begin + @test MAT.MAT_v4_Modelica.isMatV4Modelica( bbOM ); + @test MAT.MAT_v4_Modelica.isMatV4Modelica( fbbOM ); + @test MAT.MAT_v4_Modelica.isMatV4Modelica( bbDy ); + + #cursorily check negative coverage + @test_throws EOFError MAT.MAT_v4_Modelica.isMatV4Modelica( joinpath( @__DIR__, "v6", "array.mat") ) + @test_throws EOFError MAT.MAT_v4_Modelica.isMatV4Modelica( joinpath( @__DIR__, "v7", "array.mat") ) + @test_throws EOFError MAT.MAT_v4_Modelica.isMatV4Modelica( joinpath( @__DIR__, "v7.3", "array.mat") ) +end + +@testset "Aclass" begin + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) + @test ac.positionStart == 0 + @test ac.positionEnd == 71 +end + +@testset "readVariableNames" begin + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + # @show vn + @test length(vn.names) == 11 + @test vn.names[1] == "time" + @test vn.names[3] == "vel" + @test vn.names[11] == "grav" + @test vn.positionStart == 71 + @test vn.positionEnd == 228 +end + +@testset "getVariableIndex" begin + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 + @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[10]) == 10 +end + +@testset "readVariableDescriptions" begin + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + @test length(vd.descriptions) == 11 + @test vd.descriptions[1] == "Simulation time [s]" + @test vd.descriptions[3] == "velocity of ball" + @test vd.descriptions[11] == "gravity acceleration" +end + +@testset "readDataInfo" begin + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) + # @show di.info[3] + @test di.info[1]["isWithinTimeRange"] == -1 + @test di.info[3]["locatedInData"] == 2 + @test di.info[4]["isInterpolated"] == 0 + @test di.info[11]["isWithinTimeRange"] == 0 +end + +@testset "readVariable: BouncingBall OpenModelica" begin + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) + + eff = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 + @test length(eff) == 2 + @test eff[1] ≈ 0.77 + @test eff[2] ≈ 0.77 + + grav = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 + @test length(grav) == 2 + @test grav[1] ≈ 9.81 + @test grav[2] ≈ 9.81 + + time = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # data0 + @test all(isapprox.(time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) + + height = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 + @test isapprox(height[1], 111, rtol=1e-3) + @test isapprox(height[2], 110.9509, rtol=1e-3) + + vel = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 + @test isapprox(vel[2], -0.981, rtol=1e-3) +end + +@testset "readVariable: BouncingBall Dymola" begin + ac = MAT.MAT_v4_Modelica.readAclass(bbDy) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) + + eff = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 + @test length(eff) == 2 + @test eff[1] ≈ 0.77 + @test eff[2] ≈ 0.77 + + grav = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 + @test length(grav) == 2 + @test grav[1] ≈ 9.81 + @test grav[2] ≈ 9.81 + + Time = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "Time") # data0 + @test all(isapprox.(Time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) + + height = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 + @test isapprox(height[1], 111, rtol=1e-3) + @test isapprox(height[2], 110.9509, rtol=1e-3) + + vel = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 + @test isapprox(vel[2], -0.981, rtol=1e-3) +end + +@testset "readVariable: FallingBodyBox OpenModelica" begin + ac = MAT.MAT_v4_Modelica.readAclass(fbbOM) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") + # display(var) + ret = true + for i = 2:length(var)-1 #last time is duplicated + ret &= isapprox(var[i]-var[i-1], 0.002, rtol=1e-4) + end + @test ret == true + + #point-check values read from FallingBodyBox_res.csv + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") + @test isapprox(var[16], 0.002923239, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1,1]") + @test isapprox(var[26], 0.983794001, rtol=1e-3) + + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") + @test isapprox(var[33], -0.58818129, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") + @test isapprox(var[72], 0.935886479, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "world.animateGravity") + @test isapprox(var[1], 1.0, rtol=1e-3) +end + +@testset "readVariable: FallingBodyBox Dymola" begin + ac = MAT.MAT_v4_Modelica.readAclass(fbbDy) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "Time") + + # display(var) + ret = true + for i = 2:length(var)-1 #last time is duplicated + ret &= isapprox(var[i]-var[i-1], 0.002, rtol=1e-4) + end + @test ret == true + + #point-check values read from FallingBodyBox_res.csv + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") + @test isapprox(var[16], 0.002923239, rtol=1e-2) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1, 1]") + @test isapprox(var[26], 0.983794001, rtol=1e-2) + + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") + @test isapprox(var[33], -0.58818129, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") + @test isapprox(var[72], 0.935886479, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "world.animateGravity") + @test isapprox(var[1], 1.0, rtol=1e-3) +end + + +; + diff --git a/test/runtests_pr132.jl b/test/runtests_pr132.jl deleted file mode 100644 index ee4eea2..0000000 --- a/test/runtests_pr132.jl +++ /dev/null @@ -1,37 +0,0 @@ -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -# test only MAT_v4 -using Test -include("../src/MAT.jl") -include("../src/MAT_v4_pr132.jl") - -mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - -@testset "checkv4" begin - # function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Bool, ff::Bool, compress::Bool) - # function matopen(fname::AbstractString, mode::AbstractString; compress::Bool = false) - # mat = MAT.matopen(mat1s, "r" ) - - rawfid = open(mat1s, "r") - (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) - close(rawfid) - - @test isv4 == true -end - -rawfid = open(mat1s, "r") -(isv4, swap_bytes) = MAT_v4.checkv4(rawfid) -mat = MAT_v4_pr132.matopen(rawfid, swap_bytes ) - -@testset "getvarnames" begin - # function getvarnames(matfile::Matlabv4File) - @show gvn = MAT_v4_pr132.getvarnames(mat) - # gvn = MAT_v4.getvarnames(mat) = Dict("name" => 71, "Aclass" => 0, "dataInfo" => 448328, "data_2" => 501296, "data_1" => 488197, "description" => 117126) -# ...and this is not correct - @test false -end - - -close(rawfid) \ No newline at end of file diff --git a/test/v4/testcomplex_4.2c_SOL2.mat b/test/v4/testcomplex_4.2c_SOL2.mat deleted file mode 100644 index 36621b25c08f18e4545100c6eaec015123c3bf9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmZQzV1B{Cz`zK^oKTvlB(=CCIX|}`C$)mX{sT}H2<)FNn3q;>eb#2;GBsn7820@T p{+azyc_{zfo>i5WKJ&V`pz2S<^g~R6n{x&x4mWop0dqG(`$IPYCV6bhD=3Sn-kr dCzQ{hRiznf5$7NT6&L-x`{nNCr4Eu1c>u#Y9lihn diff --git a/test/v4/testmatrix_4.2c_SOL2.mat b/test/v4/testmatrix_4.2c_SOL2.mat deleted file mode 100644 index 3698c8853b46d4a42194002523b57fddfb225908..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151 zcmZQzV1B{Cz`zW|tUwF`+$E{SCAo0_8%Z(4iJjLfdiEf6^2tVdAI64fzvu`z diff --git a/test/v4/testminus_4.2c_SOL2.mat b/test/v4/testminus_4.2c_SOL2.mat deleted file mode 100644 index cc207ed9f32095f39b7690e2dc1e2dc0d55ee8e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38 kcmZQzV1B{Cz`zK_K#GB@B(=CCH#4uam|_11kN^V%0A!m6lK=n! diff --git a/test/v4/testmulti_4.2c_SOL2.mat b/test/v4/testmulti_4.2c_SOL2.mat deleted file mode 100644 index 9c6ba793cf41bf36447ab7a1890447fe5e939614..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmZQzV1B{Cz`zW|tUwF`Oo>TXrytD# diff --git a/test/v4/testonechar_4.2c_SOL2.mat b/test/v4/testonechar_4.2c_SOL2.mat deleted file mode 100644 index cdb4191c7d2eb0ac66d4f6add250e1f6a604d892..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40 mcmZQzV1CKKz`zK_K#GBoBe96VA*KN&#sC0t%m%jr diff --git a/test/v4/testsparse_4.2c_SOL2.mat b/test/v4/testsparse_4.2c_SOL2.mat deleted file mode 100644 index 55cbd3c1b3d65630beae47832ffbcc7a6fd43354..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmZQzV1C8Gz`y~-%s>nR+$E{SCB+4aMa8KM_8%Z(4iJjL0i+NJVB)xFLh2mArZB+G Wa}aKwqPFu=`o5P%3ch@jFi^D)#D&<~Y{yA#GIl?DK-2^cH@ diff --git a/test/v4/teststring_4.2c_SOL2.mat b/test/v4/teststring_4.2c_SOL2.mat deleted file mode 100644 index 137561e1f636d7b08959e43e969a6984eb7a3b37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 375 zcmZ{gO%6an423I}j^YxvVJTQKEJbWAm;*SP>$ruVzPB0Cq-nqQwbP79e2PePdwTn0 zi61w=`E_0<(adUEA-dyDRLQ$>X9acO7HmP(fmx@H{cwJe*OdBxH||jjyl4?yTB eFvZ{y=>Xxw^u;tl_z+ 0; + reinit(vel, v_new); + end when; + + //copy files from C:/Users/BenConrad/AppData/Local/Temp/OpenModelica/OMEdit/BouncingBall + annotation( + experiment(StartTime=0, StopTime=1, Tolerance=1e-3, Interval=0.1) + ); + +end BouncingBall; diff --git a/test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat b/test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat new file mode 100644 index 0000000000000000000000000000000000000000..dca4f2e2722d74d4e2fdd678f340777238096b2d GIT binary patch literal 1426 zcmbVLO=}cE5N*FdbMqo3(&!~CqONlbX2%`z5XdnnLkOKo&!%OzXPKGovUu?a&;A$%cg^9L2cKYdiARMV;#oWY4S_tuyf>fKNLyA z>Q%my-exMUaxEBNc*1!sITt?XI^{fxIA<~Eyi4v-86=F%8H%-NO17YyRFjzuf+FIL zShS0HO{&Hk$&szfcshrbwhW7W_LX3Ta1eHtZe$77YzaRvYcFfr$=N|fL*<5vZjMZJ znc`KM__1oGD$@C2O7q2J$&?(k`Vmd8d?jhuQ9x&qq$*v9$an#^rP3}PkuO6T3mYe| zDRxbAD_qm<*e^1~Cku=P z_|ynS&g8*IZ1M)98O&fjc$mRwb68^huV5yBK8Gd7z%b-5(u#g&2Gn9R%T)>(+q(A1 z-94PFGxp3iF-jnTR}L#n?&X+$^@@N|=?+Q_ruD(zU@-gb`+@iF`qQ^?J7>PO@dBJ7 zafVjTX0KQKId$MoJ-*+E+jR!DJNgTH_2D)S&E!@#_PyQ1mz0Meoq4l&)&0@=Oue?L z*S3}8IF1`tKY5)u`+c}gXKyg?zTWzXebGEaD<=p7cQEn6d;V}@2X52BnZCOFo@SoR I^8d^E4JY?na{vGU literal 0 HcmV?d00001 diff --git a/test/v4_Modelica/BouncingBall/BouncingBall_om1.19.0.mat b/test/v4_Modelica/BouncingBall/BouncingBall_om1.19.0.mat new file mode 100644 index 0000000000000000000000000000000000000000..8e7f76e1823f2de740c26dd2869522b7fbdec7d2 GIT binary patch literal 1761 zcmcgqJ!lj`6dr%_;}51V7$oAYoCJeW%w1t6vq2DS1W^lb(POhWdl|XxUUv5qQz%$O z5wWx|h)pa6EreJY5N-u60>oB4s-pOu;u)+(p6<9eleAgJZD^1gS1$gxx z8BMM|-@F;kCl}LuarW>1t@AA=)|o~@O$-=PSvF*7w6^RYT8pc4VLQT=n}l=(Q_=jgVsyFyBN4FxDtWz#88b z0qb@ufU(BsOTfC53Sg|!5COg57y;|n^Kl$&T7%knnc^VMBzRCgw)Nf2SDu;re)`${ ztf@wq`C8+6G`>8p_m7lISt&Zx`C%z-M(mOR~nQGroy`MN6Q$IDEikp2__xjAA*7KR)r!2ECxmQn2 zoYs$<;0x!o10N6dS?0kTyB6O}^v1+#byR=8@y5c^xt?DI{l7B0Kbh41S$K{3!Tqi) lH|Jgy%zH15C&9ygF>zWy)UWQ#ZzsC17j&Pqt3Lj>&L8j$T&Ms5 literal 0 HcmV?d00001 diff --git a/test/v4_Modelica/BouncingBall/BouncingBall_res.csv b/test/v4_Modelica/BouncingBall/BouncingBall_res.csv new file mode 100644 index 0000000..d4a1a70 --- /dev/null +++ b/test/v4_Modelica/BouncingBall/BouncingBall_res.csv @@ -0,0 +1,27 @@ +name,time,eff,grav,der(height),der(vel),flying,foo,height,impact,v_new,vel,,t=0,time,0 +data,0,1,1,2,2,2,2,2,2,2,2,,,height,111 +,0,0.77,9.81,0,-9.81,1,2,111,0,0,0,,,,0 +,0.1,0.77,9.81,-0.981,-9.81,1,2,110.9509495,0,0,-0.981,,,,0 +,0.2,0.77,9.81,-1.962,-9.81,1,2,110.8037994,0,0,-1.962,,,der(vel),-9.81 +,0.3,0.77,9.81,-2.943,-9.81,1,2,110.5585494,0,0,-2.943,,,,0 +,0.4,0.77,9.81,-3.924,-9.81,1,2,110.2151994,0,0,-3.924,,,foo,2 +,0.5,0.77,9.81,-4.905,-9.81,1,2,109.7737494,0,0,-4.905,,,flying,1 +,0.6,0.77,9.81,-5.886,-9.81,1,2,109.2341993,0,0,-5.886,,,,0 +,0.7,0.77,9.81,-6.867,-9.81,1,2,108.5965493,0,0,-6.867,,t=0.1,time,0.1 +,0.8,0.77,9.81,-7.848,-9.81,1,2,107.8607993,0,0,-7.848,,,height,110.9509495 +,0.9,0.77,9.81,-8.829,-9.81,1,2,107.0269493,0,0,-8.829,,,der(height)/vel,-0.981 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,der(height)/vel,-0.981 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,der(vel),-9.81 +,,,,,,,,,,,,,,,0 +,,,,,,,,,,,,,,foo,2 +,,,,,,,,,,,,,,flying,1 +,,,,,,,,,,,,,,,0 +,,,,,,,,,,,,,t=0.2,time,0.2 +,,,,,,,,,,,,,,height,110.8037994 +,,,,,,,,,,,,,,der(height)/vel,-1.962 +,,,,,,,,,,,,,,der(height)/vel,-1.962 +,,,,,,,,,,,,,,der(vel),-9.81 +,,,,,,,,,,,,,,impact/vel,0 +,,,,,,,,,,,,,,foo,2 +,,,,,,,,,,,,,,flying,1 +,,,,,,,,,,,,,,impact/vel,0 diff --git a/test/v4_Modelica/FallingBodyBox/FallingBodyBox.mo b/test/v4_Modelica/FallingBodyBox/FallingBodyBox.mo new file mode 100644 index 0000000..3340533 --- /dev/null +++ b/test/v4_Modelica/FallingBodyBox/FallingBodyBox.mo @@ -0,0 +1,16 @@ +model FallingBodyBox + inner Modelica.Mechanics.MultiBody.World world annotation( + Placement(visible = true, transformation(origin = {-70, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); + Modelica.Mechanics.MultiBody.Parts.BodyBox bodyBox(r = {1, 0, 0}, w_0_fixed = true, w_0_start = {1, 2, 3}) annotation( + Placement(visible = true, transformation(origin = {10, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); + Modelica.Mechanics.MultiBody.Joints.FreeMotion freeMotion annotation( + Placement(visible = true, transformation(origin = {-30, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); +equation + connect(world.frame_b, freeMotion.frame_a) annotation( + Line(points = {{-60, 10}, {-40, 10}})); + connect(freeMotion.frame_b, bodyBox.frame_a) annotation( + Line(points = {{-20, 10}, {0, 10}}, color = {95, 95, 95})); + +annotation( + uses(Modelica(version = "4.0.0"))); +end FallingBodyBox; diff --git a/test/v4_Modelica/FallingBodyBox/FallingBodyBox_dymola2021.mat b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_dymola2021.mat new file mode 100644 index 0000000000000000000000000000000000000000..a00c2d38446a1a1877b2e4c409df79250dc5a303 GIT binary patch literal 169803 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

!+|{-#`x1Ax?aIEy zU2VIvFL77f(k;Db%g2gyY^$QEVsM%W3DXs$Lc3MdZi=a%D%*1 zZM(8BaaY@}>`UC$wk!J*ceO3u(*L)&^pNphChon0+SpkBmAd>((D_%R|FhRLxvOpN zU5MZndNC^Q{eIv}r%3`YRMf@@Ul$7AYN&|Tf>-gG-z)m>wvfq7>m89Y}qRHWp6yfZ|(E@>pW-Z@>CDI{oFgn`7?Awi7kspEl%*teCr( z?Jj(F^Es{f*P3csdETEp$}Taj=%Cg_Mj6a?*;VD%fLTl(*G22&brGsCwZf;7LP>xb zP9W!_0}z*xprig*Cfl;gZP&P&2CPSMf%;d>2diWC=GpSF0cGgUF68g;Be5zSRRA41WnrTMo;4<__u z;-b|Cb#$^q1+KE_Le=4Q?L^e^Cee9CezXL$AW^Fm&U<8Suyo}AptiZIZBUmjNz#vs+;YID-`Z(8;0(mmz@uMfK$Dgf;(*P zXxma|RalrhTCL-k_kflhHQ2%{l_II0U`cgnBQD5uH?wUu&lDN0)`#c~DxE>0(y0~k zdN9SL(u1L@#-IV~s1j7s@oK%VLLIFK>m&4vNPzTask3uTuy^lhr5ASDV9^C|0ZumQ zv<4Meg&{o3DxEeS%xcxqt4GGGW5d+Er*#ynB(RGn)-pq-;+9~H30Ftjsdw&bySF)b zFxp&(t^VRL9)Sw%~Y=a@FzX+iG*0${l8_jt%&w;Kzp`Bf%(zTeIeTuW5abhK{$p*%lmc z620-)a4ZnMXaZi}RPZgc0O4ftp zf+jH}FWd-hkBkQjbg^86Zet}*YXB$auopMm(}Gp$x*6hjQ8=WnTXi(cLqvJHs98ikeM?Y;*R^ zK70bRoVUm}yeRdlY@#r^a*c_{wrBmCXZ@OR9#^>59@Wr)_U+0#mNn<$x!>SZ-Dlxo z{u-SFfeD2$FlGW5us~lI1NvR-xmHAT?wXyu+U^Lhc7oxj0*pezr60?n7vJICxwuo8hOAE5 z-Sr&Ub1#!{LC2@N-``dD^tkeVB3IfLT%&S$FOy}_FuZ|D9|x{cspVV-vuaO-K;qt( ze}7^}Xw>}MU2UUlH9AeCCY-#*Hm z4S29d6$9=s=2n;RQ%C>O4fSYXWary*|2Hy>R&HCZTDGI$A8c}0+rr~*nKzVk@%kUc z8trO{d6wZ%cEsJ)wkw}CAm~d}o_jpN!VZxeG_CI!^xWf--FsWG^@<;J${ICimT!-3 zZmpyz=9Y~3ct1S3_UUuYvyRKNj?1%->%Y5>%a!#z>$-vGUZziVFO%Jvi_PRAnNzcP z;$r}JwQY4xQtk|@Iqu+!+&_BOg1g$b$u&9GCarVb`HOPqw0BqA?RBaI_$*JWP)l9= zh0pZMc7$!)7k9NSy0%7mjXit!)X16$cURl495Z=rd(Ou^=VSlf^D&#TgK#aE;Px5o zb##Iap7%-G4h|ehJonWI2Wg)BYRtDi5AOCKdzYoVj@MjSZqInlGhXwI*Z#ZXHCNVe z&sg!(UpMfK6+L4`(U;Bt$Ht1n^=s1alazYWuY(gV2b(4CgXU-FJ@1nQwHW&Zj=$EzjVs&}y|JTpJb-pMTL$qmNfbYeuPcdcRiT2`cd7oN&M1up5y5 z@;%G_2IgXavKt-1@pf0+uDsVlMm79sl%xi#B=D}tc<^c{aNBzrd}t*|VlMHl`%d9C z@xlMR|AYl{SKG-hjJdnoj?;qIa#!1~oV)kf_FNbATo?TBUKjLS zoAX?oi&cEOwNw#WU6>la6-OJL0N%nZdWq^|yzkGI&k1%{+wMAEb7i?b<2BEC%`;y6 z?~d18S-(AFMbB8#Ggh>`ZtOp|?%tK-RC(*TeDqP;M1@MH1OK@z3uvVYxT?Va49&({ zHAKg2wvUzvHgI96_4NznjS~w8B;Waln*y&Gm201WI=17pC{MZh3$a`#{eyTrbA`o~ z$D2?<>$(t~IyyvE*H@wOQ~UW^J%k+(La5D4Y;lH4ss=RFTO}a0{ORmlKOg?n*#(*4 zDK~e8%k{@Q!JOyZG5Hst;9&m(9-nLOX#0QqIsd3;I6g>W8_4>aSPEKR5}9~ z5UUmOdN7@?(t{awu>JmH+fvVA99?zir#HCYV3LwN%ni z@TmQ)^~i2!+m-8)1@#-8lBo=u1n^2Ml}@7yg#(CCwINZh&boyPzBm-MuV4jbbTnXC zT7A$D7{ET3lxeNiMypk^btPL>f$fp;U|(GYtuIk#ONVDCPXs&>@I=590Z#-x z5%5I76M>Hxfl^*xUb(>E-JIYb_~ZwF;VOfwV{C+$6Xf9jdwG2TANbqMvuyJ=Z--xS zJ5+`~5a&h!d_$ZE!+9~B55v$Q^nv>qKmdG0ToA*BFkBeJMKD|x!^JRM9K$6rToS`D zpy&hrD~$m7hPVud%VM}3hRb8P0){JMxDtjdW4H>2t72Gz;c6JJj^P>@u8HAV7_N=s zIvDoBa9s@7!*G2J`(n5OhW#+y5W|fy?2q993=!Ei8!+hVvKhTCJf1BN?dxD$puW4H^3yJEN-hPz|92ZnoM zxEF?dW4I57`(n5shWlf90EREaM#KDpKYww62O=`~hSWhA9*p537#@n@&oCT<;b9n7 zVHo#66x$w#;VW4At~$Ws*#2q^M_~9@Oy6%9j>P!CW81G`I11xyFgzT?BQSg&(}%aG zIF82lkHK&(hHqf{|G+Tc%kw+IO*gUaw=f)s=^u&V+ZbPmZNGzU$IFLfJ+{9A!|@nS zz;GgllQ5i&;ZYbKjo~pE9*f}=4CDFlsQ#v6`;Wu$cnnX#@I(wx!ti7aPr>k13{S%_ zUY_aLc4zDF3~c|I7@mdUKe7C|i{aTA{~oseK8ELD{J9vOhvE4c{tMHG=P!;IVEZq` z@FEO9!1O=F@M4Vr2;2S`!%HxJk@p`yAQ7Z4MF4z5{5gi7V*2p%;dmLg|8fkk!0<{8 zufp(Z46niPS`4ql@D~_fkKr#djOV+f`ui2O{{{?i#PB8zZ^rNz3~$BoHVkja@D2>` z#4uhyXY21SOy6z{@4@ghEPuYnwim?m@o$X(0>dvcycg5A55xO0{0iG2&tDuL!1h0g z;X@dHjpay# zW)UEM|IWI-@y532!fe~)3jy!o*5;N{Ja zZ7<*eU&QSFAi!`}ln-#e3*uVl?FiSwaBd9e!Ejy-=fiM*3>Uy~K@1ndaA6FW!f@c;LCUJ;ZZ#`7wMf5q@` z82%l@*D!n?!#6Pe2ZnEA_!fq5WB3k+|HSZJ4Bx}>eGLDF;RhIgh~Y;VevIKK7=DW3 zXBd8t;lDBb0>fPd80N0C?Ll4h{wTleW4Hl^o#hwD_Ai0qk{EV|5A*eUj%p*{W|Pl7lHC(Ob*G3<@uTo}%c;XD}5i{X42&X3^&7%qt6 zLKrTL;UX9=is51yE{@?67%qw7QW!3c;W8L5i{Ww@E|1{~7_NxnN*Jz;;VKxeieUwY zt6{i0hHGHBCWdQaxV8X)iu(P()}IyOs}grT50<|uhKmU>3{@RLyx>nC4A;eQJq*{! zurG!iVAv1C4KdsZ!~Pf!z;Gaj8)LW$hMQuznE-!^d~v3|hWdpa z9E4#dh6xOl7^W~xW4INDTVuElhJ!KO7Q^i@+#bUnFx(NtoiN-P!(A}k6~o;yEF4e3 zP2I8WJuuu8!@V%v8^e7t+!w?BFx(%*128-g!-Ft97{fy_JQTy9VK@ZC!!WGEa43eu zFdU9yHHITF9Ess50fzO-ne!*eZv+0|2aWfFd1%3HG3*Qibmn{*x;9(jAN=VkK65ZW z9F$=Fnkol$1wa2 zhL2W(g`~!x6#PCl7ELNZK`YWuj zaMw$M{o&7_1sLi>*Yi+)L)W(u_6HpJK^R@XLpYG%Y}wuz!%Z;U6vNFh+#JI#Fx(Qu zK^RtIn7}ZJVG6@EhFf8{wE)9wW$=0*j3fNOYi)4xdI$;{CpG^Yhb+m&!hMCmp&s{=jDw(|;mjNY#ZT9u&IbPJ6(_KJBaN9zCQoqNM{JwB|1b5aL7t^{k&*m|vmj>Iw zUkAWNf8(HipyxCJzQG^=-D1uSa8%Z|0vPzFXdM_B2+A}HoVkt)j_bO9IHnh$$&jBKeOxtr5Crihq|-;lIB33b01hP@v_;<5s%ARE?+IS z);>bBC?K~}xw>H4#{J;-r9t|GKZ{K><_Oq7oU`0qf8jUs1MT7nds*`Myv7@~e%#I( zm(EzWK>?1D1;Mv~zq}RH2lsKjALk=2r~G`~=PYuuti87^YM}+Oh5E8|$}1Pg7Zk-2 z22J;!N|}NkocL++Ph=k{yCQkT&EBQ98HC!fE^WBFkfl^g7h>SskzDS#*#4Z1=8vT# zJ+LmI`T+Msf3P1wbwQ{Dw`Yxaqc{k6v8fMUUaJ?B1O1Q**!bPtM~x$vFDp+x&DDf5 z|7F1Mnk@z*8Hb?^@}Qi~(Kq7@hc(v7~+-qzoOe$U+YO7pXH^E!lmER+bh+uN3vQ_{Aw{DPVh4}C*f)<JPC;~A2Q2S1DcN!9wOmy$QQ%H!*`xedRr-TOReYWW-cv%_$Jh2_D%Hb zJkaUo1?|9ZXluiYqj2(uh8TRX?#~1;r<>nu`EOr#xd27iIP^Y4Tf#f2_J>p|=Zhmj z4N6Q*@A5vC8UXD5T6|)9jR8l~cLS{X#XBe=VPa5Bhn(pH02kUbsl4btEJd$=e~vyh zq*7-48u=Na+*v*4D-3EjKULCqJvxu_gb?8}2ADl3>Ltw!&y6jSIBltCL%2JJu@w1r~Qqquj_ zH?KdCwZJi3ba))qc)-!1Tw}5qldq?d^xsvyOfU!gzD%Wt=2)O?wJg}Ue0hP9dX`F6s&*nMW>7ojiF}#qML~>UKKxQM2b~&3(KDW&quW-ln7O9VPA(=$ z?yR001D`iPm{yf2-@ExC!)`6}`NC%VhBaAm2+E)hD1&yO4BA35sq%Sn5c%u_xpefg zpprGlQ*(SuDt~UCy_ocEPtq?^y-c9)C_|p4Qq%GrR_-f0(a8LfN|jrDRJpp)L}P`i zM~v0(rBX`+@+hmXo}^rN>_htS=X^|>PRT*9d_~fi|2#)899tptdAmQjm>{{cda9Hj z)MEPcK17i(*B`o)yoR}X_1M1f_*aLZ4BCJ)Xa~xmEfkZ4A`^qk-}^xNfIKcRaXfXf zWU%t~GuHXKGcLK_vD_s6l^S!vM_Gc}p#0-f6=U>6J|=OubI?-{khJ>hIr`75*J~L zzYI#hUD=2UXoqtjCFq5%a)7!Mtn7OYv?nu)5tkMmHEuneN=?v|Bt}d>YP{FH1XJ%q zDs^miXJum16UsC-VJvxvkIA$SIcYkcr0btOM|UL4XZ|^$4i^(7cUDiCUe8;+-L-}& z^+mI^@vR;(N55UN|77%(G$?~MpbXl9GH46MfpW+2 z?8T(y1cGk(MGjNzVQOWYA5*ELMSX~bUN?vA*bUB(A;HxsEmv}OXuG})@u)XYXq#wBlg zL=p6soH;>zGEq79WGdBpI7KM)c{6Voq*CX{1rrk53&;5Fl*m3@gw5tJC*ejuR z50pFjXBw128&C%AKpC`!Vq*06Rz__5Kvr8dNjd%91nS7tBg&yy8Ry#*PR7$8l{%GgC^4;nN#@{yK}?>dpsne?LV3M&St3`vna0x~ zelRAvM|ji0+X?#C+;cP=QZ{p1*+ecTNbanjmM^BaJUH?p!JIym)_Ijbd;Ev!1AR`G zNQW|L1InNsD1)|8Ox}i+R0b^hK!!&yR-PF&k=k_r7v=25*^5b;bS2&5NKVkku2=rF zC6)SfO$;&ONDXFZ-&E?i+3`g9&KgYTARV(HC6#J1dAqXCJRf5IzGcRTMm{EsPkGbL zhZFSb_;d77Q<+Sk@27DwL2_sHMEBUzGR-SDd7OQh*7IpFTe0cd1H~#gPKPpR1InNs zD1)|8Ow!Y8C{<%WkaLL*$|(mXQay@2Q@-qyy_gJ3QqtXOc$)^a*sFZ;c`DVPm_Vqi zHfA3Br&3q*P9?l^H)dAmn#?Qls ztxRTmxg}gokla~47mA*3d9hsy@^51S|h)^MUMB z_=K{%b`o{CMG0c!A!D}MlVMMS=n^-)P0!w4R91~kr8fWcIWhkGJ`DAH3KcYI4Y7Gs zALhiKl}ydrsZ^bIH3Y?S${#_^kSn^*tH?`4^qUEbSQ&1pbXl9GH46Mq^qfqvO|pzX5kzKKM@-YeDmYc3zPD!6VahAUFWy#DA zS)CyIK#tt-e21tG>c_cCX+*nw6PWg0QmB=FM~TNrCNP(8W-x;eq)@-~FG_5DHH~UUil(m#1WA`QDefm>{{cdRAW^81&_&KIEB7>(bACwStZ6 zv+Cfh9oN#K4BCJ)Xa~xmEff>qpC>2}?0!#nsg#EZxHOpx32RLxPS0LU%Ky=lem^X? zX-S)cL}}2jQu&S%8Kq`2ex*{Vk)h{_jGHr|*4~;=yx;oL*d&OL z$mAa_oXk&{}tOahwPATPP+^-_KBPnDU-1ysHS|?LUP|Rdplg1!W^9;2f~X+Lm+;Q*JP3D@pXf zpF%NL&k^m3h0NYdW2wmtuMk^vFJyvi{=~FYfx7gtIv1oz=r$m>$&RaV$B0$f@+eupR8lxL*&>Y+l3&WzYtcK|4?e zZK0U-sIgG_X25$gsbg89_4Fy!%0YvOKkuhytL}U^xFubyY97;#uPPFEPNz^!R$V5} zhp%8REgMVi%)CP^ud#ypVfS@rN=$EekqVeGIvx{q^+Sdk(NIVsah|X;8%oWzYtcK|4?eZK0Ud zZ?{6Z&*wck=}uMR!S7S3E!`rB@vE{C6L7v0QMM(WDhZ+h87{;&OvPzG&48MFgs&=!hGT;zJ? z56|C`KC^v@e9fm)s|g)3xNEjzQt#w!X2;*OhTYTEL{{v7qY*)8aSL3zR0zA=#!oXy%T6 zCApX&xwCpsrk@E?U6@Z+DOuJyBK&*yRk4nT%2(`dgfeIY%Ag%6gSJpiu6_Bnvct4@ zWbxZAi1B|+rE=t%OnkQ`dol6q*n&R4EUzhbxRU5arckRs%Sk?dkltCL%2JJu@w1r~w`tlLwfMV~++x(> z=$SiPG1-#9Hm7^_%V&biXCB@kOQo(aL{@Kknz{e;Xv*(IIWm0XDQ4ujl5E90W2l{- zh7(t}UM6yXU!UpMosY@4VfpEG>K1gdb*JfSNrf|2p>?>JAi1-8)|zrDucdxP-s#lZ z*eU%1JA++tsQj6!Mks?epbXl9GH46Mq}bC_%F*ZFl26Kjx$NuHsKRHK5DB{vQm}qo zni0zn`?9-9&FRnf<^$(|eTcNou~e>o#mUA4FESG+j;6l-t_m5t_j_hQ#q#W_O=BqU z9}L8azitqBgZ!Ba_4%08Kb)VQS+xaydC+OP@GpfjAK!1l#RSQn)w6zfQDvEe+sI*8 zdKxPpeZi(*`0~)JgA0sM25mqYv;$?(mL(?nFDmoSc}otxGK7e4I-T0TcO~)bknF{z zL11&beTn?0iVcSnmllns0@KToKPOyaGV6|}y8Kd;oXY;ftle3Kts6OpDiWVc^lAPV z(Pm0hreAJ8CK^Qny6w-+>HEb`(~ZbNnGNF`b1^}3XZ2h?RzaC#(H?SYj!yd83+0o<5!0uX8SI-CzQVnr!nLW2io*ClkGA{!Lul9mEv; zbu5>c6H^M%XBU9DeshYRxvpSl-Zw3|m>{{cdU9xLD`!MI5L<@Wnblju>@j+>3hA5Y$6-W{pSzPmD-`f=0@V#Tcw#B!Eq zn(pIcl60j2?bD?>eJ}A8T|IZfOjSxNE+$Csteyu60m`+jGsu9P6O6t+^Jjc@_Qs*b z@Any@4BCJ)Xa~xmEfkY9;=Z!Vfj4A)mqa41!3?V1;2p%iLD`GRrD4rz-}(i>TtPh1 zAlF!G!1P+=u5X?(+I*uZ_3ftQ$#0)Bx7##iM=u&p{e5>Xk>jgeB)zRI^TP~2CY@Rq zqCAmG7D#Xck9ie8;cJcp$yu9 zGH3_Npe+=Wxt*RVyAOXujwv~gc%CwYy4P$k5#XJ@m<+AZjGi*BfNB5JvBaA5W2lp= zdSv-u-!gsHB~v?ZDanKrZcrgsjE~97r3LA(Y%{vl z_utZa&*aZ+JGL7a6C`(55A$7TWx+M4N#8n)jJJGCWVF9fAZ_o`b4DnGHlPgJfih?d z#pI7ZZpcZjGJW2ilT4aq-~ zbF#OaCR0f_T9c}4IoM-gLa?{+Cnku z<(HFqc;Ypg`Ft9&vg%BVy^}%Y?3KNk{4}d6eYSN$Q_lKRiD}_usJxE?$(s}Nus$b~ zsI6B!kSDI>X6G%V*tqSZsP?1Q5-Tp0Aj=-=%?!!O$K+P8LiC=7&FHuq-_lPO=gX`= zaxfPYBzIQNv!5cA^A`O~)`;0?-0`eJM!(8+((+xtYlJdr1InNsD1)|8Os+P{Lku&# zCJzmtP2|(gq^=o{5&d57b3RW%JA9@s$dfe%!JN`e!V8RDGrnq0zL`~ktuP>oYVt!j zGW5s%Y@K((?B9b&QDsVPAa*V&OP=%@z!)!#;q2AfRfw)~rzySY_(}Tg>%5uAZ>hML zAi1-8X1_8ha~J%b{Cf0mW96PzGhTn*G_68~S4Jp zK5>beN!6Hhig>ardofw`z6m|~Z9!ArA#;ffk4IDIx)Ws0#YI@=Mk1Bt+dgECvxQm1 zz)oxj(B@V;xRnU&Uy1xYX$UiI4Ih&>g$vVz);6W{YfjRSL-J;-21ao)L2_sHj6OVG znVNEooO2=FxO7vEj43(Vr@g%9%|IEn0cFq*ltEi4CMW%i5L1u5B8eeOh=PS?QDcW( zAbx6{y_onOXhPQ;SO{JlCEo4>FBL{C9#;_y!n6w;Tn2s3Gls=dDBt7(4p3FZ^#Bwn~a%c7ABBm?vJh)G)V!ts~ zep4so)X)KGJ4X~`pbXl8GH3_Npe-;z$jwogZd8IGBVUp1rRBsI17}fp>-(w9Q;*q8iSbFJsSD%UkxRZP!&ZHqKo#0LjJ$rJG<)q$ANI3B z$y8#M14Q3@waB{%!kN#S@-ZoVyfEFSSX27W`V(}S`gt;2G>PY8g5=KXX?K5~(r3t1 z@@mfu#$6qJGbZdEo>s12Ne0TG4Jd&e=Lm=)5HggSp2QgsR18YJ1+!+Gz~&WVnvibv?ku2ZYAxAup#{o5u{qbHmol%b8u zKWfA=U(MoUvgt|@+SIfO9T;_-F8f)o%vynyxR@ZhvwA8w*{m$l!JDeF?y2$jk}Wd6 zxxX;2>Du}XltCL%2JJu@w1r}FvwU?T^2!Uc+l_6+chhH6H=8{mw#Q{7CZO&N>)x2A zYZo!Cez=ins2fEE^&LPq-Cdha`NKf59g@hOb+y=wH#Ka=AD|x!Jxg3_*@CRMThCnX z!N=rw^P=>%zZ%nDz50eOe#|>_YUorhCP?nAo(lVSD_ZuYrCy+sH8n9J-4Q!{%iPS0LGV%IZYw~2&C?@LSD9+yBkBZWZ2REjd zm->dzzbI$srS#ccOpx4JJxP6zDEoa^h?>x-5c7CX+YI)z18H;aw`HIV+JG`>2g;x= z6q9E!8W2O5{Y}PBN+X6x&Y=o~e;{V}$VN;+-RT-1NH=Iv6t2l3dag{SqME76wYvh? zqs{fyzz37amv#KvK5dfO`27jg^%lPqy?$&@URf}f$ymt8~zjn+B>~l12UAgWIltCL%2JJu@w1r}#_|uHo5 zJF92O;UAPuYn7y~RV>e}%GE7n<+TfGW0M9jPzG&48MFgs&=!hG@}(w3%YDzu<{OR> z?_SKIHiqRUKg`NrOmf}`pp6fUno@@wi7#^}Q@Iz!kOQ~0WQP~jQ42F?kimDGvzgn* zvm39+Q(oQf6XnfBEbx0;&O)IKltCL%2JJu@w1r~w>{tt;AN8D^+wug_hnh=OYMGC0 zR4;omS-LEMKGmt1sl-GRapOP|<+FVxIrJ-%y{#Qd={C(K(~c|IdcKp`gYogyCG`_x z;EaA``gLa?{+Cnjzx|<+wee;ZLaN#tOXUbgaZIOcH?r(N+K3aw) zmLGP;C*bzORbG^=!nHs3fYoF`lfvrZwB(LL8-?J&)Y<1g-2nRO<|DM8`d|u8Ud6=($(_|x_TCd^`hrT-(@_nW z(}M|EI9lEQRehqs`d54{#012x zX`TQ&*{isz#oiOdoU%#OZ8n))xU?NhHjJaj$1Nmtjt*vLRhiEIy4XN{wd*}mF?<-= z_rPT4$7Xy?R+lb8_gWi3t7;#i153U))nB%jiwTlDt0z~1cS^?c(*fKc|sfg>2iaM zgKJ)=iT6hnDeb@0P#zxD?qcys)6}{%LwRh;LF>`a1ciIgnlk-etDz4*W((6h#?PE+$CstezE*@)0S2)uO8BZNu!I9FZ}qQMvS=5@#?_25mqY zv;$?(7K+J}&Rq%9sVC&~R=*Ju%jZ$yy~>b(_-8LBC+qmrH;a~lYe9+96%(m*>;&?8 zQZKek(^%?4;gw|ZXWiM9;qzEkGd;C(cRn&PNK0xr#XW&0Ci$n$(GjnK3-$L-T6swSJk)KpC_FWzY_kL0c#$ss=rY zb?u&z>u%g2e!n`8s#mo#eB&fC zM|3}S?bI0R;+@sx(#+oM{PPRgJ*(aO(ogkY%s?5m0cFq*ltEi4CYuZO zCEl=)N!`>t#9qaGs?*(y-e&Da=43t3|PzLQl8MK9B0&W~4&NY5a zsvG=8{2n@=%KW+t+4RkJ=ko;g#To4z(XT6)H0@}Ajp$!2fm#?bjl9tHGxnS!ni~E2 zmt@I4gW1hbe^H-e4cKAZ}twvZPthq4M{C0plY z9A#Qjj`S)ziG16CHgjfg0+*MU@06l_L9X4}#?rOVJu~HPe~60-k~^zs`NDcca71&e z$H!f z9{*U8OqnxpsM=(;H~0)>n6u(S3)rdoQhJ`ybI#HOo{bcNLjV z)>=4+Y3a?!#Ajt`y4=o&^g9DXdry60`f5-H7ZW6RR?pW9ni9Q7lhnl`WkFI{T z6ubw4h-_@27WA7>w!AZveHax*?c216ELbF#-B)!zYb3N(=YiG9sdHzN6UxkGZfykT z$>4hN;^(F5lc5c1`UfLDH2kq?KpC_HWzZIiNx?f>;_t(MkzH2jBFkJ~K;`M!kbFEkdoj5@!H=%ow6v+lFRzF> z=k-+U!VAfubMdTlT_km4Z$g;H9<@Xrphju&UAk?CF5n{`1I7HM;R!CHlPgJ zfih?d#bn!FJ<+_*Uu5*lJmk8n3#mDI0?3L*vlkOnQ$PCgoYG*e#XF+j2tBo&Swybh zo6H)1jG%PQ50bm${{cdO|yQBu-6jPYt^_n`!dPw2TW^r>6f@?GyuL&<2!2J5UB~p_rUX zNd#-2?~~Jd=Lc)`7gGHWHzwDt+rs&1X<98m?6$d7DPAteMO<|9LYz@QWDgLknMWROy9evu5*{eKYx(JnMsov6Nj7BHue%*be(G$)-|dVzs5XamZi9VmmgP)vrj z8%?yjbB|oHvmm+e^M%yVG0n)7z-+_>jM=s*{d_Um$S{wYSC@~;k%49D6AeM05NY(R8~06`&(3l& zL2_sH%x%?=xG|wCb^Y`brtFqk84uepPOn`15(8z>29!ZNPzG(GnDi}}O5|^Qk6d4< z2&ufdkXqBYC3*1Z=4|DOs%Haw(&uGN2dm^JPy6er&o(V1M{J+K9taDkR*p51jg{lr z?Zgf?erhze>3RTps^aHl!kT%^{$qO1Ui6-_^wR4MXz%%l=wFZBGnMLpfr|-}JFBNF zxZiNlm>$&MeO55#=gi4C-gRaAucv=ypbXl8GH3_Npe+=WpCZN+{np$iR}U{nmMyx7 zs&G$9cJG_Lm@LTEfHpoV1J>l^B|CpJlB!9qATw7^V)xAoqk>x=AwMrNfxR5Hiyif1 z1eKy}N={s}oXmM>9#d-=ACm_K%F)diH=ui7KS;OSeAo1%%SA3GNbanjVavjZ0{Y%m z%KbIWciMRw`Wfrf|C)J|fih?V%Ag%6gSJpiwzr!|_`LX&oOZGV8QgIZb$Sg&wz;{< z`8)x2XUPFy`b>wirY_C$lkEqNq(+@vNw%0dh26F%lB7)ky%k?k~h5Br091odE9 z3vybGRpd$2Jm%_49cQm&SUGxI`v!Df|AX}U*?*dH(?4-BL2_sH?9dD+?nd;ZA{Tzi zwC%niHcB&87PA`pbXl9GH46M#HZF|VqBL$$zgR%lSd{jqIUOcO)fHIFD9|W zedz;h%ffr#NYmXoYVg3-)Zwis$Z9(#voCh-WoLdjoVxp33Dz8}A&>3{ zF`3K9WK4QF`q?XAdd}DbbQS#_Q?o{wxtJiivwEua8c96rKag6}aWiA^U6hf!bx-=j zUQZb)gEpWH+JQ1?3&o^O&Z$Jmnmc6m8D+^u>5Hfn-fhV?-r0x==$pz__N7O?ENl9$ zdtvh6s5t8Il{Mrz^$d2-^I_DKVW-HJqo=Z$&g^H`62qy85Sr}b`vv*ua!_|_@iCcJ zvOHa6n=d{7_9Mb9B)HD+Vv$F5HpY2dvGLY zZ^Vf5^xL7nbnc@2>He*5nJ(r1or?*QJFBPEh*YADe+aef#vZ2l?@KfK?mnEp$>_yG z8MFaq&<>PATPP-N56&PC5x2U&S4XLL#Vr%=gF5vXR=jZrLk{|XsC?=?a7@@Hj+yl%wuMB;$u?hNO_tm z>PuJdvyZM^@208A!|Pm3kla~4Uw55Ij42;Vg&#k_Ox(XL<5j^^>EwW1ER;bTPzLQl z8MK9BQhoj`qRI4Ic8h%dlPIqo|(iI+0`2Hj~A#%w;xQj^pg5SExYu%&bqJTC$hU zRq7AZtnY7fF+p-?^)Qd85MRF!rz)ixnY0-zGAe)Zefpjcd08lfHlPgJfih?d#bid* z9D?}nCRw^nHS+D!#nk0}-AT4tHev$i9&_KSN5AY_-jrv2X>!-rSSon)S7f=O3)nIJ zhf+<;{YbW3HHW=ZEQ2k2Ig$!4*^TT(Z6lW@&1Ldw_?XnuRiF>)>eD5D`kF5C;kxPK z-aCArnB~suS+#BkarO5|>Uer4b8yJ2j7f$o=`Z&bWT6b&fHG(Y%AhS2lTqY6;=6J; zNosTra_E`GRL0O=WcMRqWh+lEEUiaB-&P*1@heLPHIAk77TQSGc(IUO5H*CVlyQkH zrJBdal+I*35s{RBT2Hd@(H&$x@43v*=UUF*h?5oQXEo~6PfLAGx4L)DOL9zhTDvuE}^b@n(81T~QXwpbaR4cAyN}LNU2lVgd1d$RA|uYqiKi zIhRn!z5lPeGk?n|{U3N_nd~2}Q)%_I8OvC*p7(u^Eeyt1Dy2O_meHiKMiJRc+EvPu zC?##`IrnoES`euyDTy#4`!F$lp7+z|`~gqb_5I=d>+6U2>$+a&KBunpoO2&rm^Qx) zP2|^;^|P&LxvH)p-y#|27x@zFm1kf=QzX0Ozl<1pG{Ae4BdnWd3G1HZO{!FEA%E5d z;I|y&;{!XJgc#{j)mkgMaX~gss%sVou5DK|5xKH+(uW@5TH}4m*%#M&t&Za|gR1tT zvMK5e89I=m2N}A=CUvb5Tx#ND(4S=ieXOHMd+Jh%t2^1Hb@HUclKMO9;yc&ylKPSg zJ`EOKiDn;mI+95}n&4qe1dAVdl}-NcMSk0A50h_Rgn2Co`5S7TO*U`RqpiEGXvDq? z^rzB?!s{7t6-`90?406HkzA&q{66Q8Rs80wDKeSU=c0+~+6);wkf8?|y5#S>D(+E3 zek9k{*aGF5R$%87MMlqY1Y7s6HTfysl6GY23d6I9facXT-0+^O}TM-4BW;B3E|K z=DV@n%sCs$PP@B&=Ao0a+v?q`^gP_zU^rl z`LUO@t;K*LLkBYSAVZhf#Mw5MGqZgJM|R4eRlONSdhK(8j*VStBLBb6dVnPjb=DIu zR~WEQuvReiO7|mlbDyx<-7!gEO(5Q=gxL*N0pS7A?t2z(TMMTVN zdJbWgajd@d28-O_LALJohFcGK7+e<0PZ-kKB&SNB=Gw@++MTC64>ky|gTE@8h+Nq@ z?fIv;P5E2M0MDoV{Khod(cOcj=bFtJGIStA4>EL#O$=TgXi`qwsa2|mVXS|v-aa3__6S3!-7 z01ImN@zR7fieu1dK7$(HbRa_yGIWVe!txTi`j1U8tk@dnxF8cCkhRtE# z^i|~2mGuoDS_m+%Ugb-Q-*Ajzn*kZzmW_XI*_3U8M?$K zPTP-jF=kB=K4K!c9Ev9KuYEv0ELr(;M}9ql+vc=1M_+jM#S)rjKIFJ#HWa=)$?A73 zCf?d_AnUIrR^W7pB?Y*VngL~(w+qya-Xk~Xr z6Ok)BXPinlM^twa-S>aF6;G89I=m2N}A=CflZ`aBr71LSFo2VA5#f z5xfQxHM`biaiBRJ)n^Fq^#Oe*`H+gu7a{)kX*S$%5qX{T4$Nbd89$QSj?ANHr?2jgf9(%``#5`v|u;C_RuPY@7Z<( zIwa4WCYqe3m)-6PPfU9%nuuK4ITb4lxfEG2(KPAC-h^h$`ZxU~y^=hcAwvf;^dLi* z*yQZ>Q{3-o8(?ez1$*rn(*Mi`cxTY1CTG7qG^6`+hRB~S9|b>Itt9tO=Yrn8v+VaJ z3&^jjpCIb(Nj9zI9!uHiLL6Uj0i9S;8T*6UneT^uhbqkqXB9L!hqqU!Q%J|lAWCgxgP1vhyF$en03IexYMj)cRee- zvYedVw*!*AZ^5MGUGn?rJ_?_|r6HZIVNMr%o}s=DHNuFx-ijt7S9Z=n{}yu}{6a{< z(_XCkL$1unY@*bTo6eA-0~vadp-XJ?_SZDd=kIzruyh7!uZtn&CI0YU(uF4SbLS(N zQCMz(v!O&|1!4L5Fh>5{MWW?=vMu>5jQ!;d`!KbEg=s7&6C-wlXKNLNjo8WOzFDbg z61d%v8kCsP8#_~}&C+TiX{4H>iO7|mW9CrGy|}TLY^zpdMW^#+H9Ls(eEcki3?0bO zgA83_li$_Oan(BYFtyAE9>>Oz)cJox)R{!(*NMEz&+QW0kZmBO%&>;QPl+yV&K z%wn2@=8^Cpze8$k8hh9AfLVDslRNG~VDC`_fs1$Wi#L7LE z9QafcLxS#YfmEw5G?AY>Qxhcgx88>GcMv8(<3C=+IpY%CczJX?3gL0_MOIrghZKMBMheU`nBlT!#(!N#_Vx^cV@~zp zULC+YPU~!Pa;p)Isg%&?H%?LwhugvntNw~6B3E|K{PY^`R`~%kv1Bm&H&ZH`>9tro z<+CkAh7M%tL541|$*A#JT=Ryz;A`+3eAJ93ksAU*V|dq^Jn0xp3o{Mn?`2GZz4e|X zBBThaqH~$~#n~jyT9wSbdY;*+wy?`K%SdFM{Q19o4e-?I556SJTX76}R~b=ZtAvI= zIza=RZVB%;4^T7_xw3QA#?*7}x`)V)>zXXXfyq)AI7$cYa%9NRfeby!&?Pq6`04^z z7g`JD;R~UOizT14cf#)($CO_u@^j~pw4pRx)krA!2XL_QBu=ZOaCCJ(D?c=g+*#d& z>~qax2B#mhgm6bvlpYG*7BzvScmuy)zq83N5@X7&B=p<+6l&vEDQJ`rR5TH}vU7G_ zY2*g^942A+b(noOLFVD+CQa0GVaU*d3_ZxuB{sRAe39FfTmz*&mq357SYrErmwYqz zE;Nx}V;hzZrGZP0kY{-P9o@C8G7g)-)R%U&01U1- zgNf=|-ffSU;utLa)0l?U4W%=Cq|o3MH-&kc|4}p%xw3Q4xVCV$xnacXjUMZsdRcZS zdZn}`*MlKL2Qu^^LzmdZZAmW2a;w2}vmNZ(A4|@S4u-(=_%5vztzJW^P~Aw;~$yHUkr*LN?GB5W{`B1 zA4s2}xh!GrbGCVDH(pBXN^SisG@R|V_6mcf*} zv1H2mJz$p>r~EpR|Gv}D-ITsvXe_)*oeB1D-N>RW8OY8vcCBTae18}BA)ps^9WavPK9%Seen*_NQa#2riLtTS21a`y{|F#flH|tUptFnQn zROghj{GFy*5N_{A)~^>Jc&ot1Uzkc-9s7`u=><$T_yt@1&W^lnj(~e7+TiIiXTDy) zv&qX+6FTCMDgAq7BJF*+T(~e=OVLE+%Fekx=_9wbHj?CpTCgU|GFeswI0$Q9HK;>bzKKKODXwoB{8kvE~5pN)keVGe9#t|Y?pG7Okq z%m!`yg-qSumt3imUvGQ8Vi(5Qk$y#yP_6X}+AJJ+$#!>z&q~jf4x4OBqsJxC_+w>) z)*sr6CL&jMj&I)%&QB+rJi0uLWnC?o9rX*5o?f?wAwvf;^dLi**kpVT&yD|F3CkPqq(E`y`S6=pk=l9G&mMBS-~eGO`7{Q_)> zdQ>#5-TNBuge~C<8r>94xV5JA+cOhtIPnH7#4 z;c{K(6TDwqViw4dp#vFukfBR#qPf|`dtM@#WN?=(@{`BVM#^|y#&F47{G|Q&hE~#qqD)8SzyuAHD^Ukpn{ll z&Zjb%@y>9kXAYP>Bj~B9cj&*WyT1;vyQ;c(c9-E#?N6zBUG=_t^;PJq>NKRRtgI{h zzdQS%|8-;k539~3bGfo%gUYJ%X6O~hDRNDr6uBmc;^|@s398Slj0^Ko)XD-BwXy(ZhtPKA z6-MnVX*$>&BmC{9s0L%(h?k-ojDfU@-@y0o<50*b8Q0!n;gl{HFfSQbW6NeiKwhWG1HiZej175mllcQ_nCr{Ni|H$*NsePQ`NkJO^GonTw+WL zm#Cm*D^1V&d=`=7^;txU*JldKlynU`OjQ=3nEK-7V(Rlz*s?`FeKE(iAjRt(>CoqP zb+2My|>W4#@kLDLf6sgz3DN?V6Q3l5sHn?AG zVT1d{7B(clup#k<4T&c#cH^&N{YC06-VCr_Y6Ankud#$tE|-uZE|-8J8~uco*urF^pCA|6=qIGa7A710LUWno zn;ALnG1$i3+MgmFWCXd0E7I4-lJPHd5Jl`Ot1up6M3IT&BrheKZJfe(c=O40M&>dR zMWRkbk*Et&CZ}_aRmVz{2`OS_LW)?KhvH8BL1#?kv^?652=476WQrVQoY^KJ(enx8~dD|}wqbTY#jG~Yhq{t=~ zX7z_rVhfW^ECjj8CKf_UY+laY3lohS!d%4ThJ+F>OVVJ(#apU~6mO{n6uh)SqIf8zc%^s=5{g$!G>Ux{AuKO} zuv}BBx;c}~j%ZBP)~2h|DSmII{U@Nn{=RP_6piB37meccMUXNk-H?hozqRsk33CyT zM-qypB58#2SEYEuC&)!M;geD%(t;kda*0)yvVl?{42Lk^%z|@*!jKXum6{SLRgfYZ z^#o)s#T7#T40aY_fvFV4w`tWJpy4C0Hb7F2N%CDCVHA zyeC3L3F;w-`OD(TC8$S1i9RMfxkMk6J(SqOqL0Z=F44zi4<)9sIAgM%i~Oo!XjR4; zlg(V>4SgmB$FV?%2{ucl1RAKC5^#5rBAX)y`@UJ0c%!9F2}p6eiYS*@qotLLWO=N} zMJO-mv3R4UDGY|bK;?%tOP~ZQUrh-XiOP~mR;4o1)wEMNVU(a2CprHSMhR+BP^2Se zcrMbBQbdU@Ogd7A=OP^`MU5AuJa2E zp#&9*D1#=Ji%!iV3M)%o3d_YhHJi2P`$v*XT#A#6|7LcXlS3%vZjrDM3b|WEVIISo z37yYkP*lP~a)}^JG?|eI6HR8M6q4ereCWt-OJ+_EyZv8Ll|&&=QC=cpo?K9#h{Bvq zxlk3Va`8~23iD8+3S(t~`H!PdRF)vc(HEWK=o3*`S>g%{Qk24i6oD}3e&ncf&9WJ5$EZh0VxhH_rQWX{e0wHjW@gQVOd)L5kx{x4`~iNNG{r2*=n#N^D^* zsvRZFMKs#^3gZ_h8ttSMrN08gDw9=wn&7jF0EC$oMNUYu+OjmCaEde^KSjCPy? z5ibpRi*He4wXuko2E4h5X4K9Q5G=lEN*RL^D3v;wfWw0n3r>h8hB@KPfqR>wWs~4gKJVn$HVfL6TlozDL8!b(b z!O#~hQZN^w1dF7k1dBwJ*p~)cD!r7T7V@vJD!r7T76nB*QgUvvEYguuM2Rg-I#Pz` zA{{A3l$gR4BV|}Fijgv$qL3A+7iXV8*yec#<{wn0Ly@#I5k(>|oFZ*!B1&vw5iSil zT`c;gfgmNeun0F$Q7-OQC)>FatxEhJ3!wxRDk;G_6;asspy<>rqOh{WrLbJA^DMPr zHqc1we(I=v? zvcwe@q$q_2DFR^`y6&#tw5Q?1r8qLEWx1ih_(YLTu)-+p@fmjj2#bEr!YMw@U}e`vFc+(nNlWCXD3_tUKo*6)Kr!y)3_*&dJ&SUYlqQ@K zp(CTP2pu^{i7iaJ>I*N6bk!%K#1y7j^@Zi4SoMWdB(lQVm}qqXUCdiA5wBSUDH3VH zvWTYo5@DjbZVXDG=LCgeE`d@7Dat`Mj1rLI6vO-xLWy?G!dovir4;5R&LvQ6k{6^X zhrb|2G;^a&LPkrV1dF81C0HaMWlW4~7PgZ;l%SsQvUn&#Jqk+nG1*WoQqIikP>gSG=;&?7pQ#yXcX^h9Vt0CSQhC>DWb#{CLJlmbCHgeB1%kQijgub7sW^! zPEp7T)C(=&)Vm7P=(?Cgk+d_YeTO2E7fzA3GZ7`Wun5;Iu=vt;7A{QM&P0^R9&;~1 z3{hRPpt6Keg0Axm3!wxRiYRQ~Qgmt-QP}oiTna0GTnZCroy~ct^8ZPM#icM|aVd^4 z(Up;4RfbTgQblD6p-`oYD6C#^{zGS~7!;MTkX%&494ANjSO`TWEQF#G#>yg^%Sk*Y zn#)NkBqdr)W@U*h%%qTyV&oD}7;BcazNWTCd5Mbep`g476qF~T@TwHdej`w#3iIR= zRT!_An8G}isKPvysKQvi!0gwlQIW@j6h~ilila|NVNQ-KEJ#rb3sRKAn3LlQ3sRKA zf)s(UO!TX7bV_n5jtuI)Zs@NUqDYqtVH7q^j6Q?4a$ys=_>>sJVq8iikHxr@=A}4Q z=DOAY%kQy<%E*_}+*~4GO7l`;3KN}$e1-9sMRaA+|ql*0yQpBxIND-Hc zD9S5)t~5VItj1+HWwCM*tMO1gZ<{x-ajV?LsNoc;z;KFGpf$oIr>1kJr0CDRdMQ#- z+zY{6q@rAk_a)UzpINyCDH3@iibS4|GA>f%7gEH^gcPwdqBJ%))F^$$T}<^dDeF?i ziY$g*m0pTikxjv7S#EZLG5nHZeo7;MU3scz7{BwwPS1h>rH-WJ~xQr5{M3Uut zEZHzAlgd@*^2w~~s}3a0N0HZPC`GQ2C_F5WHkbt{a#7r80g7By2qjAUhg_lzW&uiU zVNu#YD z3Qe4@y!Gn67<_xUIXTV+vvCQ?Wdmq$numq$R6b!Z_awlLWVO^}PMLklUfg~^^V z3(ZA-LK9HLvS3W61|xqVtS=YedCA$N$4)1v2(^hR!baw!(3R(r>12H>pUR@7kU?pZ z`n$c1<;$f$+$vT`B&d zvY1~89iL4$XJ>V(3Ym4gtasUC9I_> zC}AU;f?{6f^;e6yD#PqsTwyL@_M=eThU}RI$a8Tio;Dhl;%Nj#39B>k6hWe-8yZsC zy@jI+CFh}!a#*@1U+0Yt{Y{jxmJv#Da}ke00ir*bP^ThCMDaLPM2R-zG1-($eHx7k zu|sJTCcy}zhzCbxI8_^_Ig zgv}dIE=UoTNkS2oNk|cI-bg61g^4$Bgt>?}ZzPo1!o-_5f?Q;qH$sX?maxIdHg7z+ zMBBLW<>I4=b@?u&darbd8W`)!USa&ItmksA=wuL}h!UD31r8VP{)l|Q@Z{oBBxU!P z#idBf?x(0rVU@=tY>GKtB$c}tLW;ONLW;ON9?G=%!u*s-!u%AmFza3geh-OiU&!(> z``4w6Ni|H0FykjmO{zB8oXMMa7$|eIav=)Sr_Rhr8P2XkC^NCkV&^idF}}_i>y{KYWyZ8`BN>c*0CdJYi!i^Np3tm5fi=*!n0OA7z~6 z)6-OVXfS3mgq|XjmCGc8Q9_%NO-^Rpc5#}e_tqwU+f2D^l^x8*q+}$WM?j&4M%d`A zpoEP80?HK0H8@3#0Y&Cuq*x`dZ05%$e_mlWWwInWkFqFrk?PPkQ#>Q3blkaC|ik#Lljjj38AQ3Ns!W5pKeHI)P03BEn&Ip zWF}QJhJW|bV@~kXqIkbsEgQj2BSA_=a?TS_lslNgTx4TwFc)(;j#n0w60a;KC9*K< zO%!yso_#5CqHu;m#YYNzTuxLd5rseaDQMmjil|T`3O&ac($4&p8tF|6Hr-F*!v_DZ zq<9mC(L@wUFAbxxJk)QVoNB-D!4k%9yHh_dN0b;3Ot}=>0qUopydMn_wG*d?tm1nq z5gWgkqP(sU%th3+!iAY#EM8emO1!d|l*q!cS1LO)y%W;y`^t_Sq?jE!yg8GTLxhP6 zC88)hG6{>(k%?lr<0$=wC^4QEbSYLo;jZ$HFy0r|MQmeUO2jtirDRpFceU!pOA*x| z^cS0Aw&ZwaF)8uNVp1Xt3u%@jg;ac`DEkXN_~~{r_5^p_T+A*OK1mY^V-%4eL=(VS&+D3?%5xH2gvTv>o3bym1AixMtLP<)FLF3F;Ju34DR33@I+9p9X6 z$cNnO7@&v}S~>84J(N&C2imck65${4xEK@~lOs{6 zSK60%YtvIxH6dcWhg?d!-n^OD8yWtaC_F5WmK9KZ3I0 zQU^7&lS;OMU8%~6I6ex$^GNM8ABEo(6P*&R7^YUrC2-?(vO(lMqC_jXFPCT~_fevi z+@yF$m}t$#H(H7dY#Mj|@#GTK&mKxtr+FxJv0hdG9tvIHR#B`G#wvL#!idD202F?O zz;TV>hmUetV>Y8?P%iA!!??P}DP#C9tgvs{xmvl@WYd#V?3sad!~Uu2I##mk+>nt1 zU3M<1hGbPHHB8vveQav{JS=xCAKhs4~>uu_RvTauPnDEOKKl`s4|yePmM&0DU9dhZDGo?RE3U+yNWx+@ex6Ir#&1!)6&b0)#BBUk z<+XtsnzHz{fpb9winkj1iAhO`-7BlutyFlLij<3AnvxQH!At~-zak?PKM#Y2gkn9C z88(*{3iDF@(jrkJdd$tmKUhR6i+`|)NQo`XKUhS{#Xnd?q{I}4gGGc~g0ERpRwWJ= z%3Q+k@KEFucI944@p~&UTqsv==FHcZi$n3Zb~B;-PdFv0#%1`&F*_G*?VjZPXIL)S z+9Oe73&YkPAs1}zkti{R1zWo^mtbp;K#|G{)=TgV1bKv0q%AC*5=&UvWI`bZ$LPf>Q%_=D$aN)ZUX6onw-diMIv=lB$FY~r*~io7mEDRPA&6vb!}CQLC}gi~S* zQ;Zg2xhO`9a7s*J%F!a2i*mFGqsV3X8jN*`Gi(M%4blodweO|KTA6?%6BkO6w=w}G zwlM!*g}?9ukI7qEs4#ge6HsD$Ox4Ola#6LiP>M`eh{w!kW8M}eYHfjBkP@s^B_$|Q zK?!SZN=j^DVXaM(OIT}DQeq49Zw?6hzPDb&Tboo^cxzKof)y!kW7Um~*&6m_0NxWM z$;C_Y`Yj^GD^NlSTZnKsODSGC?$9Kqc;!eaA~{k@xEv`ZT#k%V$)C~*Z1K2^pvSTn zMWQa?F`FV$=b?yq_C2cDPa#FDOh^$c6Hvm&FOnyu#1BKv=BC&vS{{_$`XKA_cV{v+-NI;j+6gLW-|ilnW^0ilqBt z0#9&POTIK?-Kip=g!@fO372P4>}L|q8MRE4**!&+a7|K5xF#RPx})3NzJN>M{;`4~ z66U8!WQiyeS>Y7fSRoK58!LpA*urFEg&-H%SRtgu5avEJ!k*?3odNt+8A1uxs*)1a zsGx+^s*)00SlIMOkxN*uDk-srg`ELJxv-~f;#Q?Yi7w2cM6XJl5~nI1N^D_qs?yFS zPE|UT*uvserP(XPPv)Z9w<$qcI9ftS4nLWTaxse^Pnb=KBh01*TbQ&gqLZemEX-pT zB`8fv2}+Yt!Y(|rQ5?>dzIqWoH7UtO^l~u~g*jPHv7dU5Ga(65g1+S?H~%bQK}yiK z#IQX^4^70FkRU}Oi|Pd_5?SGtu<_0*iHH(gSlD={$R%vNQ&M6J3mfl*x%e8t{47TG z5<&@Tq&8+vM?)yVT2)ZOYE?;zEi9~76}g1fs*)00SXix!axsVT@ZAL^CAu&tm*`b# zQ{q&mLy0XcPF32u#HmV$5?ffDs#H!$1ido+(txP;?OcKtiKC_4k&HYMmU zWiD}qIkSu4#)qW%@(D?>EKV*#X_8z-7YU@4U=DmVmrzu*3mlVKy~ruv7e0t4d(Iig z5*DO{Rl7|RQNl}Zl8EBp?g&=7Ba1vnPjS*5Mj}f@k;n?CgpGF!VUc!7{ACFn?-aR& zjdw~)Y>$PFcfwrU5yl*pWM?rdeh4L~(cfS~D8X7)P{L|eNr^2itX37dgw?8&5?feU zt%`E7&y|BhUVNdXL>K1d61^&IN}Q^6D6xgbsY*MSI92ITVhf8?mF!(N@?}D>S2~pF z!W>HUsEB}^O%6Hvm$c`k5UH|V-I>4~H%DM4vUN>EyWB9i0Itp${z93>?v z$E8@kl79ojY*>{ovYX8;T96{sWO9NOnWj+6wD`iN1s-&C%WeNcVbi2{0(pedYm3zN z$VgT$eu~0pVHAbWEQ+(;Asr`t#rIPp3G-7T33CS6=)(MzNW%OSg|I*ubB7Cl-{r9U z0onAVwC|l!{PXE&u>ZGN#d6`AET~E4sO=Ufx{bbTr@A&2kcvv~U7 z6~_YyHnL9xv)5-OCa0?NjoCzPwy{3ZXf~}xV=aU1qcD}pM7}X_u&Q4oo60q2CZ}o= z=>{j|g!=f6N{gTdw@0ETmF<(YKE>3xUm{(eD(}bYCev8WUTer}1nZ_MF(sX^<5j^v zV3QB4sHCW(1_=7U7D3f9vbnyVc|FVA%LmTMsRVnw85={hzACXgNw6a9&J*VTs>FnB zvPM(^l)V*d5q^JYR3aYWLsfeZ-F*`i>dSNegWCd+#%KSBp8b6ILlUHQx@UzbW$H?ptH+f!jS_qFF+{&Tp8=DIq{ zFD-(+G^~m(9h&p92~{#|eL?(o3AG4vqvHwhQX_Q!>S_B4=X5i$Z(p)hM5slO^W4{3 z^YJIuz+Q^Y*XWH^$MQ9vxkmS_%xV46r^Z~aOipH+Q*7y2n_zFbr?+D#lG#+Ep)sFe zZ$Yj}By$P&GI#dqwXE8-h?1%fr*|Z)t5ca&)@~B+9*uW5CZw0Lx2E>xlePgl^|XNs z9tgDva!G{V;jIKVyPzK1j@?v?D5>fg(VXvZe#O8ybHN$qgt75=NU4|+LM?)v)Iqat zHipKiKrMo*gEvfeiGh6+O3^*aQPgK@Wg%q)XAgeJ$ErJ~8uE2y>B11RM+O;of5yI;5olZS>Pkc?;wBgQFeVIB_ zW1VN?c|y;uyuD+Y@7_6k+ikux)x77exJ$HJ1ht6>cbw7H4d-HcRiYhldE-K~h?1%f zJ+q?f(4NqqAP-Qe+eAru!a0?PJ<09aobg|R@kF%8jTBl0wF&FSp2_wL56r1r@%KZJ z@u;B|L7oa-i@KMe%y-b(iz+s~vtT}%&iH$aH(AWNZhQp3s}AR2?PZ3B5@| z)lm|j2;EH8cNfvCt~=WkdR6Z29m{-Gu21Du9oiGx6XXF3b(<(DPn5*;PK%&6p&w)} zDNhWmsWHD~!k(_mrKhD5HOYK3!7c?Qa*g~o&b8*-ovnv<^DRxO9Eoq&(0yoCKhkZ! zM0?pj+#b64ac^gV$-f)neS+J6Ym1i>@zxeCg1VT(6JD?LSFm-=6ZQwEYBSbb7Wkl$ ztgqtlS59Qolj`#Ph1LUy+P~8|_AdDPrc5fI%Csbs?0x0gs#J~ag~?RR2%$Sptmw~` zmxL$uM3>sE_JsBXd4NLQCQ8Z^dId(+Q5v4m>khj<+*A3AzW7m0biIoRHqX`T4*r!< z|EDXxYk{IyV0y|=Bc|I$SM+sl!Pbrl^?1OcY3E+U^(+@Ap20e{V~_Zde1^jufWoi*rhpk0mQq|>bQ$8 ziUsisBtox6gM*b`i|V(>TCGB_JE(VPPiRk&2PiCwHlf#|R2`+^34Qs=t`EKVp@ylL zZR^DkEq+SE6D!c-N6|1l#}j%5M&pD#V(Jx`UV&Myq9o>+S_E}b?FsD(@&JXUvF^}U z1F1fAs6C)PpgpiWc%URM*lQ8gR7&F3g_004Ce<)0U&pUVPfq8W*+abcWkzuffmpL$E#Wr4V6>-v42|nB~s;+%KIfwm@u&4z}*J*8#J+B zg8dxCeh%b62eYGzA?)y9%n#dW5hdjbw|CT}v+iqJruI)xWiMo5jiNVe6RBKptN#6u zn)uH169%>Yjs98$by0yQT1ufhRzMMoiLPFE&_uU$JfXjgM&pE{Vfsd9_QnJLIeT|5 z9(hQwmk;SBA)+MiF4A`mP{Y)o(4HUPwRPwZ>NeP|UVFX9cDZf$NTrd84ovK_g~KEPmXs(Lh6=S+JyFm_5^u=Lfs}x$`g9=L)B3do**$g)E>|t&>kph50u0Odo6<6NJ;G8m4t|r zc&@x8M7Rs$6@4F5dxFLZ?FsUP-rHWDJfZitJ9lqe(K~c?bZ&LH!}c=V+twmz#MGWB zDNpF<%1i3Ga$Oy|I`GvG?ue=1H9$8Cm!2n9z{mUr4YPASp_py!TbyXN-8r7n?;4=# zouXmlGEl#3Ai>_8Q{7nKlu6}NnU+M7y}3PGm8$7W4lOR!_fL@jv?sJD$O9DWHc?WZ zD2aP&vn)mAi`{eTA4-<;&0$irIGOEPj>;5xDNq?;6M^>r)-~Ze{(X z3!OXM5pzYq5T|F`)F!kiv?s^|6zVonQl8MuK&p5ZzNljx_nDhDo5hmHFQtq%W%rCMNpkC59`8)DvCur1pgN1bKi$ z-6qHrrLpc{w_{|Q^C|XWt4Yn7WHvE5#fHXozJ;$jJcFeFTSuOV#Dr|JWgnXU8ey_pG$ zPk0f(T|zB_-01F4=xyZ^xTi)}hi;hU0Sa}SAW!IvF4`010Sa}Si0BD@4XksofhiiM zt`1!t*3~cfbH^)Cn{X#JEBbX&?Fl-8X-{ZRkOwF%i8i5M5u;xbgRh9uZGzf_+eJ(7 z6){u$C#SM^9I)Ny-kEemD%ab(t;m1H+&Ar-FsSX9%S+i4o%@OyfhSf#5sGCX-1-*2 z7VtM$FD2qt6D@+~6Z&O2XNxceoAAaE|sjwC6cxIR5p=HR4to|VNxR9V7;k>zkJNOR1yDO#~@;I%DQhjmvA!5H%>`rnHK*GrtO67^IY>j z4XJEColN9Y4eXuGe88w`tZ8BYOifHlv-|6_saz8i#$U2looe7NVM*$fxm>?QQ!<;( zWKtO(XNsvzCMFy=sl0x0+$$ei1f4~-C$uNX0~G2uL7vdhm1|Fs2Po8SLVJQdKw(L= ziIRBXffhk+VxI=~?mhOH=;UOkIc46-!`_CGVZYeyHQSg^ve#pHZ-_`*>$Kjv)bY)! zhU!#hE}zWidnb}p8HKF_JX2owZj7iCUM<3{4&SNlt_-vCMbcTGB_yEq~SmxhSJ%ia@1O2HFD`jvzyhAgw{IH;LT zp0W-6WsK~`KGsjH7c)9H{x#P7W>%i5Q!>wbk~J;Mr;B?@qZUDfRY`cFBvxQr1hon6 z3GE5;0EN0ukSFveNlAG^Z<0`Tl!PZnrrC9F`@(N^3w!#lhFt`X_`DDmr08%o-BeUv zoyw%L)(n$fn{LcZPSx-W^4`6>3H1YVv9Cc#khnj)?9EqyS@y<>8tXbaTlq!Z`-_l4 zlq3f>vP<>mPNunknPB^pdA219Q)%;!y&b1ai=e8qA5g5#vQ-Y<1i@A~c&~)FsZz4` z>qA-uRYyrYg5@1CtvlX2eY`AVyLZ;Yn%&ehiCyr>`7e9~UIR{z(%VpdV``h;0Io$) z!z54mE%tu20sK zkE=^IrQ+X(xOXZ&sV>j0T3;S(zq1ngIzs5aM|T;Du+O4wGKsISb-d$$fS$(8VqW!t z&e0;sBZ5hd<@73kW2!UxcPc{hlsK7=9%>QfD}g7h{%SqYFX$cZSSeZrRfp%?vFyGj z>7F}Q^mnkA#Jc0Kbj>nif>Mt72`ZA(uvavErJHjGIy}z54S9n zX>OTEM7n|9Lt;K>lWWe_Cac*Le_&$wJ=k6*J8o-bu+E&Gv(-jBo<*H;LaM<^#Uz-m zw|y0`N^z(B%dk4!E*duFXTk(inj+rq=CnuWhZaGupbd7jMRfYkx~+ml)HP) zF(iU#&Y#0;Y|bZ4L7jQQK``6aE3o<$d(_dnkEyfPqKbE%(IU8GuRdrIG*}6G2k)_+ zwHz*^=^b8vS^-6r#JYnv*!4x1lJJBx(KXNE?Kp#`F(69lPVdZ#wEM;Yz6@l-I%A4S z<3#vcbcGT@4O3r;D+y0TxLesXv7xCa-0c#j8_LpX5#*hc@PxiIm8wH~LVJQdK%s6E zCFKcy^Eg#UNq8c3QsaNjpXL)$AMvk>eKV~VL2Y7x{%9b7?Lt$wv57tL-@@)~%T;I7 zRs04g{#vtasy4&kFJ|8FRGUcFSMdi36B+ZKq&|Bj1}5rL$p-eav3wny^I3=4cUY<$ z>zgv{rt}PZ&14mOZ&_8UMsnv* z1p5b(@7E0-tOWN5R|Q@QxMGNadvvp@47&h8_vxCb-R{*DSI3Gb!h0LNbt}60@*VSK zow;_sAWkDwNa$XsUS<-?%0{MYlg*iYqL%F|Tkk5Zp@*{Uo0i#DB8#j}@iS#(mc6ow z{m7ZG^vt!_m3yxU`)WDMHEBKd!rqI-PRo6&*k#Iw#=Q4wtoF*@svEBe#|Ql0W%KRE zY&Oxv?hED5N7>U0|8qEe$2Dm`xK+e%uvY}1!_=myvWL*TKCx~)@~L5t;uVyzE3772 z&*no#I44|$wZ&`}Q>`J_lx2SEm#Ava)v+IK5aSgA4b#+6-Po9A_pH?r^%im zutx=J&|v-3ZlilxZE--2HLi@U%r{onv+5r^pom^URYjigh_HsFpbG1_BctBYQC?r9 z5)whn3%jiamKQ_Yx~8Dg%0d#shi$7jTJ0gw2dqfDa_j+ulMZCBoXeGu;=_x%>yt5` zdzGwoLez|CY+#RwvX)LSg7-}$I_gnb5aKk3%E1NsA8+m6|?kA7JYL&-!ke-e#AoW0Hs6~*Av?s_DqwSY!)uvNy zw5z6#0?+UaF>LnhjL$>b=^a}m(^-D~*V>?=(bfBKzjxBqhZ5|On6zEP0BgsjPeWh- zAWY?p9%>QftFevsY`-?++0CwJS02*+&98#ld$?_wx{~>5VM%mRyr@UzET#? zwj#EslGv%yZGzf_t`71hNe4&+WMdYj#*x79%XvkD(k)vroo*mn0Pmf=MgMRj=BUu*-+tqHHY{T1P*sOrIP zHYG7^>o!51SXT#mq9odc?xIv3CDAbT^o}MqdOkr7vn0j|J)fZJD2Z`Gw+USx92tBExHc=9TRY{1@Gb@@|>5Vh(iIUt*)l+^Nw#TK|gLdrO8+?;J+h~3EHpg$6 zH$lq#6dZf4FFOuz9^f0BL)garB>sMa<{Y~!z@7|ga=!0nec6jL6NOp?d8X~2Xur=8 zmuV5jS4Y;oRZsU0b{}~uctZCM{%t1qz<+zbb&kDgjXkBz-iN}@9p)8c=SkTdb)b$9 z_hTRPT12rfj6pz1I`{>Hi}-S}$WkxP*}Tp>L$wzJ*gaI4T#B#uyq{(zdu->~O68}dQVs5sl+6UJGi_D8_X~K{`b6+mZK->L`j2^cob}{d z+eO9rKCU)#ad=2a|iyVKP-)a?<9d0=!8rgg=J0;DUC`;mQQM6%qqO8m#f_>6< zSu_Y%FCG!>3%ko+1b=DaGH(;f2GKa-ZOI+++fyC7VbZyyG#ch|aRRfRug8y`6n*;g zjT7vxe(WSV**k1Y&Y8hMwJobqtFJ}v9rjs5JXp6~g3l9ED$O?>E`w3ik%m)5!M~P z!PmQWydIB;OtOm2quqMoliP-fWhpM1N5qi+HR<|t_9BDoI(9Z3J+}!74&4`nvDm^0HQ#o4{ z^4T4KL2_dQYi_v&d%d1L{g+y6W2F1i@9JtcQO#Nl52k{)?Yz&Hc<&2mFL$m7k}6XXF3OQKD*^skef)QYD=g^kk{IJKPH{&BR=Ux*@cH%1o_2jn89xWB>g=iWLlMVrd36CjxsHR zs-q-EOf5oJ2YG-(-6pgr$O9BQ!#2Ahsb^N^m6m1ciO@-n_pHtC4YqCqYiOW%4)b?! zThH2yPxi59Tv`P64n1OOPml*F)NMk0f;>QBNwf)lDyQlw2~Uh@&iA)I`)RdGbc*C?m_9X8fgruGDRfI{6Sv?s^|6qZDr&}UJq zj*{?1=vfp_;QI0tf5`)Xc~6IXq9odc7C~J!w0F4kS?DKjgud-9Jt^J5pF(d}Pn5(7 zOpBo2p*^8JK^~w`w+ZruK8u!=Cv?N4>d>Cho*)lUsM|zIc|xB%s5(l*6S{X$b!bm$ zPml*F)NP`qJW&!OrWQeMVubthUV5Le{puI*U2ygxbGNJmUZSr>P<6C*N7q|HML%?- zMHE{d^p-DnU8d;TC~IqV{QnbiC_Q1;SkGRgon`L>%_q|ty7E<*N}8_(=U2hZSikE% z?kH*q(;}#M=$V!F1bKi$-6pgr$O9CXM4Ql4eyWa=@I>gO25wmB@Y`9m2x=20(M7cg zT^-~B3U!;%o*)lUSP~~NErO~;pG8Z`6C)Zk?5*AG-bVI9>!uV*>*P?2Act#Dl$0k( zj1IL2vo7H6wcHEsENB{c+3SP~+17o{302~Ut19cm9~4`>gR zvQ> zZWG!QNZhQo+yc3 zel3F9g!V*9c|uyjW>z_h>BLZr zAZKe&Xity_DAa9&Jh4Y2pKWGeSWDL?__so=nP!5${G+Kc-H=Z=Ok$q~o081d9kE~=JFZ+Z2t;~M&|LKqa?m~ct z9CKsz$FXhKJjC&;6v;x4y8&JeFc(CBJpCF3SjcgAz-t2j6X3M~uMKz|!0Q5D5Agbc zxkKrX>)Vh33pw5h@Wy~Q0o()drhqpCygA@M1KtAgmVgt0w*tI1;GTec0p14iwt#yB z?gQBS+8ge_?I3=8!2JOC2V4$#0N{au2LT=ocnIJfJec<#u9^Ng#{RM+MY52m-3jo{ zfOi4BE8wAkcLTgT;5`8E3D^K!0eBeT;ebZ~9tn6a59a>yjvpL*#}AIZ;|Iq$evD$r z=#TqzGyxWJyf@%|0PhQUKfq%E?+2kt2Lm4G!MwcQ@sVTi_{gz$eB{_W zK630GA34VHaXdRlf81Y(5MUw4hXOtf@Zo@u0DL6i34o6RJQ48GfGYtX12_q|3UD>x z8o(*QwSXr9t^=F~d@SJO0A~Q#18x9pou#dVyndP>{&>LN&1Rn7yF%hP59yl$PX;^% z@KnGpfTsaI0q}`{{{r|Vz$XK41$+wNQvsg__;kQ$06r7&S%A+5d=B7q0iOr>e83j~ zz7X(5fTsh#81M|hmjJ#L@MVB62YdzKnSf^jz7p_NfUgET8}J;!*8rXi_*%f%c`zUU z)&jgX;B`Eh_mArV-vIbV59apQ1-u^M^*xx|zX|ZofNueOE8yDz-wyZ=z;^<^3-H~5 z?*V)-;QIjI5BLGV4+4G&@WX%~0sJW7zXE;?@Z*4=06Y(H0q}glPXc}l@Y8^w0sJiB z=Kwzscmd!S0RIi}-vPe}_$9zE1AYbYKLGy|@T-7d1N<+*uLFJq@SA|&0{k}McL2W& z_&vby1O5Q;Lckva{s{2LfIk8JDd5ile-8Ky!2bsPCE%|Be+~E>z~2J?4)7wtivfQR z_y@rM0sJH2p8)?1cnRQN0RIa3H^Bb|{6D};0sjv855Q%7P`Cg1c)b$fl>v7F+!gRD zfL8_F4e)A!R|mWX;O>Ce1pFt!YXM#x@H&9k1-u^M^#N}HctgM&0p1w!CV+bY-W2d= zfHw#HXTVzk-V$&E@K%7g2HX>HFTmRX-WG6gzL?E&`#+#hf`-~oUK0v-f- zFyJA8cL2O2;GF>P40sp7y8<2xcsIbi1KtDho`4O&6@Z5U9u9Z};E{m$0z3-vXux{| z-Usl$fcFDD2JrrX#{xb8@PU920(>ywae&7IJ_PWgfDZ$FIN&1y9|?E@;G+Of1bj5$ zO2EeeP6Dn1Tn)Gea0+lO;7Ne%0H*;T3-~y|8Nl^`8vr)~ZUTHf;4I)A;5^`Fz>@(_ z0X!9O3*c#hPXK%(;J*Mq3Gm5)TLGT}_*B5B0X`k@8Gz3Od=}ud0iOf-T)^i6J|FM} zfG-4m5#Z^7F9tjV@FjpR1$-Ic%K={jcqZUkfUg9672vA@&jvgP@HK$v0=^dTb%3u2 zd;{Pc0pA4pX27=qz7_CofNuwU2jDva-v#(?!1n;Y7w~<6?+5$<;0FOe1o&aVj{trY z@LvHx2KaHnPXL|=xBz%Q;3ok;1^8*e&j5ZF@NFt1^gc1_W^$Zcp=~q0e=MeW5AyP{uJk=J{u1z4fWHR(4d8D9e+PIG;KhKy2mAxz{{a3G@K1n$2D}9DFMxjq{2So^ z0{$Q1rGS42{0Cs~1zmIf&;POi_|KIAcLCfL@G5{;1>6nrYJgVZvc2hz#9SH81N>5djQ@P@MeHF2mEKiTL9h?a02jFfVT$R6L2rU z+W_7caBskU0QUvF9pLQ&_XFG?a5>-sfCmB|1b8suA%J%Pyd&V90PhTV7r?s$9twCj zz`Fz91Mr@J4ZszEhXEcAcm&{)fcFAC3h-#adjsAF@Vcmm*~08a#bG~i0W#{f=xFh5mnKqmu!E@1yxdE)uc zY7ge=H)Q|QACIc>#Pgpiz_oxUc`&!P5gB74#~TCQ1aJ?)n|d%eT?h6z^Tc!en|m-% znuhd$hWKMW@%-m;fHQ#W0XKLs&wmS#eU7&ToB+HP;H?4o1l$YoHh{MU+#7HozUkw zjewf~9}hSSI0rZnxEb(dz*7KE1>6F78sHNEp9uIbfKLK^GT>IgrvN?`@M(Zg2Yd$L zGXb9k_-w%E06rJ+d4SIcd;#DK0bc}oI^c@|&j5T0;7b8t2KaKoR{)*~coyI*0bd39 zYQVDr&jEZ5;JJXW1$-Ug>jB>Y_(s4t0lpdVEr4$Yd>i1~0p9`mPQZ5oz8mm8fbRu- zAK?1|KLGeazz+d_81N&29|inZz>fib9Pkr>=K(GNo)7p*z)t~w8t^lKp9TCJ;O7A^ z0Q>^rzXAR`;1>bE1o&mZuK@lB;C}*s74U0-{{{GUz;6J46YyJr-v;~+;CBJP2l#!! z9{^qm_(Q-S0sa{9CxAZ%{2AcS0e=Db-+;da{1xD@0e=JdTfpA|UIchC;O_zd0Qf(E ze+2v!;GY360sIT#UjhFH_`iVv2Y4yq-vR#t*jxnL$5vwDO8g)DkN;d5a2LQ`0j~mh zRlwZj7RL@CJZ41iTU8jR9{0xCh`(0dEF) zbHINFyanJb0Ve=&1$b+~JpuOuyba)O0rv*n2XJ4&+X3Dla6iEP0ha?F0C*taL4XGX z9s+m=z&iro3GmK2kt2Lm1lcs$@k03QnYFu;ccJ_7KOfF}Sx3h+b^UYU*G zy=gG#V>$c358%Fl#{)hD@S%VY1AI8(BLE)>cmm*~0CxwxCg48-UIp-~fV%-+4e;uK z*8set`Db}A??kA-qXAa}J_c|Sa24Qcz%_tVfR}fF#_eA#T6sHqyjtGLBiuFtz)!m)S1gyXpBz55xQ@7>Sf*t?&>v3I@6v3I@6v3ETghH-s%G_2Pu z0bc_6Qoxr1z8vrsfM)`p1^7z9R{=f?@Y#UR0emjt^8lX@_yWKe0=@`vsp**x`Yr}M z1905*R|9`G;5mR70Db}RiwgWU#J>ahUBIPwKWwSpU##`8LDK%?<72V+U&p;)J8t^8 z_fyABuev`J@Bfrx-W4|>=dzIdcUj(F$@A~%`!P56l$V!(6Tm$Hmr{D}AMgE|Tq>U* z@Zzi{zvWkJx6cZ_;)7M-tzNsn`Z}$3xFLv{&L@A*Ux)SyuW;IK6IQy(nAF#)4lrM$tl|T7`>=G7*lyB6K>{4j?%V|#)7N+w?dmZ20wo74LyVRbEhatsN zNcyePc+Ven#2*Ii`Jy`QyodXoYaG@npvh z+e<&%@t4cZPjuW(!GBRXDfGv>sMvVd68+NiT<(d*2F)|9etc^HlfSIctp`f)k0(Uw zD5T?hp6hxd>{rlVVX3^wsQxJQ#(96M;=O6Tzv;Yg9n#qrn&(;V z<(>|oe^b5`wpaet?#V7;QAqhl4aqKrmcN|-UtwXIC$`sdn%`0w*DlRpiH9M@Q%L%) z(zxe)!WahZK>~~?49NoX3RODdQ@ZewZ~fNVNu90U!NhInkR*s)`HS7PoOFWxH?0^{ ze#DO#Uej}a*}7+reei~pXzAR#sO8>$Cw$bc!r1?c>RxaDLB|Dl$^_{}_Gu|k`bckC ztCfL>C(54eh~k&kaYt8$rNC+O0)L5g9%J!8Y_#n-c0~liapWOJT1UrYesMABqfo>z zHa>296zN!Aju-1M6<_2B6|dNMJKy=43XuZqIBp>ZQ(LF{7X;%X>O_q8ij5E06X?Qp zh*1Y(Rr#hze@w5!Xb&;+i;X9HDm#=eZisdfqrFm(clA!+;E!p&df#qPh*M`u z3CEpwPg7@#r}V{zBL9hwi*i$?SH+9$ijJ%76&o+g7mus*!Sskle9>{@7aLD@C{)>_ zbgEEfM|7O*6&o+Iqdf1mT1-{`WWTJeUwY_Ktz+*stnganW%E+r9R=){*72+Dt%FMp zvOwvnKhyEzLXm#a@v<^o2kLV4l77D~w5Rm1pB@=uK1eJ!9_^{HD!;gVmAzu)MS4_r zMLax?b`Y!5$BkE|ck*wwQWoDXsXtRl=WSF%@v7r?y1+O=d>ju%$1$Jcu;Vu{6ZbgK zBSXvwab!b^gi-CrO^5p!fN6h&LRzO$SeCA^gk$Iy=YO|;|LXo##~6E#9eLQ(7uFd^ zoHpmUKIvnOGk%`1-Gr(GjVsUG;=NBE`*zRQw%lyyvy*|EA4qqR8Mvu0e{K!hIZ*}8OvG0Sl3St+dtCs0&7Q?J^9pSU(c9ooOt^8&2LsU z6=v7WpMLteQwtB&E!q4H7C(8Xzx?C06OJ(sU--ho$2K+@f4%wV6MF7*OyTFfcmDM& z79Y&RI&wj`t9Sf@#W&xy-9Jt{ajJ3eNh4SNnAsPl7qu_yC)97KAKmsKR{3% zKzoRhUTi$sQ`w<(aYM9=810pMysMYy>lD&_nnFrR*bbe3OY?Dxr}V{zBL9hwi*i$? zSH+9$ijJ%76&o+g7mus*!Sskle9>{@7aLD@C{)>_bgEEfM|7O*6&o+Iqg;2lT1-{` zWMA*Mxt`#^l>YT6kBn6HQEYr@!8v`(Wi@_t*B z@xv=S4*C1JQ;jbN%vohUwr_F6_uFo_8{3CleDG!ChQ8O`*mL?mkI!7PL1BuK|E=QV z^XE-Fc%3y*y5>rwX7-dfhIYB3@br%J)}HguR`z^yvGLt;2hBKcld_68wmS2(8y9t} z=vMRiTCIzg7&8}svDLwwEH;K8x%jjRY`;xpXPwjT88Vjbw_SSgFDuWT(sTZG6ZgqZ z%imS_=#KxLzRUIpJhAqub=TeKpWP}py=JY;Kj>06Ut~v=USywikxtURdACvT{QLIv z3nHGp@rdhPnP`{ymRI}OJqn~y1 zcgq!SzwPmDYmeb7mY<_-_m@>h6<_2D6|dNMJ6~t~7U~eTQ)1}-HtvabstZSQQg)~p6kWvz+`8gfe`)%Y29(HuSd-Ib0 zR_Ty&d7OwZI!^YAjVC)4s_aoZRVcC}I!^YAjThNbuDe?;rYe83ulL(rPw-z#|N4_h zMymQKHa@ZtJE1B+l|AGmMt-sJXpgYUE~Qh2Xa_O!i;Y*=bL_WTDT{BHa2|HG24LD}qmb5V6h_!@ z`|nA|KAyj1sxfQK(3^j%In}tj=dH&+b@x;wJNNCCHoK(Gn0Dc{XDnT^h4J*G-}XOr zRPRFK!{J~3=dv5;T{L$@W7U%DjSJoyzVxx5?ks%z$a(pR^@E-Lw$>jW8E{pvvWk(v zeRWIY7u_meJO6?Cn|`sxDBq#SYE``!8@JuE>tD|OeW4+;({IiO4>bMWqvDIpeqVA+ zbKJ@%@#{`Jv=w9aguHhA`dUoGfXvBr`EZuoueviTxAcEttKi|msw(n-4ie*7vI zztehILBx|c5OKYW{42NDUa^Y}tS6DbZ ztEq*DuHEAEVa|S6FyDT^jb7tVEPh_ipYOTCm|=YXb7FLs*$<|)j)>Y9^%Lqh)Q={= zzVM?HoczW0Z&`19eB0WisFcgg@wWRbn!iPU=%jp|@mr`v*iMO|_uIH9imfm5rYKa| z7x6^L(H>&t6&p|XRCXv`+z{;|Mth|m@9L#}HVSDzO(CTuY==(2rTI9;Q~KgUk^e-; zMY*ZctKvm=MaNb4ij5cLi^o;@V0y$NzUVmdi;X8c6sqh|I#npLBRWp@ij5c9QLejN zEv71evak2sTu<;{O8@$kM@FjpC^kN_5IdnNKb1Y?BSwC)@o0~*$}Xi-g=hyc@{5gE z*>mi-S}BWfmvo+`kj~qvgyL1l?R5J7HlL%Mdb5ur_9ZR?M70|?9qwZQrhPUFX`M!4 zg#9+_dD2$nKkHB0e8Q<`8@I2q-fLsneL}lE{{HZHlTAk1PhUOXHNCy@?mo91T+`zJm zHGjTommv$gRUFf0^HZ+=V2RP?ich{hW*~c>^oIxDNiAJyi0te);F)WNF6~j#XO}}B zxbJ|T^B2C>>ys_cdEB|bZ9wX{nn&h$tJr$80iK5NEI zQ9}q**BL=H_CQAVepH; zoLu4ij5~b6sqh|I#npLBRWp@ij5c9QLejNEv71evak2sTu<;{O8@$k zM@FjpC^kN_5IdnNKb1Y?BSwC)@o0~*$}Xi-g=hyc@{5gE*>mi-S}BWfmvA1|_qUmj zb^MGK(ngH-5Vz56fELwm+;q5)0hspLD5P~7g%S4KPT1hC(H~7a-FWl31!qp5eWCHU z6K~#g!L_Fwqt-m*rKK%-?eJCTE_TH|GA<#t*K$ z$2ez~Sr4r7!6Sw0%&1u}ygItDvgw<}#vfmw{jYC#FRS>|&qrUr_>FEA8*KGZZSGm&eBLzStOf#ogC@``?9z$WGVH@xR^v-yRhwP5*7`6T9`CKVbBTMAw;5(mJ#C z_8Yet+xkeiitV$X4ISL4Y`(~jT~&efBKxF^bdv7s&yU!)&)3%$M7%(g@Es7>yU4#j zdItbeJ=s|W{3VwF?NT`Jlw0x6S6k1Y)8)?J78#Enc*l=3ADUxav-C&f&X!9HuU&fn z$1j{Rr?B|I`#<>hzl)6B$M3t=(v8kA?yP;Z%Nh$VHGZGI;FCV@o>tgp*Hv4;W&3fW z9N$hp{ORE={<-%)ACcW@4%=V8L=81s2`no=XzZ)cJ}+k^>1a= z_V~87$3XFxvjc7SS2TZ%+|fz-I^(xchp?RzL+`h7PZV2UJzz`)m~Ae4bLmai`zv{WkIt4?DWvy?M!gt8~b?JWj+H9VdIm z#*-ZiRrV;IDiqlf9VdIm#*6GI*WIlaQqv`1KFm(r<1w1XJ=#m1}bIrdwvl*PA8I1lUnHq)_=pRq#Ph|wP6Hku94 zqS}p{4)-wt(>@!8v`(Wi!hW0eJn7lSUHz6`K6=6oeYFVuRiwZUO(S&%;~Ygh$YWIQOIuh+F?h& zd0?T7>6^vI_w(O)eVu*EDt>tKsAq0|xm(5iJHK|^AuqA-CH(U6QQPmm*f?Xs#Fw}F zZJ{Bub9i#@3(x%8qvDy5w>s~I-aY5vIpoB%cmMhsnyan6@l-1F2OXB8GtSb6-)lKr;BA9;QK{_Os_&v!h2<&D<8 z&X{}E3kzFk%%t`e5Grb4)K942P(P|(de{YvoczW0Z)Mc>__npjK=GEd18w(LG=GcS z(MkC_Y^@3(PJ6kA{9O;M<_FXD-gqdmmPD>k0&sq9d?xFOm_jP^=B-qlO{ zY!u>ro>Ibbr{C)RHu4Y;JG$PzdC7jObjY|oPQ(`-Cws-llN}0G_9&ey6xk6SCws-l zi|i=Z-K`c=l|R|n`)#f#_%Ee@{mCOERecm2A6bZd14lQ&weY4mYyV~H{ z0}d&xIQ`#eK6ChU-6~%F^sEQZe`bl1ntk-6KaX2%^cZ$-dgWgh8X`L%JU4s&GnVwI z_|KRz{oY)^=lr{$x^iCsJr~e?wRL{iZ*O0EbGM3TetKt(Ln_MVi|p7{6-Y0#Pr67a z>0WP#`YkWo@YaHe7ibc`1LArY`PWD903fO-JF9@d% zuK-QdzNnv2zoCA#(SwU0AJ_@|ZEb6hf#NM^2gLo!DoC;MB6k#PSH-vUb;fU@4q-bb zhTd=Eo>2K)6;Iw2g~jsGF4{w^N*_0#(yQ{Jba6wpix};ddc3Qb_Sq=J`8=hB<4(WT z`)%YQ9(HuSd-Ib0R_Ty&d7OwZI!^YAjVC)4s_aoZRVcC}I!^YAjThNbuDe?;rYe83 zulL(rPw-z#|N4_hMymQKHa@ZtJE1B+l|AGmMt-sJXpgYUE~Qh2Xa_O!i;Y*=bL_WT zDT{BHa30qCZKh)#KVyZo5u-iCZ8RI8MYS6@9qwZQrhPUFX`M!4g#9+_{cY2Yi8ID@ z{jzDM(Rcq5zdpp?gEi^h3`tNv>2er;;F@!ZpeIp?LvzE&}@u!`xM#m2^;e%*WDWLd?C)Ppm-%Tuv z-~QMVMU7k=|&kBaF_CeHr+kFDpwJm!V(UY!1S znydDS3;4irZXqUoyr`(EnzIyPzbB?I|VUaQ9W6yqLZYjJtVar43u>H8Zjy`OifqgGFz8Ns*>$jesYkZonty!zjMTNKayzQ!6 z*nXNQ$NPrewff*+7a2RYp1bo(-`;E#MlRTFv*cWAUjdq^eNjK5enb6e!9y2aGM?Q( zXBiULf3fjxYmb5AEoTQh&YyBebW*;~_$^YSo`2~5HtxG(>r3Q+I7s^c4>8&) zHlFOM>`=P6A=*WZ_DVh8)l2(q6ykiIQo?cP{G#{U$U{8r=z90&CHt+?A>;Bm5npti z>=heNb|_TYqjaiJWJh$I>=hd?vZFljwOUM7{$yY8x4E9+zm)#v_qt}i< zAJrvusxflSQy;&7#6iYuU+?|3)b@Z}AUt}1jheY4mY^8P8=W9rK)R^MdYh+7`)R&n5qUk|(Yz9q&pS3Nl9h~pL; z!?Nf8u;7PS$NjBw08P}ssGm^3p?>toL!;K4;M`v)u74||w#T=vJqC)m zoE>PpzoPkD(!~wY zE@HG->hZ2#+GnE>=kt^jjywHU@3)bMc-YbP?#)Z~TctzB<#8gu=s4LcHlFNIsIo`t zRH4X@=s4LcHeO^$x$bVYn5z8AzTR(hJ;8q|{p(L28L8@{*!ajo?1ZZPRQ8aM82QD< zqdmeZyOd59q8-G@FE(Cf&#~WXr7XT(!g*Nlx0#N0{EQXSMvV3lx6y2X7S(RtbhwWJ znD*Hyq;(pF5%$}x?}cj;hjz17(L_cTIc?@vWxbe zmTf7k`03LfpBr^&w~Br}PrPBytxJrfH^1_*#gi8sb4R!S`>sU`4UwH+CXL(r{_lEJ ze6(|qE^EHE_5AHeU4C_L(|^)@we_w`d*9RjqHYx%?tS`I>6)_nB0F|f1=5S`lP=Op zy8EnpNUGOY_ZCFFK$Gwt5ZAlNzdm{g0J^^o>&eb4;4irZXqUoyr`(EnzS{4DH(q=2 zt3}2aKWzDr+wZ;07&h>t4}NdHp|I;e5C7`$(RUSIJ)wKWtBcrv+aL3$-G0wy#_HF; zv+l<0-DuQxyJPx;J6%$^XWQT2oXz&rL^*!2cGa+n{QkJ7YhHcu{yU9(fB))~Q>NZP z?JGbNwJ+)?)NiOC?ef6VT}L|i*NN-j%Bbz}ZEKH#;w@(f+U~Dt{ua5Tlk#=OZ=nuh zJ0*tRZ{waQw!X-lqEKaD#1kDydx()&Y&_Xh*`aiCL$r$+?Uj1GtC#lKD8%_ZrG(>7 zzt#I~z4 zf3mOl+gwlZUrPV_lSf9X`Y1L&vJg9=DnFGy;G4hLzSJ`vy zw^}KSZ+A-r%HD&osXO;-9ZP_uLW2AHxP;cuxQ7LgAPf zSG}$Nw|SecIsC4-_kPM)?cl99JayH-7nU?#yyh!g9_PHjZTiiR@78)oS;fTst34Oq z(5+(E-Rn-z&0S(tT>R^E&8IFl7VX|;!mw`^8X`Me-}l*_>wev%;_Zg@Pu}{Ot><6= z<(40B^Yp7UUv16imoB*L%x)Feo!0%=Nlj(*MRx3}3Zxg=CtakIbZ^=+vM&3;{RI&( z&?I~Z#Pu%nuaDjVfZpGR^<-xi@RwWyv`gWEx$pAYXb zE`06&yYFkgsqov7aoe9a_nyMJlc#T$`(}}G==--Hn^}5=(Y5QFAJ^@1v+>q{77Tr9 z>7|8vw_beA>uf(wl;hBQzuar9|12`*pM30?vqs-zYdeGyM|9PJ@SUa|3HPi2SF#SPIeVzgK4@vdIlXQL43^OO>fJN;Jgw~>c<*wOXw z%}e%Mr9;N$aU#CxIN2*Up6pPlvPbDup~#NtIN2*UUSvnP?ryc1s{F~m-fwd~!G9_J z>rWmTsp_NH_{c)+gsS{h_K=Sl`NhVgJ;Exxlui|*9mL2lHeO}VvEOQ?EWTaBd06ka znT~b*j1|&GjP?+>(QJSg)o$E$xQ_vt_Sq<;bsB{c_S>xQlg>0A-Rjbv29CbYcyZ75 zet3MPS;mQ7uX?ohg0qc#_d05u%6M={AFkWve4q5x^n3T8b8%V4G3V@j^zm1Bt2k%TlJ5sxvBX$%%txnx zdBI|1zj;R<*!u5 zPU=>%+q6g5t2(i4zQ~SURe|&(`=pC>lJ19}x%KT^PkOK*;su(7?|``8MgH~CI{?u4 zNwJ>ntOEX$OMrGMoOjBtc;~CN=WMy#5g#owPU-%@wQtqkXC&`fxX~u(-BQSn-|Dy? zo8MO$Gv~NxpZ@nE^%7UyB04p-u=DM^^E)OHIAEo;DW<0x`ou74|| zw#T=vJqC)moE>PpzoPkD(!~wYE@HG->hZ2#+GnE>=kt^jjywHU@3)bMc-YbP?#)Z~TctzB<#8gu=s4Lc zHlFNIsIo`tRH4X@=s4LcHeO^$x$bVYn5z8AzTR(hJ;8q|{p(L28L8@{*!ajo?1ZZP zRQ8aM82QDnwy^oot%qNB{3?a@|MlH=*Uoy@Sh)LJkL>lzD}^U6 zxqDdOzhw(6nZ8+UoLyhB@WR<;6_;H7;*_PAbgM{wy6e){E?8om(dVKQ_nWoY==;(0 zFCF*kLPKOHwMLgWpZ&N;#reyZ#$AUv0f==9jm9+}y3Auw~!R z4m-PSzQ~SURe|&(`=pC>lJ18#em1{g?}rN_UZ6?%4v6br`3;(bdfP`u^r zK->Kl&EFz-bW*;~_$|~SY^TJ~`)%A4#nu;jQxvM~i+G~rXb&;+ij60GDm#=eZisdf zqrFm(clFXf8-+NZr<8Eq>9=~njXcD|j;?oaUb5dR9WpMD6Y)jI$zHMXWQRhPJxZqv zMRr8T$zHMXB0I`;cdNxzEjW&^aScH^eQeGI^~ z&qg7w(bZ3nG($JNH6*S>w#O8ebtl_c=Z#HvyR^&K8~?NL&H1aX_SZ+63%~!t z{}vgm?Q-j1FS@m?qU$QJXHPq?TScF4jSp3xzQp*d`^Ph{x?!;~_Ptx)>GI)1Lu6;~ zx%*zc*#|u;PVe>YepAohdj4K-mhE}-V{g)YwYA5?H%!gdcdOWGi!JN-xwLG)$c|lA zf%GE#q>FTt?k>ZwT<^v89xaG?fhOTQAg*_je|_{00HS)bvkLf2E&+y3>z3~oeUjdq^eNjK5enb6euaiE#@aZ2Ze{ua=8MQsWZS65oyyfgb+x->I-y(N( zQohdkEz}`wr^L|vZQK*Z))#qG6sqitc%tKI4>9tJjVF65JCrVNh;|X9y;6^N_0m2Y zg*cz5lyKbXw|c*gJjBC}u6J)R-oV3vfbI!fvLZj^SEr(9~VUm%nJ?8Gs zA3MJA=^0(tJYny33YWe8)2QKJE-;$bsk`~xQLh%h-TBC#XT3kQ@PF)Gby!sE^9S^T zeXns|xse8>y(guGpRUTxePepsaZWFuiAu+n#;z83C@z)*cMhmzIHdJ1SR z$$O`Dl=$~mzx?BUa)cUK$-*s`ef{dkM)p4wRA~Edac9G8Z#GTy6U!wJd4BDdfw`_K zyXemT^=z+e`;!kGyxEt*^udHz&6 zcz#;-=D5=5H<<@Hr{H1Rw;3z(Kk5e*RYrOEG6#+T<6$1VYt;iD4+rcLN--|xG2XAP zkINVKY(VjS9xRbuI=8lc8&t?h6T9#6V_AFVfN$Hk#Z|)i#I3KTxnhpj53PE|iI@#}@pyRHAM@B>s~+P4&%*^f z9>qAA$NpONJUj_sXR_2jm*{)gwr~4USgFpqAu4%{hk2E-GB>5T3AMv}48+5p4Jf>) z0adVX`|KHev0<0>>_wr}ZrvJeXY6sWhKrA{XBh&gb}ra%0o&w1vfuTpeOc(25&<3Z z4ii&$zCN~tS9)=aXBKyl1*h4R%>D&-4!b07SmWlRt1wo4Yu>l*Dp7OzlmI)Se9J#G zj&pYsy7t^Syx8D(Y~87@PyRUag5|2S@KD98k&ME*k>%0)W0#zTl})n^KioRzeLo)%=k(vw`04lGe{*tQ>izA>23Gk@y7kp^_^_5QrmWgBYK>UwdbNJlzyEzx zD&n9^-2+a1|M#d*{TtRC@$Ugvx>J=DL)&}9e97FX`GRu-=LXKv(DXv9=F-1srTcFR zq+YLX9%Vn?ZzfP*FJ<+n%JGxpN}u0k9^{;Yhi%_xtc2EnK{YAL!>9ZxImW|0_S32d zJRT0%C6r=Z%wxP?T_2Y(?Ad_g`#e}8xpZ!A`!=YMktTNEuOC|ViW4y#^5XIE zus`Opzg9iQ1D=Nqc07u4FpvGU>Unq)zRqN+eJ;`Wux;P=qp(t)aYIz{7!UI*VP$Sg zaT98X_ZWzWJsVJXPXnr8-?lm^r}vOv8`u!v>^%#d^IQZ^-?CkIJ1%6c z8YBsF69=#X^DNJ+~!mGu8En%BU^lOFDQSORf*|+H1~$f;^sfuhFP;c7k)KeSt3qI|+erDy_H(DEie;c#EoIp^p+(7uI}@Hy?`{7Dbm=_wp@t7P^RKI8>?Lf-uw z&l#L^&@qwnlZ}LhfbKh0Ukmp{V5mNsL&@r8Jq0wEB? z_d2o9)K4ROJf!z+&thg3toi-#F?^bCSL)vSgY0%K#}XU6_`rP05~bz~&Iz0wI7fBL z&Z;|5`uD7K|4o6^>($Mp?8p1f1nTRhtlm^Peo|cN^P9|roKx_y?c0o%(AqDkCPjJp zlpiI>c$mk2TJ?a(!vVX5QjCjvjQ6YSL`Co4tQGifCH3=2X;J4;ZSnG)2gR%xbNLMlP51e;M?|Xag{JWaqDYou9)NX zL#tkKB4$HgJRTnQ$2|7es>gW1^KikAM==iOvACHfw=?c077R;n{@ zh)N#gVO}My%uOk7LhbM#1M#qD0}AhHKo#uUszf%PyKKNl)^cE{oDJ&lWPMM(cc}Py zBTHB9?}-DtEn@i&c)uD~c`*CX&2Dk){-eb*HPasMb0MpkKFytl1tZR}-EW?4X?6CB zxM{qH6Cc(>`$+esMa5wU-D`*&<`(uJYs?J=$R3N$j*&482r7N_X$D==Jh-r~GFPY+j!br>E^V zvl5NcZ57-3vUz95W?A97UL4S=ceva4-(RC5p8o61pt-jVtlzUdpB8^Q#46_VuH@Tu zFU*(BjhZhwCva}y92K8^>1F@t5TEY9DUf=-x_OlSc)yuIeZ7>`n<~doiYtA7lX;MH z3Ldt7o3RpF`vujcC=Z|VqvRM5^VmQPeJ#xubG&|N)hkZKY{-kp!^8fV$NpON7!PoMN=g znRM-^TwpI31r@0M_No|GdPtyS$Yg2Xmb<{+ht2NW37f_Xw}U%53H8p74c*iH9jn;ynF2Gq>yDL@y7I|ItXR}9mM}3JDqr5&(sZh9a)r_;+ z4}Vs}Nm#wRas&MZJ3WO%*;DwC7vu?fKRg;STW232Qhu_Lun^FFr|N6ro(K%=+i;(# zdRb2a%_Vv7w2l)0zB)`F__30&fo*S)>s+zS{;X!l4~@ok^A#h$>Ksb9JSx`PSL|k! z69%?=$+XJ5S8ZXXOO3e@F=ik06YiZkn{9)*wQ;*PdA|St8WnL^EZef~hJl^%oREBS z^CK+jg$=_t_w$wBhp-(K#lNTTGZTq&kN*JHG^|drt%<=l6Rj)V^vmq}Y4-fle9{X$6V?5w_xM0Vl7zgv% zU#p&nC*kW%mfGhMeGl9AZ9fVt)fqQLC6DniuM$?~rW7}!c6g70c-XT6h4(a|3ifSd zGVHD$<-VB({Cjw?SMA;GYR2a|_N3jy2JZAu^{U?zmL(>IOOgMEvsTSlM;2`~LG0YX z@8Y|6E@G{zFPElU8p^7_s<8FtKi5UW3&-u7?o1V9%)j53eQN8C6=Us$RW(;N&fnTe z*mtep*By1=u@^V9ZMJ*(f}KuQySM+bNJioO{X|#h{$Xcff39Xd=B0JfAF1cDFDUE| z+*ft~to-Y7>q<_-NpaQXWw-3~6pp!7GJ6Ui@`5}e@3O~^ZO{53K&1R+BVi$+`%cx@ z!aWffs!!%nvU*uh0nH_O@3f8*|Gv7mOVZJ=b{JUE;&s+7Tyu;qZ8SIQ`=0y6&e6M4 zbwA-Rw*C^Guc+R@&fh8(Qa5}nYnn25%W`Y>Gmp=iX0^VvK|FS})1IT>e}9dN=sv1W z*qAE@_P%$cfZf}VvLl`&G7THN59UkeM$H$T6F4_;jvh>m7@Gbu#Hagj3Z!1IZXRVn z-ft#QUoU0#rpob?;!2<2WFF+4f`@J2W~_wPenB-U%EPDpC^^Q%JoeM72Rt4Q*d>%= zT+CyK-{Z%!_RPz}ny*nu`BQSh)2as?pgcUV z<53ESk^`PrJ%z)4@79?-dGP_?wr`88gz<@6UrTev9Iqc*^@_M@;;opD1{@)!^EDq&@AN^uivhxZtW zhdmomcuxbWVBeOt`0YH76SuJALl4?N2-?Fg6fM~6O}?$HN7V~s9t>K_+5|szX zzt{r*EhG7PIEAlQ8*vZE$Vy8*I9^gsJ7|c zmt1c# zKiNoF2A-pN!~lHqr|_juI?9;QrKu|3Dnn1S-q)p{G_)KUJF z9PqU20S71#5A1l9!lC4Vr&UkkaNoOiCQn{`z_;z&;woW$;?~#FTrtP%hgQAfM9hY~ zcsxApk9q8`Rgdw2=i!1Kk769mV}GrB9-f4+Gg)e%OY}W#+qeBFtW;;*5S2W}!@NpZ znVVAFgxcXf2I67Q1{B`YfGXIx9jTkjq44yrtohKKvyZO#W|v2lt@EtPHs<>w`|%CK zJXq!04@30_M>D_J**EJ(PZAG3tmZo1rJ&fo>8mQmDqUo~_BGx$zTZtTYE<|g|8BFy zr{;Z|N8xnQAClP%$Lp_8e!Z5H@b>w`>ut)uWB$qK9Gn071zUE!*X5Kxk&MFmx?^d| zK3>kksug}c(>=+hcL^=D!hghFxUcH$tGqv&qo9*e$17=I#aDKE3dh_knLULMc|o3# zcjSXJE7R>cE>eE7k+2ZZeW&Vc;hqQ#)hBZ(S-q^Mfaa3CcUnh@e_tIxq;Xiz6$W;y z*q*6#FX>ssHeNF_4)7C;7g-&dxv5@kvEyp^x4j0|XG7fv@49=k;khrRcaJ{6`YlL3 zqNCd;apSbt&pLen`=wOGRMlgfE&Bfde`^<+D%7W*$O7KCJiE=^59UkeM$H$T6F4_; zj^4hS9rLgB|Bs{lZwjPduWlY?Ki+R9P+u=)^`^@4lj2IB-(()-oPvjK-)5|Y)_y@X zDaymA{3tob!#wuWss}tC4%j7>VqDB)ykA`(moMzufa3c+SR%P}Zf*NEsF0B+cHiU2 zvi8i&!66TJ?$(F&pyY@$j%e=CQw4J;no`hYNN*ig7TH{k7_OcoM$OWT|~F(f6=z-}a-h zQk`)_RPq=P^D1FwZc1?zYKQk2h=)BJPyS({FK*NcqV|!a_jzovN>edm=DYpUk0T^|GD!YRf4Q$1&9jjL@4Pahvk~Q=lbUg@6{{L}w|4o6^>($Mp?8p1f1nTRhtlm^Peo|cN^P9|roKx_y z?c0o%(AqDkCPjJplpiI>c$mk2TJ?a(!vVX5QjCjvjQ6YSL`Co4tQGifCH3=2X;J4;ZSnG)2gR%xbNLMlP51e;M?|X zag{JWaqDYou9)NXL#tkKB4$HgJRTnQ$2|7es>gW1^KikAM==iOvA zCHfw=?c077R;n{@h)N#gVO}My%uOk7LhbM#1M#qD0}AhHKo#uUJ{kX9!s6|0Wb)4E zn`PU}PWuM9JnZAiyrwS5-F?h*=Jm)?Y>~O&|=R0=C)qc$7 zw6UyfNYeAEH%2lFC%VnfXA4$43(Nl5zI0b$F1=g3-C>8b+=Kh7Zeq;dspm5}3F~wf zGM-LhucvU#t&-VO_>dRm33=D9RI}plk-;M6CmRV10o`}1z83C@z`(u@_sJYeRxj%* zpt&UPoz_v}-&dRUSU-5(R0G?xsnnkXS_QHsoidgCHsqjqq|4x0;YR|*O7r?%-niMo z(z;a$JNI!X+t&Tb(ayyVv(XQ_<{x)zvnU+f=~?|ey>H7itm*8qKm+Sq|7eAv7kajH zLaIn%@}fgO)hIFualwCX7w?t8b+ zMTGKs@Z(fWmtkPzC!o(|egG zyWikK_O&>}A)}_Ss zx}hz;?zzk^ci&UI?X}zD-24w}xBR$3+PAeIlQaG3BKAW4Pm8bl7I6~X%M|TWEaN*C zmi$d%+ibBcbDd?S(yfeS6i%kL!CiAL`TqZJD|KK}(mlEKUqdE-X#VV;^!G{oy<7M+ z+n-KC)(`IMW@oh5Q#kPVHnBa04|zeJkheH+LiKCyPKcDBY$PlMbl<7^TDT_y1N%1I zC#qi7Q$TY`-aD($L8{^@=Y^L97+y& zTJ;nT_q|(Z^5n$_eA~V)t`f#4ZhbAy6?43PXw@rD#B9im$HT+^n8*HF^%xI$9xmAN zD8|7&_SdTC;Ys*9lcn~#MBl@s2$#8ARhK?K;b$0cS(|<heGC?%LhaNm!BI?NA*Tdp(6?Zk5cQ!iT&dPslsX@&273un>{*lZ}Lh zfbKh0Ukmp{V5mNsL&@r8Jq0wEl|97It~Zaf^q+k@dyF_N z`c(+1)-(5UF}!4*LC#AJY+&l(hTB%|X3tX>se63fQReBeYWt-8Tg6UU^iExaVc#W> z8+rIzXvKry|K7sv9N*@Z3uMcJ=KN7|_FY^L97+y&TJ;nT_q|(Z^5n$_eA~V)t`f#4ZhbAy6?43PXw@rD#B9im$HT+^ zn8*HF^%xI$9xmAND8|7&_SdTC;Ys*9lcn~#MBl@ zs2$#8ARhK?K;bn@rBbTHM`0x77gB&FV!8f;?>A3-hqpyeOs;N zX$`6Vvll$7F0NM~tCP@U)UGtKcJJ8st_$_WO2)F*|GH&$nHk9_9QW`IF;ylx3ofai zwLLLCm%eD^iY2?|J%IbFu65l$!P~w#3WM!Wcd{>LucvU#t&-VO_>dRm33vMxQrkKu@6R!?3ZY-3w#V#Y>2{1=lVO`bTU%(~)pMa+ zMcqF&Htf*DzDpjr<|qB4HQ)cIf%%d-Q1b=n1kMedBhP(P z{HscTuYvBrDUf=-x_OlSc)yuIeZ7>`n<~doiYtA7lX;MH3Ldt7o3RpF`vujcC=Z|V zqvRM5^VmQPeJ#xubG&|N z)hkZKY{-kp!^8fV$NpON7!PWH#pC_W*#9kdA!hKa&r|Ye%_g*^+8E@=cw7;Ugp29J=N@h>t zLtc<4EUe;4Ub4lJit)s-huO96F z*NG)<4D8i_%kxXP1+(ov``^}&Ix4brB}^dNCj<3ojdPBF0LYd@6Sz0sRFroTOR zSlBVPrQpzG)kka-J5--iHsA=oZ>v22VfYy@152}a)`Q(6jm3;AzzZ4p1H**zqWZL&*V8tDeH)zIW?Pp1k;gZ`-%URl@kh zt*@oIVvg4jt$M|Ym<@UHczDJ1cuRj_Z%lC))-qI-9-YpK3e?tW%J zo0GizmF16jvoz0d2KrB5!|eO%r%rD?jXk^jX zHm|Z5iV_c1oi=)g-b32AT^sXH#JqO)!tkp;`YFksggV2$E4O_5mJJU0a>}n}EX(UQ zeDQ-3k&MC#Ec+?o^8jaIl1H_bn>*#w|9NNKxl6?#!T-O_f9?*4fGp1(g?TSCHRxW` zUQgke|9@mMdkP=&f;=JbI`@`->HqYUNcqV|!a_jzovN>edm=EfZ^M0}>Sa9zG?(PP z(>hB0`)Yx;Nj6PuXkboVSns{rPB6#W$HV>`?Jw>RJ%4^og<$dZSpW0ujQ@YPF2#Cw zXzat9wA<@@`?JU%eSH;nJ=1nE>CowOw)(-oOCI<7x-0=fTMevFaIN{PG6b=_XI`}s zm-XQ+|{j<6$2AY1IQB4+rcLN--|xG2XAPkINVKY(VjS9xRbu zI=8lc8&t?h6T9#6V_AFV zfN$Hk#Z|)i#I3KTxnhpj53PE|iI@#}@pyRHAM@B>s~+P4&%*^f9>qAA$NpONJUj_s zXR_2jm*{)gwr~4USgFpqAu4%{hk2E-GB>5T3AMv}48+5p4Jf>)0adVXJNVDkr%ey; zW|vNFIs0LnA4~3DW6LlHZ}#7@wj*25T+8~;y?1S8#B^4?SLY<-IxG~g`p-PPdK?qo z%aqFL6m*?UYE^VW!Glrag_Fyl4sO0&+P5{B+~)7PJ?(|RcZ53ZuyYa~wYvMF`<=IJ ze!9uIA{xiCB2WHV)1ZGOqi|YhX}|ntH)kQkpmu-cXqZc%J4^WDXWt&deN{KgrFql( z(T+m8GU;ah)zn^3;h0+`v#0POFUS+}_I>kD$&?pPiLdqxF-Sw`!?Js zb0}H8tfzqHlDv0XM~Q!5?bhMx#njaetW~B@bsvYHU?iw}F)U^d&bM9g4mShLfD<<$6&r>4%B?XIe~Kn=jfYbyHUf>LVUXaragG}Qk(WDX#SSP3A$)DR|iSZN^Gy?H5#&qC9-ckCJ0N%ws>Tdcfo1fL%f<#>G6w`_=Vv z`NEzJD8A2wC6Y_$*0yhh3K?l)_dR|rYtOtqtoa&sls_d0Jgs`b0m{PzJ07KQC^_J1 z)l)d!_imlZlNTTGZTq&kN*JHG^|drt%<=l6Rj)V^vmq}Y4-fle9{X$6V?5w_xM0Vl z7zgv%U#p&nC*kW%mfGhMeGl9AZ9fVt)fqQLC6DniuM$?~rW7}!c6g70c-XT6h4(a| z3ifTSD;ob`!X7rk<$R|yH4ZTG-gR-{0&f;@u59s5v(~X?)0{7Ub)U&j=gZpMb?PFq z1&+)I>cT$;Jv!RsdtXT z1G`e=I$U|nT>NSYFWbbj^*y${?$|YwQ8-(|58dnB)>)W-$-C)>in;W?Cao^$ec>_O zS9PL7tHJ>J(#I&D?uNr2J$fVIiRVPSw}K zJrNk#x8XjSL&@r8Jq0wE7K`=}IHe>X;RU*un%z>IOI45v!;2e!=KXK6elMtWozbTMY^L97+y&TJ;nT_q|(Z^5n$_eA~V)t`f#4ZhbAy6?43PXw@rD#B9im$HT+^n8*HF z^%xI$9xmAND8|7&_SdTC;Ys*9lcn~#MBl@s2$#8 zARhK?K;bfr7? zv)QoSoyU~jzgWCA@#gtnY5x^FrbrjwtLshHue-Rg^ToU3^~|Fh7TvH)+P6)feEQX% z@%BPcBe(T;V;qHb6W!X4BjBve#2M=2pq(rHDWJI|@153B;@?*bEgNtxJfDFjsU3BwuiHsB zaqA+-l2!EL(_!qyTR5^Z9T0_3Nr6ObU7rFPJxM1zmf=f!>V&l8a?>VnSwD_N!^UgQz z)=2xd=Y{Vtahqi?tb62Y_x8S{5HVy|w-n-AR)6R8DR=tEGN(L+F3)Qe$tavI*ZvyV z|37D8jMwDl$I|7}C!bgFRFMHs;l8SC`67GILnj@DzO$za?}pmzDI9aFWcCz3{jf91O?mJaq3-?4|VBd!OWDX^(m-Q6TT$1-r>nQQ>t5@qa`6I8h zfn}{Wtl-|Pr`UbJNl|N#=*8xXz7#3;DnzWe^n8}xt_BvSn-$RD&wXt1kO#dhv<+mt z1O4|F7`a27vnul6`rrS(C>3#R(;2H5fB*ZY=UX7dtMx{ODM&-n8$d(x;`#n*s}q}_j#~Ha_QXK_H9rhBTekS z$B$+0nU{w(U!#ulr{sXARS!5od3a#QqZAG$2RyBM3WxjNtuuM@;sd^I-xgO1;}f^O zmgb5%UO%+z6(?dg^Hx)${Nqe4WWs`&^>$VcWj# zM`5Kp`b)5SiVwf1>e zJ=>FL(XEnekx#kTN&B`D(^qUy>R~T5vWp#%E0hF9Ud9WE)V}Q zddI(!jKV1u^Wt4>1!p1Y8oNwwzva}wwkuTgPL*eHU)6nG(z3=y(NW0rhs%%!6YTXA zj=5DbdkP=&f;=JbiuD_mVQ%L|%1<^D76Q8ORDCVn6M=z!8}5@il&oIXQ$TY`-aDPc?U|Q{HD9BS@~7m0r&SL)KzVpz$DJ=wqHsr^Ll;S4T4(~A#4|_JC@SX-#!M^QTQ{!Ju@L_FF zyQY{^_%O>iG@^oY=tZJ5)@uxxxY#{IZU2o%=GecIjuO&fTdaZrHaZ z^~DR}tZ0xhE#>fgV(D~wwyhkyUfQ>HxMV22eS^JFwvIyEi$F=g6FjmzN(virAWE^evX1m z`7+LVXWQ#39CNE=_7pzk1$jc=!&0t`D#tE}l%H%QECh7lsrp*DCjtZeHryw3C|SL% zr-0^?ymwkhiGN=eCSTbw@^cKc3#)s*%8Ju$de2?|o;nmDrrY^yZK3+7#KU{rmPr2n z?NKh|SB%=7FMst0>s9I#6$#kiQqc)z+nE??NQ z0mb)uutaj{+}ieSP$45t?7qj3W$l@lhc#cLj`FAEfTvXtI6!%LV8^2r4kZUXt$GTF z``)cHdGg`|zHQ$YR|(@2x4xFDPvKDZ6h7nyc|zWcZrv&VN7_)4@{^5(g@Eom zRbLDDL||awhWkX-%X$iEF3EeRb(Hw`)fnG$Lnb|sVFx@0*o|#_N>JoD(=VaE|&8%klc+euz)^-xNo^Ufn$6 zpYC_#)z?c|y{UTqq`1=OH<<@Hr{H1x{WfDIwDt?CNl_j?} zfgO)hIFualwCX7w?t8b+MTG zKs@Z(fWmtkPzC$8IsG1f@{HZfZn$_vC0l!hwK&-$>*puFtW3RPFV+QbWIO*0aJZFy z5ldfd0!qD1GxNSU6 zv=>tBdQvc1prcUb?Dh$b7rtf1j&yUnv?P}Ob@{&}f98s06i)X}VeVryI15EK?aRASx5mJIRaYhDqCp=vI|^-@4?iMowAWKO=2pqAYLO}POs;`B6A~3LT!+kP`lGV$43TQ6Ld#81j`1jRvpQFNaN5rs;6|yz#@b_8P zsqF)M`vZYut^Ttcj#_tGJQg_pRsF&SmNReJ7Z;=bSg9hO69)PRu?+Lubx*R)OFa5^ z&-63XVBaN=+s^U*zH|e>|9jOYb4w==J;^qnPEoD(WqRL6%@>>#I5%*P7N5B`X`2tk zr~7ZpOub&+Jj(0wdr8#SOIf|CYW$?Q(&sms2Ye1v2DaaC`%xLK{UXb2R`Bq7{#cLk zFwe73s2=QjalkI26ystZ}fgO)hIFualwCX7w?t8b+MTGKs@Z(fWmtkPzC$8@DJnr93AG%>V(gId$8qE)@fg|4+Y2UV*yd8 zIwz~SiLLnZqI}CNi&^uuf%?1iSBX_W6w6v8u(7zVeBm`A-~T=6w9_lr6vBqYOEuS{RaAyZM&YEt zSG&LtCuiYQ%CaH3_vY09H|2B7(4hwD_uG<*2abGPedm=EfZ^M0}>Sa9zG?(PP(>hB0`)Z|RLPodq zG3>lc{W4J(&$5DU4O&+%dt6*wQ^ge_=sQH3( z0_O(K(VsK({MB(6#HagjilbhyZXWSZ_q*}x>!qyTR6TxDTQP zeJ#xubG&|N)hkZKY{-kp!^8fV$NpON7!PzX7kInKQD}F$=%4it z+3P7B^ZrO?PvJvekSF9VcuW&RyM>9ApKK&71a#l2`dYXr0t5Rt+$XAD)>A-pN!~lH zqr|_jhCY1JyzsFY=HRuflEZ>?Y(&VO{pEd+i!Lul+;ezwM(ot}R4w-m2G;CWCAPx# zAiKM?&c{`;!R*b$Ea&v|c8P@wH_dc&6zsd?amy?o-zH;61Ka9fYErEir&yMJcVADv zLhswC`GRu-=LXJErxr2mKWvBibpK76sn@HUM|nMdFNykkDXTYCjh_@(`uryIklx!V z3){ZUSQ)MTqN)pW9zNJ9r5F$M7)Prf@OU_2mr#mvF^}jTW9j*#Rq)b zzAdg2#wTulEzK2kynblaD^A30$cx9r!~U4Z{#x}I4|pCf*zqXF!94cYs^{TJ_&SrN z_PIpg!?u0fkHSiI#tl))V?4~Ogq67|#Z9Ok-eVvh_H01mJq@UWecPA}yK6_e?_-Yl zJG^{#E&VP(=CWbcu z&>%_gJFIDwVRwePJQM>9zewI?*cNHuHqyCC_>x=p!db6ON3U)D{&z%oZ>azGfVXV^ z^ZvmVePY?;m~3n2e~MrfPXDPpPyg{evydlyapCyv?|(00QDK)})1-5!>$&aDp{Fw) zg@I{GUJePe*Hbv=R>|xse8>y(guJ8P6mWQcB}}CJWFuiAp!-hM*TOv!7}&SrKAA(w z>Sa9zG?(PP(>hB0`)Xjj9OoA9iea-Sbhve_!FhIgQltJcC4$7TMzybA_c$v)2`FN} zGns(}`@LA+{I5eSJV)w54d$F+52_TqJt^sKu||jB7E1=hzDpi=^}IDxPqj3#m8r5u z7a4z=)peXS@`-DZ^ge_=sQH3(0_O(KQ5vuMl}2uc_;mkGan$S8%_IKlem7oyy_D6P zs>e@?D}8>Gd607o9=3g(u@YMQ1=XY|51;a*z+=8V^zM7fji~TacGRlJcs!mLFQNWCJgs^vA095{hvgUt^E~^6 z>Us83e4WWs`&`0#1`6NXI1|wG-qV08*tebCP}A?#mwoJE_}FgK#~)+qn{+KdrKlh4k-S!|)Pb8> z`SM%aWi0Q(dIlX{81rhK`07=7bkzsV#eZ{rxb^M)9p+JgV#>Xq4@K9$wR?4$wpH4< zjeC96KifllVP5rC7muuQ6dcD*uUDwcTQ;;yN!`rDvF!4z6^Hu9L@)|x!jz_2%0y-s z%D)Srxp7oZeS>ayo3(f*ojYB_kJAU=(m4v_u5S1&gxc#V9CPDj_7pzk1$jc=K_xua zzOHpqr2MQ82;Fz8zSi-yYhmAp`$W~tdPFpr6Nl~+Zokj-Mux&$sj}&Lv=q}fq6}d|9+o<`1a{}iE&e7$tQBl{{ zL43OZrp(mq)y<>49>14FeZ7>`o2tf7iYtA7lX*z*?UaRW-)5|g)_zgd1vw8N?37ZB zhk1;nRS$SP9I#6$#kiQqc)z+nE?>B>1BLrEP_P8vOr>)R_i@mJy*5hKkCIc-c=kL! zg-gkKcv|&TTrB6s!S^Hx)${Nqe4WWs z`&`2JEKvB~#+iVgCpX*K_H7o0lKN(rV&1}71EUl-p>}wWfq2-n0fqN8pbGYFS&QG! z<2Z3Yo4RSyb>FfgyYOY@vHQpT*qa)|_HPolu)Wtp?2a^C#_sG-{`Y(T_2R%8C-0Z( zo_PPShikV<`?ld1cYRp*(q34-?|Jf-OB{vXF|T6_ zw0z4NT-sJF_;@VKJXVZ(b0>mPI3XFDZ%ZAPSy-wMnK7q(PJPI-@;4lt#Y+F4bWljr zX2ByJg+m>uPThaYUQgk`zmLTB6h7nyc|zW^?qo{$@W4fp^0PiXbl<7^TF29_g?$_D z6ICzk5z$<7-*YLnxH|dm5?%Aoj$tlCGhDF?zQDXQo~S*maIn}!*Yd!MsB_|zzo%xd z@gjx|Sg+gJYvy6r^iUg6F4IZ;mT{ev^5Sa|#}|eVefo|D%3DQDu~eFLU4sFdpWyyH-8m@o>N{p%mj{ z9^?J$`nY`Iz77=b=|I5}c(|W~+_rCnN*HNk_dR|rYtOtqtoa&sls_d0Jgs`b0m{Pz zJ07KQC^_J1)l)d!_imlZlNTTGZU3G$t`f#4ZhbAy6?43PXw@rD#B9im$HT+^n8*HF z^%xI$9xmAND8|7&_SdTC;Ys*9lcn~#MBl@I{G8&G&p1FB%(_Wh3~EY6(Nk997xdfB*VBK!6r?}|lD4zQq`kJ~wjTiDwhZ`0JW)@mybr?Uf zSx$Y=LbuwEKOpVfsgrSP1C8Q}wlQPXq?`ZMaWVy{xBz=90X3T1Sb0U+uL?ceB~(7?x4=S}F*kEZwaN zfA`xSEOt)bJ9NU-^J24t_eXg~fB*fr`A&!YYad}AUv}o$+~Fjv)3Bv)j)r^0lXbQS z&1nn!E_vK%H45GLtYTmr(~Z7q_~$H(=;e^=>!8#P~WPT<_YIhr4OWTQa`9RsQ#Rz0P)X*dF-cE4|qHr zuuCY#xR}Ryzq&pyU)Zw&#rJuzL~`le+V*WwAtO!fzQ>Pc?U|Q{HD9BS@~7m0r&SL) zKzVpz$DJ=wqHsr^Ll;S4T z4(~A#4|_JC@SX-#!M^QC-Bb>Rr~9!I)fc}R+*Hr*Wi8_}>+S(oBlD;y`KxYa%hJwV zF?HSww&2dt%Qu>B6u{8%j7q)eXy(VpU?Z^r+= z|J}&``Gzk~?jYRRu>8S)lN^Q1gA2}#uKJes{<~^6yQ{HmZO1krGXzI43TI`yo|TXL zW)@shj@dDh<fW{2)XY6&*sHb^ zW}aFT%3P)t@JN#Pgjn#nenO7|7sPtcM(WRBjbY8A95x9~N7+9oJEyIOI45v!;2gd2 zOVO#pLWocI-xNr_Ufn#(e!SmIpuS$p>P?m7C&iUMzsWqvIRy{f@3$E%p|xL7O^WjH zDL+b%@i345wCVwmhXZyAr5G3U81Glt$K?xqHlX-E50*$Som<)KUJF9PqU20S71#5A1l9!lC4Vr&UkkaNoOiCQn{`z_;z&;woW$;?~#FTrtP% zhgQAfM9hY~csxApk9q8`Rgdw2=i!1Kk769mV}GrB9-f4+Gg)e%OY}W#+qeBFtW;;* z5S2W}!@NpZnVVAFgxcXf2I67Q1{B`YfGXIxrMR|fP}LL%*n{Y8X@+{~S@DBz`_2qI z$UcUZN|*k~R<>)~r}vud(x^obEdWH73I~2) z3)@rpkQd|$c~8kys(qytmqp4?HWC&By6;qdE!-1Ajt@u}xdp4l>J`a{iE}dK3z6~m5 zq>0`4__3@#^YXCfYt&KxlpOH1>H!BR4-f2kl)|CpfTvYY;c(x(btX?xWjo;zZ1bym&l3?2mcuuT_unfal?Y9gkuh%wvD8dLEvHuQOR{pG)*T zY}>c}D6CXx+z^#K#>2cySectr+=SZUJqF@o&ju9U(|{`2x3%k5IkNHG1FZO}u1Bjo z1+Xm9Ydl*z9b%jN51JBKVH?~3$#rIh$E#S7w^%Uv{U)(cWc`en?A^pci(&_kI(3&_ z-uYnXvHzZkX$#sW(-3saL9^V;m3Q{S}Q&^p5(#QlETq(Qn>rJ6emA*HjG zYLnbSPvMwbrNj0VKI8>?Lf-FF%uIjD{jx~;$wtCLK=+-huZ4ReFtBgKeKLoV)ysMc zXfDZnr*)M0_tm?H7S--wJ%;tm`DbW})?sXlpi9>^;Y7 zT=zQI#5F{EAHp8ge8D+^a|7oneDT12$)`hny8os)>hD=1(ZBQX2P3*qMk7ezdmxnc9qmJ^Y&-1cwon)6b>Z^Jgs^Phx^{G zGkNmj1HNtF7FP-56SuyW=88F9KeXx>IEP2-2Gm{Z^VU=86_y;pEw)%m7b z)%ROnN1?@5??HYU9P|{9xm7ZI3Lo-K>v~k#J);j6rZgaYMi(Q7V3jR_7_FeM0AG>#zSW@;p3obmqs?TM5-$u+yR@)YnT{ zy{T&aq`1=OH<<@~4pIhowtbtiG+O&bme;J{;q&~l9^+x2XP;0#*z@9mT|z0w#XQFQ z)%9`t!k!H%zR!atl1t~-wr_(98EInoJ$@`}&%8XW`5JYUKP3k|t$M%#%EJRY9;I+7 zIpAs4Q#joBZk@@K7a#C#`?k1B7@xTHwKP}E@%o`vuQ(C2Auk>e5Bp;t`)k!>Jm7h_ zV8^2v2lLoptDc7^;p6gQ!Ec#na2*s}qJ z_cWji_HCcW8~8AXSO;WvOFPnsvoAe=;Wz4%Tqi$vdqj1_648LA=cxIu(&5?Nw4|C`o{;y}#rq?Uw!0#7@{&!4NkI3V zsxJZbKNG{g4fn|$Kvpm7fze!&_fG35@$aki4CmM6%@V`@%37|8SK5ngcCHyy+hjN? zc1e>w@3YIHV!C!S8s1nR!$J#8I~D$y$kIQ0+Tg^1Gb~TuiiR`uyv5x~C(h4U0`^_< zxJ&O=tC~Kwfi)daFV(OE=UGL&D~%pEKPkNrVGnA);GDp@fpfHe-i(vY$3lF%|E4(V z_3Gvk|8&0_ufAT&>P^+-C&iUMzsWqvIRy{fzRg$(t^I;(Qj~{J`B8F=hk5L$RS$SP z9I#6$#kiQqc)z+nE??NQ0mb)uutaj{+}ieSP$45t?7qj3W$l@lhc#cLj`FAEfTvXt zI6!%LV8^2r4kZUXt$GTF``)cHdGg`|zHQ$YR|(@2x4xF@%PyJ!`?2nWl?Y+0N`3D|P zTDV~yyYlbR?$sA=6*J#;*?;9;C($AJ{=;r>qgmGlU$*Q&_f)KsJv7$;+aBrfllq-; zndw`@K?pBa{&Vscj>5fpMMmuV_J%EJoOgC{hnH;5u(rHDWJI|@153B;@?-ZCEa%7;U6)qUPRi( zX$D+m?_M-J*lqqvG2^cGQ!nHW6T8{>jXAO?hHVeKUc>pS$lkr~^4E-1XIa1KfUGfo z-r|EjYesy`2m3C0-0Mw`HGAz~VC#17JA1bL1vX)9{y*Per1x#qe8D+^a|7pSbIWs{ z=SDz$y8ouk)a%vFqr4u!mqdNNl+~N6#!reXeSVX9z~>-kVEg^HAC=MCFS5L51rML+ zkM$T2^E~^6>cO5D2ka6`F)rpY-mk8Y%NO=+K=FMZERkF~x3+y7RLDpZyYKO1S$pQ? zVa?a5qx>m3;AzzZ4p1H**zqWZL&*V8tDeH)zIW?Pp1k;gZ`-%URl@kht*@oIVvg4j zt$M|Ym<@UHczDJ1cuRj_X}{T{?2R@r}W&2fVQS-s0ssy1(Nggv~y ztYq4vo~-xQR=J)GU(cTGcw0Ej$Zg_3Lo!!mFFK3C|J-pe+V&nBSZd;*6B<7gr+yZ^ zo^J7$e!ngMxj%}fZR8-tJZQP(P(4TCRQg=Sle~JvGBr$9{bY)lEcxb{tIqX~U=&W~ z;T_63w#Y1O?0NOz`_(!0`Jaz!b9=;V>F*`HE#*++-(rqJ!OG{~`;>FgQ#kPd|Ht+e zKI8>?Lf%C?<~j9mm8&AGdt|N{4TI;DO3A}H$5f24`B~# zzTlj|xq)*O{O;(vLW3bb-G5UY^?G&lh=01@jaOeUW%Z`&@sr|8pWkF2>ep>Z_$HM`;gi?%)d5rg~>*Ml;JsVJbp9f1Mm(Hzi-v$*j z(!}n2{8-kWd3jj#HR>pTN)C8h^?(DEhX-~%O5sp)z|*RyaJcW?I+G_aKH%H-ZE=+_ zK5^@7X|9;#^+T&(aUy0zUOXNi_QyQ-*Q&>O!1Hjyjz=*L=CQw4Jr7U9*O@G}&n5aE zw(Z+~6jrJ;Ziq@A<6&MUtjtX*ZbI$w9s}{PX9EiFX+Rb1+n(ernanHOVJ1|Ww65Oi zKvrpb)|&r5I>IKsJ?AmW)01s{x;vusKO0!)G+7R1>bzZio#I@*u8v*BCY~jIz5Va8 z=rzyU>G#`qMej@3r;UT4yV9;}*XoW!?QOGaR(bG-jXv*Ft3mpg z%v)d9*S$>yqi~$h{`u^Ajm*NheC>;E9+^YmZvLKD4Q@*NHvcBKuRVF6$5E)aX+o#Z z)gAN{j(LA1v#0POFUS+}o~!e^*U9UuNcqV|!a_jzovN>edm=FK`)#;SRK2XHfaa3C zcUnh@e_#DPHGThz;m_IW+Mcie&UcCR?OFc**eR#PC&692*z3c@A{kFS4H_Q9u0Cx1 zG}cGY0;Y{U998EW+Yx;)@S>fM=vK0K^4u9<-zAUhH|%Sn>@hLS$#L4}m(HPV*{Uu> zJVNPx8#P~WPT<_YIZAszqEg?!5TEY9DKqtYb@M2%$L}RkUoU0#rmFFi;!2<2WFFFc zJ7r?qLC(VmJEaukVIJdX)dL<62ka6`F)rpY-mk8Y%NO=+K=FMZERkF~ zx3+y7RLDpZyYKO1S$pQ?Va?a5qx>m3;AzzZ4p1H**zqWZL&*V8tDeH)zIW?Pp1k;g zZ`-%URl@kht*@oIVvg4jt$M|Ym<@UHczDJ1cuRj_ZXwD07IW9tsH ziAz04M&~}xwtYQ&c+}XV?2L2Q(cXD?u%cBC?>};C1KWQlC~w)uo?>a2nOR2U?AYLO}POs;`B6A}~~+ z%%NoUvYrB(OY+`n9VPyKby_3;QKwEkXB#sMYY$Gm#CnykxN6$hQ{v`;30@6TUlh}M zKX3o6M+{3hss78d83WiyF=Wxuhv!&{gDy)Jy7-7o910KUnF97*^0;+hW-ik`I))XV zF#SVVt56o(YSYj>jZedT$sDNpf^!1r2F_7b?5%h6xJ$ zR&S~tKPj&C`Az0Q&MA1<_HD*WXzdqNlcGF)%8!y`Jj`Q1t$M)Y;ecI2DaOS-#{1Rv zarwfY4Jf|PgC&wn=hn7wg9;gGV)s3MENjoaJgoT|b(B9P2RyBMzyZp`13Mn2a40$8 zY1LCW-1lys$&(i!@NN6HxJnqGxb?L(SIqJHp;fOq5wjsL9uE)uV;=i!)nh#1dAMN5 zqZkMC*k7xjhbQ6dOqSZ`5`7Qb_H923E7ch{L?w^$Fs~9;=B5-kp>}wWfq2-n0fqN8 zpbGYFv#NKxAL??18D5zRp7$T=H12P8%qX9O}@{1HPzLu74}@Lx_r*Nd^`7w&&>O_N=`oWQx9?wCe1v% zXUq40_xZX{%iI0WzF~Dot^D|Ro|nvN_SQ6a%SSK@Cxy$A8NoR+3mvu;eO9k%4*k?J zVp#L_Z{XjP`uB@=%-cMjqfq&ez&DdxJLoAK__N6wq9f_fG35@$aiwD<(_%+WR@HRcFtK%(pMG6pef;l^=Cl zj2Yf6(_d{birJPAopiNT49l5x>hvUI0+>tJ6TbgVJI^ZTKj>Y!hL8B*P|+?U>|oy| zk9(x?NS*uj7}l@;A9bEC31v58pFf^@j^4LX^9APw&JCQSyZ=r|>CqA5)BQJPre3dZ z9_97;y(H@ErL5jmHGWcD>GPY+LwawgENuHWV`a4Vi>fZjdH7(blwv&0V;rq|z~kY7 zT|z0w#XQFQ)%9`t!k!H%zR!atl1t~-wr_(98EInoJ$@`}&%8XW`5JYUKP3k|t$M%# z%EJRY9;I+7IpAs4Q#joBZk@@K7a#C#`?k1B7@xTHwKP}E@%o`vuQ(C2Auk>e5Bp;t z`)k!>Jm7h_V8^2v2lLoptDc7^;p6gQ!E zc#na2*s}qJ_cWji_H8%MziS@7u&?z7f=N}kx&Bt~pMJnzAK?R~`<^S;gZ zOLmvmV;qE@j+skU%j+ogJW*>#5Ah9K((KCAc12z?*HeDeSLcgh6pri8We@kK&MaJ; zezRolWI6QN2H)^}?C=)utGe0#6+Pc2a}=KSa?dVybcXvMy*TeM|cXv4Ka3>+S z%;JFnAw(ep1SbLs1ecFxw`Q}oueN8m-+nLm-rheo)m8QD+Uo92PrZaQ3Acknt&?FY zvwB%h0qslj-f5YV_`cfdM89Dt*T*o=;&X3Rsv5u+&uCw0QQUPoBK59i4R_v=cRMe6 zI-^l6t2TN<{>~51vsQo4OW&rjFZ)pFY_H8dJ>;qG!!Nab7t5#_%YO~3*!^ZKyV3Yo zv)X>Q*@}eJDNZ%@f&FC`M(r;+Cva}y9OasLbh4(i{rhdU?MJhZ_a7Ue&y)Uxh}x-g zh;n)HhIy6mZ!!;ZpF)JA-=?bsuiiX6RFjgT@tBKwSm*g$YX^VcJm6=o#9XXnUQ)Lk z^MyVeNPM3MPc*KaTSvbQDx{P4UiZXvS$gK>VJTK;qvEM?;ECFS1CqxBKc1vG)Hv`& z?G%Um-mTF`^5zG=qu*w%gzkx3UsHG08gD&B?Wz+oIP&J>@o+rWalEJ<^FZfu!H*{~ z2kSUq)Xw86e2v~yd|#rU!;XI2&%!Eo#tUMjj(J$O5o;EvmTRpa?qeVw`fMQKJq@Ia zep|6_OD-f7J;Q2mbDuT5%q7;PZ1rSEs-I&Q7uL_YcI`eksz&J4s`t0CP7^YE=PbYf z$6xLB4{dp?zx*lZ_c<|*p0Y7c;ZLXd#>%^k#Q1t=_L5&4`fbI-RzKf3+etcJG*!aw zAAk2*u!;Ah#-0gmjOLvC!iw>1q-*DnDY8Z|iZedyY4diUvq>FW_Uzm9Q66njb+0KE z|1|d7-aqI+X7zVh>C@64f4>{%q@_59rP5%3iVt}~o{;yX;?IMw4Gxj1II}HbGC;38 zRbP{EJ1EpT8KyF;m*o`Dz9jFRmMMwvt95tpjLk7KhMhQ3>uI_b0j%rH?4hp*YH)#FT~}b)+DpDHpnl~0mlHkYc1vE4eiKFa+dg(( zIX2yeShi?SjiJ}m`mvn_hJKW<(fu}Rf5AC{a|7q-ZK*|VTC}x)zslh%;DE$Dtb-p&QM+0W_*pA) zeptu6q;5Cn3w<___&yJwXk0nBj(!_dNGI*R?uqBJ^vuh{QmoEK#Z%+J6SV^eB##Gv zJV|k=ao~yCDGv9&TceNU%@2G>zs*<)-4nOIrtYdW-g=1IRVQL_vIpeL$F@EyoO_^rd})q+PWzs+ekE?^I^O)1JYiR)_w5^Aa=fA6R&qz_2NBDh zq_`IuKX?D}cb{e7MY?3)k-$#;m1Fm=n(@qi*`RfKog)~}^hsLl4Ws3f?B_@C!{oS)i zfk!^FYmpwBUu*fx7d)MJ6)zUcdS6LdZuR~P?4qXgiZtbKv#@KE1Jkbj@%wF~*SG8a zknXpYPPupRf@87l^t_50mNfKZORr1sRyVw%ybs|_sr?1#1kMedqcTyISAK71|9+co z`w@S-|C&ypC;cZ8wNv#Fzs*<)-4nOIrtYdW-g=1IRVQL_V#f`Vi_Tv*@><$wZ10un_dEN%l4sPfo&VF^qw;%0 zzs+^!N7tMioh0`cyBa+C@pqp^7oJGwwYswUcX(yI&6u2Mj+6i++Ma?(;9!%~^~Q+&t^@`Sus zR5^AosL5TKiZk01CIj@kQ}s0ow}V2hlVK{edRa~Z?Mw3BX_=DvzIwal)kz21$FR5q zb5lh%3}nr^Z64dB>kT>Pjy7`Z9DjNGvOWb0=ZIzJekng^V&;o1VEx&yk<)IoK9iUC zb>8P8kMBOA{<Z_7|KJI5%*P;+SjW zl@09QZ?kPb^41}#+v)S9&s@|_eST3cFWxY(^8HQb0pEj^Lo!FdO;;LTy?J)Byat14 zJmzB_)_MNc+QFYU5BOOtF&FEYm(=aXe4)<<65r>+6OAk9*3oZ+3hAW1*FEuEmY#We zSc=uzsCa4|c%pXTfaLMOk0&V(H4Z#cJH_F?cWd;Ky!nCe=(iawp?l)i*VJ9L##;|j zyXr&?j=cGJJRFa894~6eJkWVu@Z(9$!8(o?wexriU!(UF- zV;I=0#EBvtY+eXrk-zx#CQ+&Oym`~=qKMS=dCo5i!9*Hgdj6%o!T zPP=n83pcosO`33i&xm$o@@PlQPvcjA{(I&3+X@yg_iw95uF{{=KR^C^sgsuC!0&5e ze~J%zL7tFzyWV3KmpgS=rsB-DgvkKC?o@qE!tJ0?>qOPdatdf)lJ`!_l*IQ{ZQ062 z9@dIstv2u77qc~xEi36XvYP)5Is5KE%5L-Xmo;zDu(e8JC z{5x!)KTIunQ}&Q|R^L)=gAd(rJM?L7rnpV9tZc=u1LRYFY-Xe5{$6!&D(^!$Q)+*~ zIe~Kn=jd{^lH=1#_V2gZwjc4Q`>*NrdD4FpQ9D%+Q7$juFt76cP3A%FQ;2Z%+jN!S z)thIBYEn`(9&<4d>pXvJ?cmRw2mGv+n2UAHOX_xGzR+g_iSP5^iN=+4>*%*Zg>=&1 z>z;TnOV7MKEXC?YM zYwE6AU>(Pc+Ic*MuhDyo?@RP^*wJtM zSy-jcctLE`F%RoDV$H(Ta;^2leGH^SpA96ur-4+_ZyUHQW-_aEj-_aGXmI6amsw!J zG~ISJ(y~nlKc6cw;{fXvd^y?QcXl%NZ2XWn`47vrN^F^)qs>rxa)W#g2Kzo^AExFF z8rJ8vysp{D(!0|fSNd&3?`v+)JmMt%@~ps#$OKnu(U$z-&QlWD{)z*}PHGp=Rt9#e zG|wlTQ5=`nPYQ27m`(Ed>QeJzt32A!y1y^&7^U>vY86|#X=lh?S1I}1shPHKaMDs7 zL;uLkpW;JakSF9_!hi4jVa4vrRGitCFd3lNovN=%xE&Pex8Zf7>SZ|vv@gkfr)5gw z`|9iJ%Zhmvj$y;Mk3GNhb0C}ix}T>-Ahp_odGUsMmG5sd59Pg`@^JLq zbd}-Nn`fu03*(~kzyXPQSO-6lqIR_$@UvFp{IHIBN!@PD7y4`<@qHdV(YSJM9sM?_ zkWSir-4oAc>6w>@rC6Pfil@ebCu#={NFERTc#`5!TOQylJlw?-eyn;-a&ew(op zx+iXZP2E*%y!8;Zt4_q=$eWMH!|_|I#Z71@~ifVo09P=6*9G&OI zWw!s-zOz0nw5-qbOl~Q@9$+i#?S7PF%`UdSO1nYD_Z*g&xdtD6>^DrFKX!e=-^)H{ z&pZDb-gC=qdEup*BY(SnT>fnM{WkXlgNJ*bbCO=Hb9&V2$KQPpUp)Kyl;H`ilN430 zZnt>$tic}xd!Gnr6lcou*pa)|XOleJmH2aE$vj$*t0`|}YxV)|tD1c4YJcx?+f~|F zXaC@fJDs!?$FNjp{uCebf;=Jbkz@Yty=d({nTj*p5+(!mx>NNv3Acknt&?FYvwB%h z0qslj-f5YV_`X`Y&GeAm>0(%s@i((&Z6CyHX1=rK^7)(c!QfvPzx^XXc4|E4#_?w{ zEUZJ7uO*sZVpAXeR{hOLKQ`+?aw#CvLvE6?S@$nqbiZv}(3!DIX2&vVP}M2di{D{w zhA*m+uGTHsUuKC?`wPwqoEtbtOZxoQcUO7)_uFjSk7gh5KQ=(0C;bNzwNvE~zs*<)-4nOIrtYdW-g=1IRVQL_r-LFNuN>MY? z6nfMxu zoO|xU;47}uj0Rg`!aba{6vwbs8thN;Auq@i@}9r;Tc1TKLuD$?Y)hC7(Cbds*CgBy z3jBT>UMIs;X7#e10@|14z0)!!@qKmS7vI^xy^UtAz6DLGaxjQ>9noaxsH(T*gVnru z*4-Z<-|JOopHI+_|9@%oav#!pU1Ck@wVIIsf*)JHsM6*1aUODyEaBt2?xFi_=^LGF zn`L4wlh;07xuy3VmhJU~E-@$RejByF;GDp@fpgR}&C;^hOW42PX4`(`twU0`)8|Q_ zxu~7`{Gwc5ykTDD`tthx^{G(MR&;2fm}I9}Aw<0*WN-cx*EqMyT#e%sH&Ds{#SVxx|ESho>t7N(YKtsm}VARYQ_ zAmKd?q>6r9r)pl_W6#N~UFT9Ujn-aagHlhaII8%0)^}Bt(h;u?vd)Y8xveU;hg~n$ z`S{GU9`eJrId{i@9wE;OYS-w~>*p+c`pK>S$QdX9Qt)%OfqhRZzu$JXZn{k01DvFQ z-NTAi{PB06CDu3EQK4M|yYoJL{gT1)tn;Gl=dUsS2Z4U-yCak)>S%}b;7DQXPmSY$FNjp{uCebf;=H_zkjFY88$XlrsB-D zgvkKC?o@qE!tJ2I@3-M~GE8MwFUu*QeM#OsEmIQTSJUQNIq%lJX!f|_iHm7d2D5D; zB}<<=cuQWGEytO!Zvy0hCUv9Y$`ss9+!MQK9TwQVv?w#ol+c>NHHMg%G@;@nk zbI#mI_uC?8e!ACxXe|5d>%&$1*4$x(jt;%JuCg!eFSA6c{RQU)&JCQSEl($`zFo-v z{Wjb7quIy%j}6f0N&i7a?Nm8Lxx9G8yvp}CnFqN~A;Qsb(^Z03Z=M~hNlDRo%*8yc z^Zc!~gFkN`@UvE8F4i$GsoRbDLZ1yJzR!av8duJ(qu&M<(n))-d*Zn)J@fLg6sxmQ z@zgl*MD4%<$>V_^Pf{Fe9C)I3io<>H*61U7^8??}Z!=ax_r$HQsk>^8w;rN))rlA! zdGqmjI3DXbUeu0xp!2xk$CH?YbsR5h=kXN2M(-)UFVW9oN5Ac7VU;@L1+h`bJgnP@ zH49VAwbl>!F^~>@Hjwb122w@8t=*#?8!qM3vdfoxT$uOj3QH3iwk~1JdA1`%)n2y; z9Af?VWOg0#eh=HzpmY6gO+4j7nh_}_-;uIgkquvhTD@R@o$;$Rwo{z^eCm!YMJk+9 ze!s1`%cl%^9ym!u54~x;&ev6HHT~_h;SCa4iY;Zbxs8fv1!{LYm}W{iqc~GG_x6tH zmQA|-?fSDu+T7Z;H)k#CGsM_$dwA)SyY`5ylDh>9h_>dRm33+Eq zn?6gKC!sPGXSO9w2IzIC>T42i2L*n=4X=}7Dzkc7P66#p^4@8glK8$Fkgi6R%;%$7 zlcH-*t>_ud+Ey#KV@)AnxqQ>B_X^brl(nssb!qAq!`8mMIJ@qg%dA&G$H^Xj?y%tB zihX~X!c*>)F;ls*%jkaFphgR;%GPz|T+~i|eo-zj-Y~E7{Y~bfyth*xj((f2GQ4{8>{NAO zTr?gyATbZ?;0IFFu9gFS)=Hco)-f-s+l~1`pA96w&x0o#SI(`Y-v$-ZNqet*;<+q6 z^YXA1tFuw@)Hv`&?Z5%a)Q)+e^SI#0lbC~b94~6;@f5yB?rv$4BVQk5zpYI59N{JQ{`fo9F&&#`^mo!y9K%v+ zus_9zydY1=+o|);i@t5cWGc>VOPCDM>rU0zB-{=PwN8eq%<5%11+*{8d#7be;`?eI z@9EDHc1E*&-Wx0W91mu{dJmtFX8n(TTf+BD$EO9#@yRl`Kk;u2%e5*+*s{3GEUfpY z>=}>TVVY%)3eQjPDW^EPVN1DLbid7iN$ypX+Qzb0CA~g$PwvlNA1jh;Z?@aY`w-5Q z+Fx)^;M~ADO1Zva%eij$@3+~uAMvOAuj%x8(ti?BJ5>)+E-&6Nuk!s(=0WaLh;a1V zbd})Mn`eh=Qc^S?b1@I=Jb!EL;Ln=}{H&Fji*?LP>ULwk&}RdQ@AKe^#+7sH=(j%;&9))HTp>2{J?kg+l-aa zJ#p)6>aJSjt%s;xbs`2w-h4bBj>kHV7qw#^=sYg?@g(M89mk8>c|3)$(R+&TOZ0Qt z(Qo@%Sf$Q*L2T4959>B!&BD}jt@Xow45UM!4J5p$fmG3N+diUJn&53(Hh*a25xqBF zWhH)(_|`t%1y&@eONa21huP=PUA^1(lx8pd+$hgZL(I=8cjUEoRcf3@_PC zk0sY+zc{&L%k+z`-#slS82W8hmv89M{GF5J<(Xx9n=`Ib@2e5p_Y_ZH>6fl(A2}_a zO$<$F71K7HQJfw=0XOoN&nC5QUU2!D@wv5MFVAiDr1&Qp{{NX7{CvJI+u|y%{C#uV zY!OacibMHRe8>y(guExX4L#8ELYPd&nQaM^0eao3`kI8>L7~=(s+Z*y(7q(^ot7zy z@2e-?O?WeQVKf_E>6?pZh7fisM}74k z_7|KJI5%*P2LFA^vswoG_uFjSkGyqA>UR1(=`$C#Q=eay%ZoS6t9*Zxc_{Dgl!v3= zrmGCE-aI>1T^JXQ2M$Qg!#enZ6t%16fS*%*Z zg>=&1>z;TnOV7MKEXC?YMYwE6AU>(Pc+Ic*MuhDyo?@RP^ z*wJtMSy-jcctLE`F%RoDV$H(Ta;^2leGH^SpA96ur-4+_Z);X%UY%Y~v@G=Dr!DW_ zTxB)#e`r>r^97c@P|e&S0}iw72m60&H)=mya_2+Q-CvK$&)mliTrhI9e5adJ-9E!! zvcZkt#lA}yFPCpowby=+Gjg1v-xhsn(Yo%*ou%V_J-Ahp_odGUsMmG5sd z5BMIW9FjTuZMxF%>dmv0Y<1rueu+HdBD$FiMd$Eyrgb7<_mo`koZ0i zo@iV-w~l@rR7fZ7z3z$Uvh>W$!&0oyM#WR(z!S9t2PBUNemqHWsBz$l+9?kAy<4M? z38WIi>Hc6!n{UapY4=be=c&&tt;e%qI47h(pab(ZqqKGZm2x2rTZx?RoX z=@VGWDpl`wTpG{*T0MNl@?znP;zTaqwY5-+Y*N>In#xDZ<<|CH`J}t^b7Q})z{IB` znlEyd!mcbm?Dp14OK}WKrNRCbAM%1cA@5(#ymtIV|EkD1fE&iJ<{KqdxXRL2zt(otc7GNrmFk;2yQjSHpPiL*_N4o5O`_lZRiZ>J^S-y> z*t2#1>{V_}kwJcRzm3{oa8BUdz&Uz1KDlSZWcKg3*|r~f>yXs#^m)=}E^4PfzbKa% zZzJ3+?Z$ke z&ju3T=fM+=E9chHZ-WZyq`lWY@m!Xkd3jih)!C?cY8-f?cHn^I@xYHKDGoIbJW)Hv z;l6ik^pU*zf$!+I87rZC;?~#HUA4wr4^g}7L=29+`FK1Wk98a`YR5d#d0g=0NzB1I zju*A_cnV*m_Y~ik=;yGb-}bYxN}ch7*r;P3)@{U^g{kFQ>xcUoNQXWfNO(^JsiNQZ z&!v2a{~B&cNj|jrK{n@h+jmWi9hI;6 zwqD}Veyn^es^$G}#iQ7|e`mN#IcF{Sp8mU&mf{$e%FLhQLtc<4%cncl z_RO_9NM0Hnv9WcR81}HlFX?)1zRD^zYQ(<0^=B8d%{?3D<|+4U*Qdtk)^xvZTIlC> zU2?~=XC1!#7xeLGEiyiOnk$1J>@TxKsr?1#1kMedqoK`j_SpUb=BL+RKauTr+xDZ` z$NP^B*j_Jn^`^@4f0|eM{wDJv_bEg;`fa*Oh^`k@laf3>6-SL@9@cT3s2zAb4)|Fs zF&FEYm(=aXe4)<<65r>+6OAk9*3oZ+3hAW1*FEuEmY#WeSc=uzsCa4|c%pXTfaLMO zk0&V(H4Z#cJH_F?cWd;Ky!nCe=(iawp?l)i*VJ9L##;|jyXr&?j=cGJJRFa894~6e zJkWVu@Z(9$!8(o?wexriU!(UF-V;t7d+kboj1EM_WblowJx%Bk2jw@pVNaKXg=cmpvEYf6H9M7Zx15DV}+{WSjN*YZ#+AQkEtEyb5uXCU2>{zH(G9 zZS$&S_IYmmto-|JmF1e#<0rUE<1_uy%PEbsmg2y_M~3|=KI8>?Lf%L7~=(s+Z*y(7q(^ot7zy@2i(OHxBm56U~zE@$Qge$X!;j`^Y8( zU)`2-*1ZsN;%<;!sM^drUJYZ|x!|Z3uYU1nWpb4|zo32qd+xGhV(%aSe%s5f)v9c- z2mLPdc{lfvc6H7W%R2oPa(?M&f7azuT)HF8=zbfuzu=s}xq)-kZoc&3@oSi$UVnXN zw%cvnkLLCGZ;jYqFLm{%s_}oCSNZ-X^HAQ~DGx`#O;;Jw^`fc^<2*k2sg;He)4pPu%*Nx~tZB>mh1aoruAaHy@9O zy5F2&O!@7-FvoN(> zYyEH^1L@Fb0}1bGAXW6+qFbNHGb`#m8~mVVc)DiS*x|M7OWmAzk=0$2vi2_>J=ia2 z!Z$96ImF6mI}pC8+c7z1@$H$1cNi}hP90aP*wZNXO6wEx?Q^`m=zft!`?G1~M}~ge zq+ER}4K3v?J)7=YU0&cS4Y`!-T=1i}tkIsMgZl1>XPrm3ICk<`7^65qV2tva(BVMwcoPz@E1Hw_3^hbia+-UvN&~+`u_Xk+&^N z{Q~Bv*I%ER?RMMtqj^34TO+pDOI^LGYW$z(RldK;Jm7nfa&Y|nZ9gj`x?W^?4F(>c z7mw|jhjpI6wRZ65%>#bcO3cMN<|TEzF<Rvxpnm0ph7xn?{!Z+m!)T3 z9+qNtHY%PP2cD=MI3RgE@Z(8}LyZGZ)J}1@@7)@GByWD;JNj+LO6Z=r^)+=@t?|}F z)UG-agClP~9uLQ39mk8>F%NVe7yNhX?Uh8?k0#YPr_>;XVe^q0a^q-qS#;=(nxR?$RNBsS7M?_tB-MZN0|g2JJ~-Bj_T_ zkZfRBk>wsNL$-P^uMa%T+}ccT;PUa9ygdBq;Y+#U_X@cUZW zpW;JakSFB5Vsm8q86ED+RGitCFd3lNovN=%xE&N~ov3!q&VR5kuj^D5upWFE?UJLTc{{We`? zMAwU|E{yZ|;HOq%9@a5O)DAoz2mGv+n2UAHOX_xGzR+g_iSP5^iN=+4>*%*Zg>=&1 z>z;TnOV7MKEXC?YM zYwE6AU>(Pc+Ic*MuhDyo?@RP^*wJtM zSy-jcctLE`F%RoDV$H(Ta;^2leGH^SpA96ur-4+_Z>xE~<)SsiF0gUhB3(|tyT&qY z+Te4kz$G^C+uS#qZ+fsfD<(8*7IBybzdyvs}mDoh9j0p9jUpyGky%i_Um|VEmxrZTosEj8#(^hl2M$Oc5BzwN;!xwj6SY$u?t8aJAIX~^ z_>O*?u@br`ZhcMNRcpNU5VfmL#Nf!AkH^FDSjX|AcFY5v#|1y0#2l>Scu_l#r|>m; zPw{<;ehxeOZ9faE)EO^`jXLIG-A1fgm|Cv2ez=c;bm+5zg!eR%D*A1YcWn6ae@wW* zJ-vT7^`POG*t%~6BK=Z&vP^ALbZykmgKZ2?F?VT|6LQ~lm+u8Ho+PIX zufHbOmuR+bz_nW|_PvoaYrFf59eZ95GxXbPuB87rvS%*sy5{Z~HoAR<`>JN#$nrrS z+qg<=%5Uhtsgkpn;ux07%%9>zUXUl`J*H#+h|}30$W)x!mM|Hh*PW`bNw^&pYMl&I znbpg33TR)F_fE@{#P`+6Q7d_bK5xBQVgHN|jA3)f zRW6+WXaH*!=9T7Y9lGB}?JqbdaBkon?cDwJ#m(C=KfV6?%xt&Ywja&w@!uM;yOpw?Tz;(%$QycrHuNygV$$>TFazH4Z#cJ8(eqc;LsA6o(oI zo~WJTaNoN%`bggVz<2c9jFr$maqDa9u3F=*hp1h3A_hm^d^{eG$2yJ|wPPOWJTCb0 zB<5fp$BWu|JcX~(dy4N%^mEwJZ~IwTrOtRkY}7Fi>o#J|!qjrD^}~G(q(h$#B)q4A zRMBr+sQcd~{P_Qo4!T;%W!#qQY*gr+Va3l}VsG*`Ox3HdC+oDrZSh$j55_!d`EI>? zLVmWcQMxOKC(HM|US|8=F@_cDHSx*9CvW7dkxklkiajrf82WAHS1m7Hs)XJrOa_qv> zK7SeeZGUEoxK*mLtMn;XwV3gevzFo*mP&*DDL&){c|zXJ2X&~}z2yU$iZk01CIj@k zQ}s0ow}V2hlVK{edRa~Z?Mw3BX_=DvzS?Kdq86DJMX_fCcDO`l3uO_rEB;w1?H&0^ z#S7#26$p{Ac= z)@+q*{*@TE=1btx2@ik#J?Wl-Q=Uzw`)$?s9h}w z{H&EYKdfV3QnwrPg+3cde4htTG_IUmN52g!q?7hu_r!BqdgkR}DOP8r;;C`qiQ0h! zlE(u-o}@U`IPgU66o>oXt=XK<9D6k0&t)>o{K2&f_V3jowpyU!tGGj(*$E!YXyf3u2>=d04j*YZj)KYpoyd zV;~*+Y#`x14Wx>GTZNzhU1T-qMcn`V_B#7ib>=45)R$S2(wi=wo$Sdn-d@mXZ#_>| zYF>*w3#XivYxL;^P7vx|=zb(_6{5v!D zb(S9P`;v7_7gwoU!hkH9H@s!*-7}ujUWsQn<~*4iJSvP)oT6o_+)vlUP5S+QHP0+L zb7_l)pZMd_&9BP8-&XBa<2v1#tJMDFi773bI%_Eo{Ci~BpW;JakSF9_DOvY}QL`S% zRGitCFd3lNovN=%xE&N~ov3tA*k)_Cia~}9dvj;Bef64u$Puwz7?kqWq1+jb`K31RX`=j4>c=h8< zujzhU{5iLgLypI=-?wM}<9pgbHotg_U$390`)$GPz|T+~i|eo-zj-Y~E7{Y~bfyth*xj((f2GQ4{8>{NAOTr?gyATbZ?;0IFF zu9gFS)=Hco)-f-s+l~1`pA96w&x0o#SI(`Y-v$-ZNqet*;<+q6^YXA1tFuw@)Hv`& z?Z5%a)Q)+e^SI#0lbC~b94~6;@f5yB?iZr-xsDS3D~pM#Ho`CH!dAmZBU^s($~YON5jw=O*3j zP`C5b4>`4|TmRWYTl5?B+s-;|b1FNllB*Q;sBzl79h|ik$Iw4A^QZWb7vu?f7h75* zUH>x=WGc>VOPCB;Sa+hnCgFBasCA<1WjO`3FUfnSWlG}vYQBHJ&#c`litRa{_Q5P| zD67+^PeAv{cjW#)Un|CK4Uvb9Zg%9E9L);#nHjJ!gAW^c+kfqz2|;X&%h?7k&Unb% zM_2w*^&#DF(^UVvK)&5EZ1%(xX+3}ZyKgD~&fR~}2fE)z?JqbdaBkonX&Ux8xca#L z`)#)EN8UOlb-RW0^fPr)JN5ZRxx9G8yvp}CnTPV;PI)-`ZMw?v>dmuL)rE1W$!&0oy zM#WR(z!S9t2PBUNemqHWsBz$l+9?kAy<4M?s#v|yU0pj&EwW;qYulG<7`5jZI{`m8!6^I zdF{z6-=6pGQS~G2R^#`TpItvC4}RHp%+>-L`TM`Qb~PRt%bxa`x9iTbw{qmLf1|WL zF3SFfe%rj+JtufiaF$jqsb1W_fvfbm#gP%iro3g>1|O+0#Xp|S>D0Stm*2w}#ql{9 zSSC$&H|c|OZ0CreoZ4{@W{+w=;~U&pHP=rq+qk=gt2D7+fz>a2Icq77VX4ggDL&){ zc|zVVulLB}8~;G2;>@;$$pF3XRDDgt?VwQWWSGjVUY1ip`;xqOTBanvuXcNK>PDk- zQEY9hujiX&31df!`=!n0_2c*3-gi5;{XvM_qT@JEd0#Zk<=Ze>&%b@x$E|y=SGXR; z=B}&Q`TkB1Ic!at&A;8D`)z;ZUD$NT+8=)pU75XI+B=Y?xIQU+xk~=9zswS)_7|KJ zI5%*PvM(Q6;Qc}S_uFjSk7gh5KQ=(0C;bNzwNvE~zs*<)-4nOI zrtYdW-g=1IRVQL_Y_$|x@_@_l^0`Krt1l( zk6eE%Z=A6(&zP`_vY(;fcBb|)-+yL0OAC*!9`v}Tt2FQK#jJl1d&|~lV9!q8jc3hg zx-{5TJd9DChMlU_T=OlfbYbO=qF=poYU6^sZk!%&{Qb7+FXt@0lHXN2H)2$FkHOAb ziUYr|h5ac$KApINZjLz#**+Y%-N^tw~^H3_$aLah^3FUu*QeM#OsEmIQT zSJ#isI`G}EQLJHfbe_Zg!&rrr#T%`Ux+6dQ^zV-2dG5-Mevc_Ma7{FuGuOj!*()Db zW9aad$J~RNXLPF2(n~z#Ue(K8j<`Vg+s>?y%KK$*4D<3hxOl_-AOBy|tlzx7M$-K@ zYJb5wfpY`r=yTIr+8R6U-*2;RKl0WgsoUxEq|aQ`PJMn+E-&6Nuk!s(=Apc|Qyz|f zo31jvdh_g5bzxjI9ylN|59{CuQq-=N1Af*@oFCRPFR9y&`9hx!B)-psCmL7It)t%t z71Bw2uY2OTEIsq`uoSDaQSsC`@I>vv0mb_ak9#|T31+Hn-bOP^*zECeC#*=d1WtF;J5V!7w0`A_iS8nP?`bL4QeFUh`!e%p;w){<9{Jnr@~)Ei%f?eD_k7Eah6m<+ za6g{4%$@!4&R@eA#Th)LWxKnNvr5}HRIc{>ik#Yto3E^CP~|(^S2bn7jr|^x%~i@1 zFnsA>W1Y1W$FNjp{uCebf;^SHyJXJOx93BdiZk01CIj@kQ}s0ow}V2hlVK{edRa~Z z?Mw3BX_=DvzM6JZj>`?>U$WoQ`QG(96~;al_xBDi;4k|f@oiYF+g&+@Z|!}Lr$@6a z$A4Q;u-6TCZ~cn>r3VMIQKL)LtvSX+K2sv5UymREz92PYttmwU3QdS%J!U*_<>wj5 z>enxnEBisZ-$v~(I45v!;2aHa@NHS&jrQ-i*|s04s{B{R>GP!j1fq7T4x(IMykTDD z`$N!)7XJvTx=Gn>e8VsWGn2&i_=lNS}2Y=o?;AgGGT&!bWQnwrPg+3cd ze4htTG_IUmN52g!q?7hu_r!BqdgkR}DOP8r;;C`qiQ0h!lE(u-o}@U`IPgU66o>oX zt=XK<9D6k0&t)>o{K2 z&f_V3jowpyU!tGGj(*$E!YXyf3u2>=d04j*YZj)KYpoydV;~*+Y#`x14Wx>G+oUW9 z8$O(PiMiH(;pDRR26HVpEVcjqD=b;&m<>BO9%0*4rCE^axEK4Q#I9XyZ=8{D47gP$ zEaPpTbsXAgUaijrOG)x`@SpcD*d^8 z{`r2b-m-N!!mq!263?7d{(HI8hfqdwf>Q?j-oBnyy0v%hx5FcIYIEPeU3SY-W53Nm zZQO^rbgt6Ipk|*-Pjl8%9K%v+us_9zydY1=+ci~+ZA(`@l&LtgEnzZ1uRB#=lW;pI z)H)faGOL&66wtmT@12$@iSMhcdtYf*>&{EI(QA8f$8_Or#GPq({5$*0&NZJr`LOn` z+^SZOr{#x5v+||o;hFqyuuOG>vi);9n5C}VK6A>>9&$;qxG6ig(fzi)|Ngcq@4y&# z>BXr7H_WSif0KDA@9mU_eC{WhqOPTG6j6VGMonU{y9Se=cEr^bONY6lKT9uNF@lHyR~ zz!SAo9PWF!Mjy$WANY=bo3RqQCvJUB-BoM6^$@kIPQ>8In~%rC@mR<4qIS#!oyP?~ zp2Qri<9Ja!kEif8dQb6viGB_{`fWc8tJE1Uh>betVckZoS(sX`wSKsdfpqAzfrR%o zkSh9Z1w(cJ!-PvLbE=&oIihZ`XB7WqXO)grsvXs!RZi`UmQhs} zzcBXOvTm9;>ASP56z)=|!_x)MT8d*>Dh>9h_>dRm33-2ry%Vt8=b=o+nQaM^0eao3 z`kI8>L7~>kFqK)oET@3>C3)|(Oi6rS9TwkyXt2jiHY4)bw>3S(*|hK1_bgiKFE41g z@J8XlAN{tWcgD5r9L+pGeb;uYdz1ONb=&#nmk_q#)vf!Bt9!_w&V;)hUqScVR?Hsh z72PR@HJ!3Q`h}x-oh;n)HhIy6mZ!!;ZpF)JA-=?bsuiiX6RFjgT@tBKwSm*g$YX^VcJm6=o z#9XXnUQ)Lk^MyVeNPM3MPc*KaTSvbQDx{P4UiZXvS$gK>VJTK;qvEM?;ECFS1CqxB zKc1vG)Hv`&?G%Um-mTF`^5zG=qu*w%gzkx3UsHG08gD&B?Wz+oIP&J>@o+rWalEJ< z^FZfu!H*{~2kSUq)Xw86e2v~yd|#rU!;XI2&%!Eo#tUMjj(J$O5o;EvmTRpa?qeVw z`fMQKJq@Iae%p;7|F?uUUoWx9?F)y;)w;=AXG|^y6u!!CR^HX^Rt7J2D9-unfzwA> zh0y~pWGs13{$*md^a;ae$QwJA@M)3dH5=9|A%6D01Uaim=d9}=8vAWkn{EH&^dVL4kf7UMIs;X7#n40@|14z0)!!@qN{OL(Nrg%U-f^+t*bd?Get-3@otH z<*L8@{;gZHLmBVM8Al#T-lSeMTYoU-Tr#hltk=0?UM;$Xu*p-#*7wihA$R$^_xmL? z=zd$P9JyO8Y!t%=&H3&+qDT;1SZQU=$$bKp_aU4qwZGt;z`22Q^zR1$9aX2>zu#ut ze#D>dzoygYN&iVi?NmKPxx9G8yvp}CnFqN~A;Qsb(^Z03Z=M~hNlDRo%*8yc^Zc!~ zgFkN`@UvE8F4i$GsoRbDLZ1yJzR!av8duJ(qu&M<(n))-d*Zn)J@fLg6sxmQ@zgl* zMD4%<$>V_^Pf{Fe9C)I3io<>H*61U7^8??}Z!=ax_r$HQsk>^8w;rN))rlA!dGqmj zI3DXbUeu0xp!2xk$CH?YbsR5h=kXN2M(-)UFVW9oN5Ac7VU;@L1+h`bJgnP@H49VA zwbl>!F^~>@Hjwb122w@8ZPb8eF_YP4R`1Vb6X!3z$(pvT95sH_Rkq;us;SExd9m#! z=Z?uK9b*TYJx`braZavRdC|1Cg=Wf2;!<@PAiZYM7pl1Z`Y1t8QKZe;;r*^Ce@|NJ zfM?^er<|pTLB+~HPva`Jdv$Qtkpgeo;g5e7S@S-geYn5u-z|GX8O4chKR@&5iCLuz zwHcdveV+86MAS~zLzK&lH_WSif0KET`xGJ^{We`Cc=hJlp_-Hw zjmKQf!#dC3T08jj<^exzCFWus^OCyVm@o9%K;rv6c%pIT+&cPgP$8YP_qr#Z%hEG1 z4@ED15eZr9FRO7`0*sgp~itHYNt5d_il|ok~cr_9sM?AC3H{R`kK0{)_Ch7 zYFC|z!I3u~kB8&2j^jn`mqIMon;cN7s;`@c7SpKZMVe`$s&Q(PYSL_B?p{#*Xgq0`IvJ)itC!^z(7q(^ot7zy@2lfa zeLgj-#Y?uh!~Q8>+D5Qee^x$Iq(Xo^r0(AX183cn^PLHwcO*wN>$hgY$rfvGvAGE| zwAwErtV9{#LR7>2aJ@H(Yo_Tp#iq+YucxoJYqITebO>5Vy!m)M9FKJzFKWj;(0N?&<4MfHI*u2$^LPqhqxTfwm+0rP zqu=(kuu7frg4n2I9@cHdnuV$5TI+}V7)Xac8%TIh1F53lb}^x7w5OsEgDp$H1ti z$DFkk$FNiy>`(C_FUS+}PP6##-_^E7%2b@$mM|Hh*PW`bNw^&pYMl&Inbpg33TR)F z_fE@{#P`)Z>jE|$EBTV;U%al*&g~JbLA~blZVw2M|Bwfq8X@14^YlyaTht|*O&;%G zIw6fOD}8I$=?nx&up`)#)ENBrsjYdU?N^q)l3PSrz{%ZoS6t9*Zx zd64@QA{_lTT_t$+=Gmc|loXA}T+G8d&)-@*`19rgKWioCVjc66y4{#B^w~h-`#gA} zapl}P`fX4lowWD5C!WjFGcONIu{s+SPmKdl)D9ewJRbP*B*me|fhTIGINbMcjXsh$ zKkyy>He)4pPu%*Nx~tZB>mh1aoruAaHy@9Oy5F2&O!@7-FvoN(>YyEH^1L@Fb0}1bGAXW6+2AvtbBD>2K zR%G|-W}&lhv7GZO+=}n!%@*YuGkWo#J~vOC&Eir+A9K*zUl z*!o7hZjMa#hF!i}qeJB`p^V}*4jZ&^dC{y=>6#llZrbOrbuS!zHNrQU_Tm07Gpm+p zc{XNV_~1A{7pdjiayLt#ch*uI%AevxUXUl`y}4KT(B~nMG8JdGB}@kBb*JiU5^e{D zS|_SrmQz6clDv0XrX;?vw%fb8@!~WuS+)m3F3;aau#eUPp9ek;htQt z>u>8v$40SV6ROmYo#M-W9p>ToBAwq2h>sx@+rVZD~M z{_gJ;#C$JQU0tL#-EX7z7n~C~H*k)6r@YpGQaAhe+ics9ymd(GcKST&GZ(c}pI?;A zi#N=xe1DU9DDUl*hoj%7s|>H+JUdlg7#EEP4oJ+yI{1MUwX5ZTpS2R_hjq+L>ULwk z&}RdQ@AKe^#+7sH=(j% z;&9))HTp>2{J?kg+l-aaJ#p)6>aJSjt%s;xbs`2w-h4bBj>kHV7qw#^=sYg?@g(M8 z9mk8>c|3)$(R+&TOZ0Qt(Qo@%Sf$Q*L2T4959>B!&BD}jt@Xow45UM!4J5p$fmG3N z%REDSeQW(I?A@q#MaPBQVj0Tsne}F`H)~U{XZC5ujeK z)33vqLUZJD&qMETiha%e@_&t3d+wclU{3Mm7w34({)T?rj^uwXFa5+>s_v61bZ(@J zw6*{BYUdvR=(n{l-5@gE8}{O1+a12OLm9=HdLh%kd1Fp#g1Qm)yG*&aSThP!TuB<@`5}e@2Vwkt)7;6UNs+ z=T$aT4oL65WKBpE8}aT@&8l&}tYu(vm!Rmo?8v0AtxL8$EJsfm?KQs)-EUi;ZN>4F zZ=+fF7kk^b3<_eoccptiVIkdbqxKh^6F4_;j$FM*w!PZM{{1%F_9Jf{lDeHfPx{P7 z?bPQNED15eZr9FRO7`0*sg zp~itHYNt5d_il|ok~cr_9sM?AC3H{R`kK0{)_Ch7YFC|z!I3u~kB8&2j^jn`mqIMon;cN7s;`uH@3t{C;J#y;qnW1bVs{7rB~i(lU#c>E}AM( z9^>Ruz1x6Ld1p|8@0dSoG9nUaE_WRp5*_&k^TE^w(UpWIwW;F zeV+80i`uErFUsY`8|GEMzsWq5_jby|(QngLhF5Q%ovJR3i^c;7B<5iq{6LD@)pEek zT8Z<+I_4#HyD?wrvw_6-dGJKz%DHv)+n_=^Y43GUJeQ?sULKZWbv7!V8V8=J9XKF) zJn-X5ibIV9Pt;CvxbNK>eI##w;5+(l#!Bd(xb-!4SFQ2ZL)5N15rZRdJ{}LpV;#qf z+A$Aw9vA$05_7PQ<3;T}p2FAYJ;nDW`Z?_AxBV=vQfIs%HtLv%bsMo}VQRV7`r$qX z(xJ}=65i85s_3`vuGe36rrqT?Y)fOW22-=YVP86Z?UyHID5E$JkF9ju>Yqh&d*yZU zb`y8)y7cng-K~^*+sx`^IR&&Y$$O_|O5*!!p-Al^r+;6tEQ4pBD*Na@ z`~BTNTaMKXloy_F_Ok2dP+6OAk9*3oZ+3hAW1*FEuEmY#WeSc=uz zsCa4|c%pXTfaLMOk0&V(H4Z#cJH_F?cWd;Ky!nCe=(iawp?l)i*VJ9L##;|jyXr&? zj=cGJJRFa894~6eJkWVu@Z(9$!8(o?wexriU!(UF-V;ZHxNn+2G{rB2~(~rC+K`F48Y2s&1Nj^bLzFbMnZZ+;3Q$1Lw;Jyu8OKPTFyU z3tu>vMM^Wip8uob?%Fy%a+l9PDY@2Add)`96nAItagkEVC*J%O<*cPRlt0CXydY1= zyW!euPTmI}$yA)#mM|Hh*PW`bNw^&pYMl&Inbp^F3TR)F_fE@{#P`)<=ZhDeIQ0d4 z;&Y_)qwEh@!5PsVzl;c!v(GJ5<49nrTyNow$X{1SvG$=CPmbPsn?2lq)+J-uJyzgm z>Re}M9+KO5=1)HRQ#7MyoLj1Bz1ng#`?jH8*!D`nEOkP(&p);3ejByF;GDp@fpb*1 zN4aw;D%ro^X4`(Gs`6hMr_YoA6NuWWI*4+4@rHSo?{6{><-MKqaP-@BmEqN!XQ!$Q zeC{WhqOPTG6j6VGMo znU{y9Se=cEr^bONY6lKT9uNF@lHyR~z!SAo9PWF!Mjy$WANY=bo3RqQCvJUB-BoM6 z^$@kIPQ>8In~%rC@mR<4qIS#!oyP?~p2Qri<9Ja!kEif8dQb6viGB_{`fWc8tJE1U zh>betVckZoS(sX`wSKsdfpqAzfrR%okSh9Zo*Q57y3+J2iwrBVtz7!s?AxMxdwm~W zWBysLr+AU#7;CprI-J~yd1Yh2?Ro#dYOT!RB888xdi~Bx7irny`n~?$@rHFjQ^4t5fj8{jg3Qxv-@3;r z&eDE0HvY3Ni*&iG?;iz!ao7IkceYWF9m?;wosH{rq<*)}F4FU<6SBQeaMn^B`1?rg zPw^oy$P@CO;xczn%EymnD$Z<6m<-VCPSw{W+ztx#+weM3^|G7-+Lz?L(=sLTef3JO zF7o;=FIe)~sh7TI4_NiAdCz{>9w>h~=DfN0FJW?{zO6s5`8$eDdoKO$`QXRCEmwQZ zsuH2BOY$~H#=0Mp8}(nYYtVDL-&S^PpVci6M6*pVD;7M~HkkdACUSYt@pQk9+Fx)^ z;M~ADnzJ{CJt=Pgew%Ikk+%*>-Ahp_odGUsMmG5sd59Pg`@^JLqbd}-N zn`fu03*(~kzyXPQSO-6lqIR_$@UvFp{IHIBN!@PD7y4`<@qHdV(YSJM9sM?_kWSir z-4oAc>6w>@rC6Pfil@ebCu#={NFERTc#`5!TOQylJlw?-eyn;-a&ew(opx+iXZ zP2E*%y!8;Zt4_q=$eWMH!|_|I#ZC(4;xZiToRaSCHE~g%qZ?ojF z6-w49f1O1iDmB2P)-iUh#;9Uri=1ZKTIEhnJb6K$|8Jvu4Vx^G3(XzBtJ%dk_PpTk zzsmc(m*2e4w4zHZA34PE`)#+oJl~to-9?J*cj?BOLoQN_RVi{FT=j;{XnC>F>EGV4 zu=umnyL;SY6es7}z7NYz&LXXjT=Q^AR1U4*!GCM^^fmr|+s^QEol38Ak%~Dvr5l^f zMN4rEOQpg76d&?}JR$F4#fx+-o8z%e#hGmhlL30&srs6P+d-k$$uN~!y)37I_9c1m zv`k5SUp=v7)2qIfU$E>uT_*+fd%#Al`I2W}NT9sG^|%H7q%e8J9PjZT21KzdpHo)L zlGBg5H_ll8%fwLTwYGDMUHcBoyPrgOXA7eHZDrEk^s2BSnzcF*zcy+_Fq;{cr}I}& zy5C0aFE}S~Zr~jCURPq~?tJ#|x7oHIdFznW?euxlXD(`|KEEiJ7jKwX`Ti#JP~O`q z4@bXER~cTtd3LJ0FfJMo9FUlYb?^fzYFEnvKWin<59^qh)a}N6q0a^q-{-*-jVtHY z(Qkta>7>2aJ@H(Yo_Tp#iq+YucxoJYqITebO>5Vy!m)M9FKJzFKWj;(0N?&<4MfHI*u2$^LPqhqxTfw zm+0rPqu=(kuu7frg4n2I9@cHdnuV$5TI+}V7)Xac8%TIh1F53l=3HWq+ud7N*{iAH zTL<>I&B{Ef6LU&)ojv-vp+oJ_$JigCalh|4ahl~lKIloeoEPQi8Q)dxS#qJgqfw@j z1!CgZ`sH~Z{Fdy4{Hpna=FyQp@?Ar}Ei})?2fGTnNIt$%BRcMIkqYj<_jK2+H%vQV zQTnYV-!LC<*FVawyvHcc8uw}){_c@QTH)FFM6nw=v}voYn_BRrvENpFN0H2*=etO& zcdqq&mBvL&aSThP!TuB<@`5}e?}^pk-K^8%u}sC8Z3&YBdfln|nuOayq1MSTm07(k zr-1e)dGEALNqk>*Z#tsl-Ctj@%)td$PgwMT-Mi7?W~Q`3@>8eV6V8tglUKH=k*Z>w zC^r80*2&hj_ha=2el1n^dMImBE?b?JRS(J?pdm*%9t3s-$v~(I45v!;2dd7URvGC&Hnv1+x8=G9g@18K2Q40MeWq*7v=Ke4f87B z-(()jdpqUf=(p)A!>c#XPE{AiMdN`367#SQejr8dYB}I%t;G3Z9rKd9-Iy=**+Am^ zJb0pU<=i^@ZBQYdwD-Cvp3BlRFAqzxIvW*FjRQ~A4jhm?9{BMj#i7Q5Cu*lS-1lyc zK9V;-@E!d&VyJXemLE3RRYdx@9YLNt3y0 zCQjxi=gWR|zx&tRIWzM+lR4)k_s*9#nl)}cWbK+0aXNDIaXcK4bsR5i$2`zEF8Fa0 zbFhx%W$heK<(nOz^7|5f4(t84s=}&u#tY)7j(J%36YCMCmFue??qeVw`fMQKISr(S ze%qF-xkJY`Nf!%>KYjX%h1nvs%n(29XohH#e51#*ExSdZH+mcIAF@}p?>_y-&{qyB z{bx3e?`&MEG)swWv!_L=*#FYU5ry5#l{*&2J@Cz}EcN?s6RZOYIz|{w7DM!>P2auM zZ@bvNVA)GH5qx_2j;UR3V%JBTBR`vUN>H2w#TBC$nL?Lf#EW+p>GlKd(@69$Ug~fL?d1zHZ?G zP~iJ*c%7V6dDPc?3TR)F_nqD;)$dn>6Q|WLFDVvl53GLryPapnh|POiMuE0;V~>v+KDhT~F=Sol4pZzqHm2+QqFu1~R~D2;Yhtj_$1?zeTRdtyl3 ztP+t@{B!LCoASlAkOvl|-H<5s_)-q9^^iS2)*BCuLM_b&JNY2q-;FqVjk8xe_!q3&&>mVzDmr+ zI_A}MyDMMlvw_6-^Wcfb)pM)&+n_>r8R&JdK9{#^E)Q?9b{iE>jRQ~C4jhmi5BxYu zaj0?N$=WFn``m4IL~`>3U+=fMDq+83>+9~WS>x72)~-1brz1BX$HVbh$MLdu%mba{ zf*&U_2kSUq*3R)%zS-d^zc10}u-C-thLX)qdOR@K0B7e8^x*{A%Z> z!wCjc{~y*GCr+@5E4Awz?)$w>%syw0D~~=UC{EWCMU#fy6Jj!63~zODX_)2XPfK^& z2D|!gkM+BK>1(qLrtO29?E9v*!9sDIOJ&CX6d&?}JR$EThr6~7|Kz+v#d&NAw*h+H zsrtHw2S9;-8(t^pR37#6o&wsJ4}3zV4Hkv~0h?`5&SVr^J(W{wbFzOi+}vjyVKf5zPSO}`W+y=CoZa@WxP zwh(LEKb{*`A_n!dT^V&SU+n7Bzfaj1y5C0aFE}S~Zr~gxu51uBymsLGZT{^?ZXIg6 zojy;F%w_G=>zCzn@y>bG_ir)}^|_t$(EDxn%5e4O>{NAOTs9s!ATbZ?;0IFHu9X9R zzDk@Q)-kW9+gvUZNA^34uU`F)8#hxLA2RbkaS;|1|k$2_e2iS-E6%JtO`_c4$T zeKwHroCZ=uzpdhxzwVv6JYDqedur9;}^snhy9viJm)la?306N{6Oz)_U-{Qc-zq;-y7v%9VGX8n$><(%0(mlYU=3J}kVS z!Bm-^+;`T(TimD55?dYuv!=QlIj=493lF^%ZHHMYUj zFw2nhb>nZ&boJX(_6>~e`@F#vpSkEvpZg6KisM`=Gxn$WkQd|$c|S9E#~Z~Z=M^f> zV@tRV(Cbds*DX8%3jAIIUMJ^N9`*8`0@|14eW!Oy_50P|9e7|&kECMJJ95CqkXlwz zHez)4tLyWW9=q2M$Z1%p#BR@8-?-srkvg>52mcDn6@_1Qw2HI>ku-1I_0WUcl)NUh zl@Axw{WfD-+d6kemWa&_BL35+Fke_E?pa>(F5PdV_7|KJI5%*PMuqHbwehDPgyR*{ z>o05fZ$EPDP}A*!tEcx@{y&;megCGi;qO26ew)2^vg<|FAI3R8_-U1xhjq-6wF8gi zfS<1tbFq$jHQnyY7y4`<@%=n_qH(?728D5xfnImF$-F3B9$rGLOjJBI4m?>qa6ocA z@Z%)Kp~itHYo|Eu=bqUS$;}UZz2D}lg#C)Gue-Zujav^{yXHikj@*14565F2$IIF= z4|I+Tew@S{tmAlDJI7P`W{0QzzC@qHdcUozuxg$0g7~Rp9@hQDdW31^`s#=K7)Xac z8%TIg1F50kwyytf+l^!C;)U!<*+Xw+i|=O+Esx!kDdOrkTw@%PCZ^19KOni$elhgb z=SB|S`h}9%d1>&V^UIWs55Ep?npi6Oc3;xOoKvo(9xiV^qt_Aj|F=Cg?X5p23^SNY z$Ddo+Ce~n@Sg+sK!hSXp`9ZIjhYOn+eDbAXD_Wfr6ep@)t)O}rjHZcuo1b~+u`tWL zmwLRKvBuSJd!qik>E&Y#rkSRPzDVk3uuvT5Qkk(o#fQ8gPsqDbz34Aj-+e)$;ykv5 z+W@`pRDIpT1E9eFZ^P^4oXVqK-cvyPl6}slXs$XXrUadhjx83WnlIdVyq#5KTI-zu zBri`H{I^j9=7~ZjtJeOy`^zqg1#d5TyWEs3uD}0aL1|E-7;xtE^+|oUDetw3&U|P# z-EXV)N=AC;M@q!1=kh=KKbR0W$nLAmG9qhc{%4*-@nN`)aQ1}L+`iQE5p^Bvs2ZDaoKp_fW$nkgC9s) zyH*bP`6_XKSjW7YZg=GieKwHzejYs0xO#5&@3%pP>@v{nUVSca*IXXnV(m66o*D<9 ztQ|NYIUe|NlHyR~z>~F89QL`}?1<#%2fp5Kb5+8A#n#u|U9-lmhpb(5B2GtcK8}au zv5wP88fEoA*El*67z4TCMaimJ#S0>tW-o*y!&>~pzF%GsAk`<{^p2s+WC74 zSGT=BJz*`V)M}3-W}# zzgrdgLAQ|?6e=%|E#WpmuRB#=x9|Wc)H+f1@}2_Pm*jn?cS`m9)s3;@hxQ|j#peUN zG>92&6|3U@ac*sJz7pJYLy>t}q4HGgNqYyKxg;!$CNyasn=7{1He8E+qEHM=xsj8+ zbF0!adu{ycTYoP~&1i`$$qVUJB96V*{MmYqPKcO?lQNb!KA}Dj;W%o4!8w6*1LtUQ z@%E4ESOed0^KU=mSNChv>GR}xC9-y^9w|c(~DrA>|Uia#AdAsKF z@D^*gQSsC`@MP`40m<>ekCPOK8V8=No#L?1-DXE5H$U+8ew(Wj_A9o&?(Uj3Zark} zniFw4a`SOK9FKJzFKfp<&^a#naT0T|j^kzR98cw&9iH<05`7Nq{kE#Ys&&Q-;-`*z zSoag_5vG;vs~_%TARYQ_AmKR;q=tT5sr}~?qQ4UFWb{@3Jvc}FZg}_fp-*NB+o1a! zFW#Ld{*iSxWPV>o{5B|N&wx3Hl=W#xD^h+;P~J+sXlwLssd({XixWF~URR2nA35D{ z%2B1z*>Br^DY0VwID@HZ&-ZO6Ofs07j(%@O<9l!Y9qHVTP40NqCI(t_qi!oYDJV{t zo71Q7*l09`?n>?dwlU0-Ft5?lbw9fLZ851GPe%qsmfI_VkRWI);pnXZ+cY3E(zh8Ys1T{#CC>E2q zk6DpC%_;`oXm(GpUir$^$xD-tuPs!%M0cFC`|u_4+2+w5$ED?pgJpf@7bF#my?ysg z?a*$k(yZl!ph;TVxp?Qi>iajDhx*)3dFcH%du6zKb9Sn_FfJPp z9FUlYb?^fzYuCyFKVK!z59^p$)9tQ&q0a^q-_L_58duM)-fx2n*=3;Dz4~0jwQV;#rK+A$AwjthRA#2l>Scv(BgQ~73xr~JM|pTl~;t*WqUo$-SBsbe12 z{lt2NY32Irhx-^vhdvufcuoVUq2K0wE)z2k%!s&SMvhoDbzRP5o3g|wM<(7k@p77| zOio{Rwm=bgems8W&7wm}$)N_yrfF{}*>Ufz?A*X6Laz?%b7k^%C3H;rweyB-rO4TD zd*oQ;JdKKlE(8(I&oIUaMZr5S!S3^qV)2DJKQRnH@3XmA0=N zO>K8IymNM0OUso*5eca+>ROzoH@8SU@N(rJ45rtkKeYYxaf5~8Q2rDj@`5}e?;l%< z&f&@hg^Kgo5^e+Zx>NOa3lD%ot&?*qkNSE~0qsljzSBFU`u%E2{(E~ff{R7hIhBXf zR#?TO~ED2PDS>KTc8{Y8-g7c8bG3cbgrN-2A}T`)#gD*ss|7y1Q%Exb={=Yfi-J z$j!&`a6Hy=ysRDbKyiN@7)tM}WWLUtMGb+0~` zw`(pBZ?Se86;F)=Pu30`kQ@*EI7xA+ap1|?DGvMGZFWR*^8;V+x49}|zhdj_?ygzm z)I9}Gy@l?Lq;VHi_(dV$uV-T4%f< ge(IQqbw9BlVOqJq`r$qX(xJ}=5}wmQYUsE97jLJ0#{d8T literal 0 HcmV?d00001 diff --git a/test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.csv b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.csv new file mode 100644 index 0000000..0bf3c2a --- /dev/null +++ b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.csv @@ -0,0 +1,103 @@ +"time","bodyBox.r[1]","bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]","bodyBox.v_0[2]","bodyBox.frame_b.r_0[1]" +0,1,0,1,0,1 +0.002,1,1.417825012934035e-05,0.9999740006763724,-0.0195807445407832,0.9999881789265018 +0.004,1,5.313923834174793e-05,0.9998960030304396,-0.03908714168556562,0.9999491422687813 +0.006,1,0.0001181362617060314,0.9997660114590055,-0.05851542917791717,0.9998841477207114 +0.008,1,0.0002091234527678357,0.9995840332464712,-0.0778657434873069,0.999793156699239 +0.01,1,0.000326099054897985,0.9993500776948991,-0.09713808926719215,0.9996761767497971 +0.012,1,0.0004690560147366672,0.9990641573526586,-0.1163324869886311,0.9995332133673953 +0.014,1,0.0006379874421543658,0.9987262866361137,-0.1354489568144222,0.9993642740782681 +0.016,1,0.000832885617011653,0.9983364820850886,-0.1544875215725002,0.9991693677021003 +0.018,1,0.001053739093578769,0.9978947644247245,-0.1734482153767369,0.9989485035183032 +0.02,1,0.001300538971511691,0.9974011552321307,-0.1923310647500386,0.9987016942036424 +0.022,1,0.00157327094282644,0.9968556808437448,-0.2111361125177071,0.9984289517865712 +0.024,1,0.001871924983280909,0.9962583689451037,-0.2298633885555008,0.9981302939283846 +0.026,1,0.002196481817791961,0.9956092513011041,-0.2485129501878651,0.9978057331188961 +0.028,1,0.002546927054133541,0.9949083620349535,-0.2670848402930672,0.997455289089087 +0.03,1,0.002923239445405512,0.994155737557995,-0.2855791220502004,0.9970789770034005 +0.032,1,0.003325388624709284,0.9933514166140098,-0.3039958856428557,0.9966768052387192 +0.034,1,0.003753358061751959,0.9924954413939879,-0.3223351802805359,0.9962487994557399 +0.036,1,0.004207126617115786,0.9915878565784442,-0.340597068821661,0.9957949831955599 +0.038,1,0.004686673151383015,0.9906287095709687,-0.3587816141246513,0.9953153827223518 +0.04,1,0.005191967386675178,0.9896180499664131,-0.3768889067903736,0.9948100173530883 +0.042,1,0.005722986459759737,0.9885559304269864,-0.3949190149103882,0.9942789168867462 +0.044,1,0.006279709994901068,0.9874424064724534,-0.4128719990247363,0.9937221164673544 +0.046,1,0.006862115072954707,0.9862775361787828,-0.4307479273947147,0.9931396512517375 +0.048,1,0.007470178774776187,0.9850613803166175,-0.4485468682816198,0.9925315590913937 +0.05,1,0.008103821130588527,0.9837940009841819,-0.4662690606761235,0.9918978221147704 +0.052,1,0.00876304148364556,0.9824754647342051,-0.4839145062172048,0.9912385062178507 +0.054,1,0.009447812213827225,0.9811058401953279,-0.5014832872342715,0.990553652409155 +0.056,1,0.01015810452490512,0.97968519863496,-0.5189754895763452,0.9898433031598651 +0.058,1,0.01089387776305806,0.9782136138791815,-0.5363912346772112,0.9891074916422395 +0.06,1,0.01165505381920325,0.9766911620635708,-0.553730756374293,0.988346215882774 +0.062,1,0.01244162634538108,0.975117922605218,-0.5709940731491192,0.9875595489505991 +0.064,1,0.0132535600626235,0.9734939773231635,-0.5881812903054828,0.9867475373857869 +0.066,1,0.01409081969196248,0.9718194106847828,-0.6052925131471765,0.9859102303767453 +0.068,1,0.01495334316027893,0.9700943096511918,-0.6223279274395663,0.9850476528114708 +0.07,1,0.01584106743243119,0.9683187638112045,-0.6392877218369272,0.9841598312436357 +0.072,1,0.01675397185431944,0.966492865632629,-0.6561719577252614,0.9832468374869485 +0.074,1,0.01769201473536401,0.9646167101017287,-0.67298075966213,0.9823087248370926 +0.076,1,0.01865515438498519,0.9626903948376431,-0.6897142522050934,0.9813455492226283 +0.078,1,0.01964331707618472,0.9607140197251685,-0.7063726561112313,0.9803573368013532 +0.08,1,0.02065645250112456,0.9586876875208576,-0.7229561218141588,0.9793441400219822 +0.082,1,0.02169451650984585,0.9566115036712002,-0.7394647812565036,0.9783060201810461 +0.084,1,0.02275745575203531,0.9544855761318363,-0.7558987940078782,0.9772430318838716 +0.086,1,0.02384521563088566,0.952310015452082,-0.7722583233808898,0.9761552310829678 +0.088,1,0.02495774030309547,0.9500849347706286,-0.7885435364311402,0.9750426750737241 +0.09,1,0.02609497267886922,0.9478104498111921,-0.8047546039572244,0.9739054224900613 +0.092,1,0.02725685442191735,0.9454866788781227,-0.820891700500733,0.97274353330004 +0.094,1,0.02844332594945613,0.94311374285196,-0.8369550043462501,0.9715570688014161 +0.096,1,0.02965432300661144,0.9406917650407978,-0.8529447077563223,0.9703460880474092 +0.098,1,0.03088977204827575,0.9382208709649862,-0.8688610307695054,0.9691106430132619 +0.1,1,0.03214961678515921,0.9357011894795664,-0.884704141886301,0.9678508062647256 +0.102,1,0.03343379109670443,0.9331328516383901,-0.9004742389997172,0.9665666427350945 +0.104,1,0.0347422274909309,0.9305159910386045,-0.9161715241190189,0.9652582185295354 +0.106,1,0.03607485710443521,0.9278507438155497,-0.9317962033697258,0.9639256009199849 +0.108,1,0.03743160970239083,0.925137248637621,-0.9473484869936142,0.9625688583400118 +0.11,1,0.03881241367854828,0.9223756467010917,-0.9628285893487162,0.96118806037964 +0.112,1,0.04021719605523505,0.919566081724903,-0.9782367289093201,0.9597832777801381 +0.114,1,0.04164588248335553,0.9167086999454117,-0.9935731282659692,0.9583545824287673 +0.116,1,0.0430984131729114,0.9138036485978986,-1.008837966181234,0.9569020617708101 +0.118,1,0.04457470783647349,0.910851079164642,-1.024031483095255,0.9554257870011156 +0.12,1,0.04607468513507586,0.9078511456196503,-1.039153922601708,0.9539258307547261 +0.122,1,0.04759826680889059,0.904804004006921,-1.054205519016186,0.9524022708158115 +0.124,1,0.04914537332958941,0.9017098128396142,-1.069186510460787,0.9508551861692036 +0.126,1,0.0507159239003439,0.8985687330936636,-1.084097138864106,0.9492846569940074 +0.128,1,0.05230983645582534,0.8953809282013514,-1.098937649961244,0.9476907646571767 +0.13,1,0.05392702766220477,0.8921465640448423,-1.113708293293798,0.9460735917070471 +0.132,1,0.055567412917153,0.8888658089496781,-1.128409322209871,0.9444332218668312 +0.134,1,0.05723090846134186,0.8855388331121092,-1.143040987506299,0.9427697415734511 +0.136,1,0.05891743315753914,0.882165808066411,-1.157603532071766,0.9410832412239502 +0.138,1,0.06062689417026532,0.8787469105544052,-1.172097234005607,0.9393738047246706 +0.14,1,0.06235920240475686,0.8752823184025715,-1.186522360130819,0.9376415208073284 +0.142,1,0.0641142675341053,0.8717722118281508,-1.200879180967436,0.9358864793622561 +0.144,1,0.06589199799925725,0.8682167734316997,-1.215167970732527,0.9341087714309569 +0.146,1,0.06769230100901449,0.8646161881896115,-1.229389007340194,0.932308489198626 +0.148,1,0.0695150825400338,0.8609706434466029,-1.243542572401574,0.9304857259866367 +0.15,1,0.07136024733682711,0.8572803289081588,-1.257628951224839,0.9286405762449859 +0.152,1,0.07322769891176151,0.8535454366329454,-1.271648432815197,0.9267731355447069 +0.154,1,0.07511734742166784,0.849766158927348,-1.285601286354159,0.9248835063490158 +0.156,1,0.07702909193293221,0.8459426929871825,-1.299487814158628,0.9229717849201147 +0.158,1,0.07896283092029514,0.8420752381907177,-1.313308320329912,0.9210380691110128 +0.16,1,0.08091846365462857,0.8381639956960949,-1.327063106609619,0.9190824593507234 +0.162,1,0.0828958882162892,0.8342091689551816,-1.340752478312082,0.9171050571714708 +0.164,1,0.08489500149511885,0.8302109637050734,-1.354376744324351,0.9151059652001923 +0.166,1,0.08691569919044412,0.8261695879595679,-1.367936217106201,0.9130852871500119 +0.168,1,0.08895787581107682,0.8220852520006039,-1.381431212690126,0.9110431278116807 +0.17,1,0.09102142467531353,0.8179581683696659,-1.394862050681345,0.9089795930449794 +0.172,1,0.0931062404511159,0.813788551136279,-1.408229046683594,0.9068947915873949 +0.174,1,0.09521222061667139,0.8095766154277348,-1.421532507944097,0.9047888360444063 +0.176,1,0.09733925001502079,0.8053225818512313,-1.43477277940791,0.902661831866252 +0.178,1,0.09948721786057077,0.8010266716656287,-1.447950193008633,0.9005138895261995 +0.18,1,0.1016560122183165,0.7966891083251946,-1.461065084130027,0.8983451205435111 +0.182,1,0.1038455200038418,0.7923101174701734,-1.474117791606015,0.8961556374740152 +0.184,1,0.1060556269833189,0.7878899269173223,-1.487108657720677,0.8939455539006412 +0.186,1,0.1082862177735087,0.7834287666504196,-1.500038028208257,0.8917149844239283 +0.188,1,0.1105371758417606,0.7789268688107436,-1.512906252253159,0.8894640446525042 +0.19,1,0.1128083835060125,0.774384467687522,-1.525713682489948,0.8871928511935345 +0.192,1,0.1150997299043263,0.7698017976709137,-1.538460651046364,0.88490152757524 +0.194,1,0.1174110933557253,0.7651790976937483,-1.551147522567888,0.8825901910494736 +0.196,1,0.1197423519115858,0.7605166085958401,-1.563774662499517,0.880258960507426 +0.198,1,0.122093384374056,0.7558145728344028,-1.576342434024323,0.8779079572084588 +0.2,1,0.1244640685114575,0.7510732349244313,-1.588851203428083,0.8755373034358889 +0.2,1,0.1244640685114575,0.7510732349244313,-1.588851203428083,0.8755373034358889 diff --git a/test/write.jl b/test/write.jl index 3a1d820..d9e6c6d 100644 --- a/test/write.jl +++ b/test/write.jl @@ -96,9 +96,11 @@ test_write(Dict( "sparse_zeros" => SparseMatrixCSC(20, 20, ones(Int, 21), Int[], Float64[]) )) -@test_throws ErrorException test_write(Dict("1invalidkey" => "starts with a number")) -@test_throws ErrorException test_write(Dict("another invalid key" => "invalid characters")) -@test_throws ErrorException test_write(Dict("yetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkey" => "too long")) +@testset "write exceptions" begin + @test_throws ErrorException test_write(Dict("1invalidkey" => "starts with a number")) + @test_throws ErrorException test_write(Dict("another invalid key" => "invalid characters")) + @test_throws ErrorException test_write(Dict("yetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkey" => "too long")) +end struct TestCompositeKind field1::AbstractString From 586ee2d2d33bd12baa437e82e91fa32c938df1e2 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Fri, 16 Jun 2023 13:37:07 -0500 Subject: [PATCH 75/91] re-add readVariable(s), returning dicts instead of DataFrame readAllVarialbes requires all data to be Vector --- test/runtests_modelica.jl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 4e22a5b..15450fa 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -121,6 +121,20 @@ end @test isapprox(vel[2], -0.981, rtol=1e-3) end +@testset "all-in-one readVariable" begin + data = MAT.MAT_v4_Modelica.readVariable(fbbOM, "bodyBox.frame_a.r_0[1]") + @test isapprox(data["bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(fbbOM, "nullVariable") +end + +@testset "all-in-one readVariables" begin + data = MAT.MAT_v4_Modelica.readVariables(fbbOM, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) + @test isapprox(data["bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test isapprox(data["bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) + @test isapprox(data["world.animateGravity"][1], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time +end + + @testset "readVariable: BouncingBall Dymola" begin ac = MAT.MAT_v4_Modelica.readAclass(bbDy) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) From 125b1481d60adf483b3509f4af429a1b377432a6 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Mon, 3 Jul 2023 17:25:47 -0500 Subject: [PATCH 76/91] remove Pkg dependency --- test/runtests_modelica.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 8340af0..24a6e83 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -7,7 +7,6 @@ # These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents using Test -using Pkg using MAT #OpenModelica v1.19.0 From 894d345472941d703e6204bce22289dbdc410494 Mon Sep 17 00:00:00 2001 From: Christian Schilling Date: Sat, 18 Mar 2023 20:53:46 +0100 Subject: [PATCH 77/91] remove invalid import of 'exists' from HDF5 (#184) --- src/MAT.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MAT.jl b/src/MAT.jl index bf6d1b8..13a79d9 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -189,7 +189,6 @@ end ### v0.10.0 deprecations ### -import HDF5: exists export exists @noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) From 612d1ba653173e3fcc02206f40720c3a3c521e88 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 21:31:04 +0000 Subject: [PATCH 78/91] Set version to 0.10.4 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 9535a1d..fde5d97 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "MAT" uuid = "23992714-dd62-5051-b70f-ba57cb901cac" -version = "0.10.5" +version = "0.10.4" [deps] BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" From 9f7fc053dd14ce985d9e005493c4eee6019d488b Mon Sep 17 00:00:00 2001 From: qnikil7 <52482564+qnikil7@users.noreply.github.com> Date: Wed, 7 Jun 2023 06:53:08 -0700 Subject: [PATCH 79/91] Added read and write support for v4 mat files (#186) * initial read support * v4 write support * add tests for v4 * move check routine * edit docs * added functionality to write into v4 mat files using keyword argument * Reset version number * Updated readme * Updated readme --------- Co-authored-by: vsaase Co-authored-by: Quadras --- README.md | 7 ---- src/MAT.jl | 7 ++-- test/readwrite4.jl | 52 ++++++++++++++++++++++++ test/runtests.jl | 2 +- test/v4/testcomplex_4.2c_SOL2.mat | Bin 0 -> 176 bytes test/v4/testdouble_4.2c_SOL2.mat | Bin 0 -> 103 bytes test/v4/testmatrix_4.2c_SOL2.mat | Bin 0 -> 151 bytes test/v4/testminus_4.2c_SOL2.mat | Bin 0 -> 38 bytes test/v4/testmulti_4.2c_SOL2.mat | Bin 0 -> 240 bytes test/v4/testonechar_4.2c_SOL2.mat | Bin 0 -> 40 bytes test/v4/testsparse_4.2c_SOL2.mat | Bin 0 -> 223 bytes test/v4/testsparsecomplex_4.2c_SOL2.mat | Bin 0 -> 294 bytes test/v4/teststring_4.2c_SOL2.mat | Bin 0 -> 375 bytes test/v4/teststringarray_4.2c_SOL2.mat | Bin 0 -> 156 bytes test/v4/testvec_4_GLNX86.mat | Bin 0 -> 93 bytes 15 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 test/readwrite4.jl create mode 100644 test/v4/testcomplex_4.2c_SOL2.mat create mode 100644 test/v4/testdouble_4.2c_SOL2.mat create mode 100644 test/v4/testmatrix_4.2c_SOL2.mat create mode 100644 test/v4/testminus_4.2c_SOL2.mat create mode 100644 test/v4/testmulti_4.2c_SOL2.mat create mode 100644 test/v4/testonechar_4.2c_SOL2.mat create mode 100644 test/v4/testsparse_4.2c_SOL2.mat create mode 100644 test/v4/testsparsecomplex_4.2c_SOL2.mat create mode 100644 test/v4/teststring_4.2c_SOL2.mat create mode 100644 test/v4/teststringarray_4.2c_SOL2.mat create mode 100644 test/v4/testvec_4_GLNX86.mat diff --git a/README.md b/README.md index 632e012..c11ab7f 100644 --- a/README.md +++ b/README.md @@ -94,10 +94,3 @@ close(file) ## Credits The MAT_HDF5 module, which provides read/write support for MATLAB v7.3 files, was written primarily by [Tim Holy](https://github.com/timholy/). The MAT_v5 module, which provides read support for MATLAB v5/v6/v7 files, was written primarily by [Simon Kornblith](https://github.com/simonster/). The MAT_v4 module, which provides read and write support for MATLAB v4 files, was written primarily by [Victor Saase](https://github.com/vsaase/). - - - -[docs-stable-img]: https://img.shields.io/badge/docs-stable-blue.svg -[docs-stable-url]: https://JuliaIO.github.io/MAT.jl/stable -[docs-dev-img]: https://img.shields.io/badge/docs-dev-blue.svg -[docs-dev-url]: https://JuliaIO.github.io/MAT.jl/dev diff --git a/src/MAT.jl b/src/MAT.jl index 13a79d9..a11efee 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -29,9 +29,8 @@ using HDF5, SparseArrays include("MAT_HDF5.jl") include("MAT_v5.jl") include("MAT_v4.jl") -include("MAT_v4_Modelica.jl") -using .MAT_HDF5, .MAT_v5, .MAT_v4, .MAT_v4_Modelica +using .MAT_HDF5, .MAT_v5, .MAT_v4 export matopen, matread, matwrite, @read, @write @@ -190,11 +189,11 @@ end ### export exists -@noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) +@noinline function exists(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) return haskey(matfile, varname) end -@noinline function Base.names(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) +@noinline function Base.names(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) Base.depwarn("`names(matfile)` is deprecated, use `keys(matfile)` instead.", :names) return keys(matfile) end diff --git a/test/readwrite4.jl b/test/readwrite4.jl new file mode 100644 index 0000000..16b8f47 --- /dev/null +++ b/test/readwrite4.jl @@ -0,0 +1,52 @@ +using MAT, Test + +function check(filename, result) + matfile = matopen(filename) + for (k, v) in result + @test haskey(matfile, k) + got = read(matfile, k) + if !isequal(got, v) || (typeof(got) != typeof(v) && (!isa(got, String) || !(isa(v, String)))) + close(matfile) + error(""" + Data mismatch reading $k from $filename ($format) + + Got $(typeof(got)): + + $(repr(got)) + + Expected $(typeof(v)): + + $(repr(v)) + """) + end + end + @test union!(Set(), keys(matfile)) == union!(Set(), keys(result)) + close(matfile) + + mat = matread(filename) + if !isequal(mat, result) + error(""" + Data mismatch reading $filename ($format) + + Got: + + $(repr(mat)) + + Expected: + + $(repr(result)) + """) + close(matfile) + return false + end + + return true +end +cd(dirname(@__FILE__)) +for filename in readdir("v4") + #println("testing $filename") + d = matread("v4/$filename") + matwrite("v4/tmp.mat", d; version="v4") + check("v4/tmp.mat", d) + rm("v4/tmp.mat") +end diff --git a/test/runtests.jl b/test/runtests.jl index e16c825..a3fdd53 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,6 @@ using SparseArrays, LinearAlgebra include("read.jl") -# include("readwrite4.jl") +include("readwrite4.jl") include("write.jl") include("runtests_modelica.jl") diff --git a/test/v4/testcomplex_4.2c_SOL2.mat b/test/v4/testcomplex_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..36621b25c08f18e4545100c6eaec015123c3bf9f GIT binary patch literal 176 zcmZQzV1B{Cz`zK^oKTvlB(=CCIX|}`C$)mX{sT}H2<)FNn3q;>eb#2;GBsn7820@T p{+azyc_{zfo>i5WKJ&V`pz2S<^g~R6n{x&x4mWop0dqG(`$IPYCV6bhD=3Sn-kr dCzQ{hRiznf5$7NT6&L-x`{nNCr4Eu1c>u#Y9lihn literal 0 HcmV?d00001 diff --git a/test/v4/testmatrix_4.2c_SOL2.mat b/test/v4/testmatrix_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..3698c8853b46d4a42194002523b57fddfb225908 GIT binary patch literal 151 zcmZQzV1B{Cz`zW|tUwF`+$E{SCAo0_8%Z(4iJjLfdiEf6^2tVdAI64fzvu`z literal 0 HcmV?d00001 diff --git a/test/v4/testminus_4.2c_SOL2.mat b/test/v4/testminus_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..cc207ed9f32095f39b7690e2dc1e2dc0d55ee8e0 GIT binary patch literal 38 kcmZQzV1B{Cz`zK_K#GB@B(=CCH#4uam|_11kN^V%0A!m6lK=n! literal 0 HcmV?d00001 diff --git a/test/v4/testmulti_4.2c_SOL2.mat b/test/v4/testmulti_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..9c6ba793cf41bf36447ab7a1890447fe5e939614 GIT binary patch literal 240 zcmZQzV1B{Cz`zW|tUwF`Oo>TXrytD# literal 0 HcmV?d00001 diff --git a/test/v4/testonechar_4.2c_SOL2.mat b/test/v4/testonechar_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..cdb4191c7d2eb0ac66d4f6add250e1f6a604d892 GIT binary patch literal 40 mcmZQzV1CKKz`zK_K#GBoBe96VA*KN&#sC0t%m%jr literal 0 HcmV?d00001 diff --git a/test/v4/testsparse_4.2c_SOL2.mat b/test/v4/testsparse_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..55cbd3c1b3d65630beae47832ffbcc7a6fd43354 GIT binary patch literal 223 zcmZQzV1C8Gz`y~-%s>nR+$E{SCB+4aMa8KM_8%Z(4iJjL0i+NJVB)xFLh2mArZB+G Wa}aKwqPFu=`o5P%3ch@jFi^D)#D&<~Y{yA#GIl?DK-2^cH@ literal 0 HcmV?d00001 diff --git a/test/v4/teststring_4.2c_SOL2.mat b/test/v4/teststring_4.2c_SOL2.mat new file mode 100644 index 0000000000000000000000000000000000000000..137561e1f636d7b08959e43e969a6984eb7a3b37 GIT binary patch literal 375 zcmZ{gO%6an423I}j^YxvVJTQKEJbWAm;*SP>$ruVzPB0Cq-nqQwbP79e2PePdwTn0 zi61w=`E_0<(adUEA-dyDRLQ$>X9acO7HmP(fmx@H{cwJe*OdBxH||jjyl4?yTB eFvZ{y=>Xxw^u;tl_z+ Date: Wed, 7 Jun 2023 09:53:46 -0400 Subject: [PATCH 80/91] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index fde5d97..9535a1d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "MAT" uuid = "23992714-dd62-5051-b70f-ba57cb901cac" -version = "0.10.4" +version = "0.10.5" [deps] BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" From d2bb7d12eb71f13dae32001fea35f0552a35f739 Mon Sep 17 00:00:00 2001 From: Jeff Fessler Date: Wed, 7 Jun 2023 09:59:04 -0400 Subject: [PATCH 81/91] Add Documenter documentation (#178) * Add documentation * Tweak docstring * Add urls to readme --------- Co-authored-by: Steve Kelly --- .github/workflows/Documentation.yml | 7 ++++--- README.md | 7 +++++++ docs/make.jl | 6 +++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 5d04015..8863b97 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -8,7 +8,7 @@ on: paths-ignore: - 'README.md' branches: - - 'master' + - 'main' - 'release-' tags: '*' @@ -40,7 +40,8 @@ jobs: Pkg.instantiate()' - name: BuildAndDeploy env: - JULIA_PKG_SERVER: "" - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +# https://juliadocs.github.io/Documenter.jl/stable/man/hosting/#Authentication:-GITHUB_TOKEN +# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ssh: ${{ secrets.DOCUMENTER_KEY }} DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} run: julia --project=docs/ docs/make.jl diff --git a/README.md b/README.md index c11ab7f..632e012 100644 --- a/README.md +++ b/README.md @@ -94,3 +94,10 @@ close(file) ## Credits The MAT_HDF5 module, which provides read/write support for MATLAB v7.3 files, was written primarily by [Tim Holy](https://github.com/timholy/). The MAT_v5 module, which provides read support for MATLAB v5/v6/v7 files, was written primarily by [Simon Kornblith](https://github.com/simonster/). The MAT_v4 module, which provides read and write support for MATLAB v4 files, was written primarily by [Victor Saase](https://github.com/vsaase/). + + + +[docs-stable-img]: https://img.shields.io/badge/docs-stable-blue.svg +[docs-stable-url]: https://JuliaIO.github.io/MAT.jl/stable +[docs-dev-img]: https://img.shields.io/badge/docs-dev-blue.svg +[docs-dev-url]: https://JuliaIO.github.io/MAT.jl/dev diff --git a/docs/make.jl b/docs/make.jl index de06777..c426c2b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,6 +1,6 @@ execute = isempty(ARGS) || ARGS[1] == "run" -org, reps = :JuliaIO, :MAT +org, reps = :JuilaIO, :MAT eval(:(using $reps)) using Documenter using Literate @@ -65,8 +65,8 @@ makedocs(; if isci deploydocs(; - repo = "github.com/JuliaIO/MAT.jl", - devbranch = "master", + repo = "github.com/$base", + devbranch = "main", devurl = "dev", versions = ["stable" => "v^", "dev" => "dev"], forcepush = true, From 55469c5feff3abf523938d22ead8629bca9346f2 Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 7 Jun 2023 11:49:00 -0400 Subject: [PATCH 82/91] Update Documentation.yml default branch --- .github/workflows/Documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 8863b97..2139386 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -8,7 +8,7 @@ on: paths-ignore: - 'README.md' branches: - - 'main' + - 'master' - 'release-' tags: '*' From ca41a283dc3ad38ae17286e4528e5a6fbda1dcf0 Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 7 Jun 2023 11:57:42 -0400 Subject: [PATCH 83/91] Fix some typos in docs/make.jl --- docs/make.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index c426c2b..29e3a7b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,6 +1,6 @@ execute = isempty(ARGS) || ARGS[1] == "run" -org, reps = :JuilaIO, :MAT +org, reps = :JuliaIO, :MAT eval(:(using $reps)) using Documenter using Literate @@ -66,7 +66,7 @@ makedocs(; if isci deploydocs(; repo = "github.com/$base", - devbranch = "main", + devbranch = "master", devurl = "dev", versions = ["stable" => "v^", "dev" => "dev"], forcepush = true, From 2924e9356dafa86fab5a8ab9f00c70370aa1b3ca Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 7 Jun 2023 14:42:48 -0400 Subject: [PATCH 84/91] Update Documentation.yml --- .github/workflows/Documentation.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index 2139386..5d04015 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -40,8 +40,7 @@ jobs: Pkg.instantiate()' - name: BuildAndDeploy env: -# https://juliadocs.github.io/Documenter.jl/stable/man/hosting/#Authentication:-GITHUB_TOKEN -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ssh: ${{ secrets.DOCUMENTER_KEY }} + JULIA_PKG_SERVER: "" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} run: julia --project=docs/ docs/make.jl From 39454cd3828c9669480466ffaefd78c3a63ef823 Mon Sep 17 00:00:00 2001 From: Steve Kelly Date: Wed, 7 Jun 2023 17:17:47 -0400 Subject: [PATCH 85/91] Update make.jl --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 29e3a7b..de06777 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -65,7 +65,7 @@ makedocs(; if isci deploydocs(; - repo = "github.com/$base", + repo = "github.com/JuliaIO/MAT.jl", devbranch = "master", devurl = "dev", versions = ["stable" => "v^", "dev" => "dev"], From 6a4c6da05555e4897f38fc1843bf81c4ba6f68bb Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 7 Feb 2023 15:07:43 -0600 Subject: [PATCH 86/91] parent 39454cd3828c9669480466ffaefd78c3a63ef823 author Ben Conrad 1675804063 -0600 committer Ben Conrad 1688437150 -0500 started reader write --- .gitignore | 1 + src/MAT.jl | 14 ++++------- src/MAT_v4_Modelica.jl | 47 +++++++++++++++++++++++++++++++++++ test/readwrite4.jl | 52 --------------------------------------- test/runtests.jl | 2 +- test/runtests_modelica.jl | 25 +++++++++++++------ 6 files changed, 72 insertions(+), 69 deletions(-) delete mode 100644 test/readwrite4.jl diff --git a/.gitignore b/.gitignore index aa26b25..1d5c485 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *~ Manifest.toml +desktop.ini # Build artifacts for creating documentation generated by the Documenter package docs/build/ diff --git a/src/MAT.jl b/src/MAT.jl index a11efee..642abd9 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -29,8 +29,9 @@ using HDF5, SparseArrays include("MAT_HDF5.jl") include("MAT_v5.jl") include("MAT_v4.jl") +include("MAT_v4_Modelica.jl") -using .MAT_HDF5, .MAT_v5, .MAT_v4 +using .MAT_HDF5, .MAT_v5, .MAT_v4, .MAT_v4_Modelica export matopen, matread, matwrite, @read, @write @@ -53,12 +54,6 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo return MAT_v4.matopen(rawfid, swap_bytes) end - # Test whether this is a MAT file - if fs < 128 - close(rawfid) - error("File \"$filename\" is too small to be a supported MAT file") - end - # Check for MAT v5 file seek(rawfid, 124) version = read(rawfid, UInt16) @@ -188,12 +183,13 @@ end ### v0.10.0 deprecations ### +import HDF5: exists export exists -@noinline function exists(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) +@noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) return haskey(matfile, varname) end -@noinline function Base.names(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) +@noinline function Base.names(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) Base.depwarn("`names(matfile)` is deprecated, use `keys(matfile)` instead.", :names) return keys(matfile) end diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index 9bf14d3..a23bc12 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -1,4 +1,5 @@ # A module to read MAT files written by OpenModelica tools + # Copyright (C) 2023 Ben Conrad # # Permission is hereby granted, free of charge, to any person obtaining @@ -443,4 +444,50 @@ function isMatV4Modelica(filepath::String) return ret; end + +""" +All-in-one reading of variable `name` from `filepath`, returning a Dict with keys "time" and `name` +""" +function readVariable(filepath::String, name::String) :: Dict + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + time = readVariable(ac, vn, vd, di, "time") + varn = readVariable(ac, vn, vd, di, name) + return Dict(["time"=>time, name=>varn]); + +end + +""" +Reads the vector of variable `names` from mat file `filepath`, returning a Dict with columns "time" and `names` +""" +# function readVariables(filepath::String, names::AbstractVector{T}) :: Dict where T<:AbstractString +function readVariables(filepath::String, names::AbstractVector{T}) where T<:AbstractString + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + data = Dict(["time"=> readVariable(ac, vn, vd, di, "time")]) + for name in names + var = readVariable(ac,vn,vd,di, name) + if length(var) == 1 # a constant value + # data[name] = var[1] + data[name] = [var[1]] + elseif length(var) == 2 && var[1] == var[2] # this is a 2-element constant array: [123, 123] + # data[name] = var[1] + data[name] = [var[1]] # Dict infers Vector64 + elseif length(var) == length(data["time"]) # a regular vector + data[name] = var + else + throw(DimensionMismatch("Length of $name [$(length(var))] differs from the dataframe [$(length(data["time"]))], cannot add it")) + end + end + return data +end + + + end #MAT_v4_Modelica diff --git a/test/readwrite4.jl b/test/readwrite4.jl deleted file mode 100644 index 16b8f47..0000000 --- a/test/readwrite4.jl +++ /dev/null @@ -1,52 +0,0 @@ -using MAT, Test - -function check(filename, result) - matfile = matopen(filename) - for (k, v) in result - @test haskey(matfile, k) - got = read(matfile, k) - if !isequal(got, v) || (typeof(got) != typeof(v) && (!isa(got, String) || !(isa(v, String)))) - close(matfile) - error(""" - Data mismatch reading $k from $filename ($format) - - Got $(typeof(got)): - - $(repr(got)) - - Expected $(typeof(v)): - - $(repr(v)) - """) - end - end - @test union!(Set(), keys(matfile)) == union!(Set(), keys(result)) - close(matfile) - - mat = matread(filename) - if !isequal(mat, result) - error(""" - Data mismatch reading $filename ($format) - - Got: - - $(repr(mat)) - - Expected: - - $(repr(result)) - """) - close(matfile) - return false - end - - return true -end -cd(dirname(@__FILE__)) -for filename in readdir("v4") - #println("testing $filename") - d = matread("v4/$filename") - matwrite("v4/tmp.mat", d; version="v4") - check("v4/tmp.mat", d) - rm("v4/tmp.mat") -end diff --git a/test/runtests.jl b/test/runtests.jl index a3fdd53..e16c825 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,6 @@ using SparseArrays, LinearAlgebra include("read.jl") -include("readwrite4.jl") +# include("readwrite4.jl") include("write.jl") include("runtests_modelica.jl") diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 4e22a5b..def3613 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -6,7 +6,8 @@ # test/v4_Modelica/FallingbodyBox/FallingBodyBox_dymola2021.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) # These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents -using Test, MAT +using Test +using MAT #OpenModelica v1.19.0 bbOM = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_om1.19.0.mat") @@ -121,6 +122,20 @@ end @test isapprox(vel[2], -0.981, rtol=1e-3) end +@testset "all-in-one readVariable" begin + data = MAT.MAT_v4_Modelica.readVariable(fbbOM, "bodyBox.frame_a.r_0[1]") + @test isapprox(data["bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(fbbOM, "nullVariable") +end + +@testset "all-in-one readVariables" begin + data = MAT.MAT_v4_Modelica.readVariables(fbbOM, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) + @test isapprox(data["bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test isapprox(data["bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) + @test isapprox(data["world.animateGravity"][1], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time +end + + @testset "readVariable: BouncingBall Dymola" begin ac = MAT.MAT_v4_Modelica.readAclass(bbDy) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) @@ -186,7 +201,7 @@ end vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) - + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "Time") # display(var) @@ -213,8 +228,4 @@ end var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "world.animateGravity") @test isapprox(var[1], 1.0, rtol=1e-3) -end - - -; - +end \ No newline at end of file From fb39ed13fbc27b58206b116f5b6500514a06b479 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Mon, 3 Jul 2023 21:34:21 -0500 Subject: [PATCH 87/91] restore readwrite4.jl and add testset label --- test/readwrite4.jl | 54 ++++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 test/readwrite4.jl diff --git a/test/readwrite4.jl b/test/readwrite4.jl new file mode 100644 index 0000000..9930a00 --- /dev/null +++ b/test/readwrite4.jl @@ -0,0 +1,54 @@ +using MAT, Test + +function check(filename, result) + matfile = matopen(filename) + for (k, v) in result + @test haskey(matfile, k) + got = read(matfile, k) + if !isequal(got, v) || (typeof(got) != typeof(v) && (!isa(got, String) || !(isa(v, String)))) + close(matfile) + error(""" + Data mismatch reading $k from $filename ($format) + + Got $(typeof(got)): + + $(repr(got)) + + Expected $(typeof(v)): + + $(repr(v)) + """) + end + end + @test union!(Set(), keys(matfile)) == union!(Set(), keys(result)) + close(matfile) + + mat = matread(filename) + if !isequal(mat, result) + error(""" + Data mismatch reading $filename ($format) + + Got: + + $(repr(mat)) + + Expected: + + $(repr(result)) + """) + close(matfile) + return false + end + + return true +end +cd(dirname(@__FILE__)) +for filename in readdir("v4") + #println("testing $filename") + @testset "readwrite4.jl testing $filename" begin + d = matread("v4/$filename") + matwrite("v4/tmp.mat", d; version="v4") + check("v4/tmp.mat", d) + rm("v4/tmp.mat") + end +end diff --git a/test/runtests.jl b/test/runtests.jl index e16c825..a3fdd53 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,6 @@ using SparseArrays, LinearAlgebra include("read.jl") -# include("readwrite4.jl") +include("readwrite4.jl") include("write.jl") include("runtests_modelica.jl") From 426ebe3382ead3acfa4f22bac5a7b9e085526ce5 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Mon, 3 Jul 2023 23:14:16 -0500 Subject: [PATCH 88/91] fix dropped MAT_v4.Matlabv4File type union --- src/MAT.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/MAT.jl b/src/MAT.jl index 642abd9..25ec306 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -183,13 +183,12 @@ end ### v0.10.0 deprecations ### -import HDF5: exists export exists -@noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) +@noinline function exists(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) return haskey(matfile, varname) end -@noinline function Base.names(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) +@noinline function Base.names(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) Base.depwarn("`names(matfile)` is deprecated, use `keys(matfile)` instead.", :names) return keys(matfile) end From 18ca0fb768410ef21de9c0876de9d7415ad2875f Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 31 Aug 2023 14:46:31 -0500 Subject: [PATCH 89/91] ignore vscode workspace files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 1d5c485..67d9b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ desktop.ini docs/build/ docs/site/ docs/src/generated/ + +#vscode workspace +*.code-workspace From 2c64031cbd2e14cf0f8a06c9f0ac1a0c54c82c7d Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 31 Aug 2023 14:46:58 -0500 Subject: [PATCH 90/91] restore small file error --- src/MAT.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/MAT.jl b/src/MAT.jl index 25ec306..61b49af 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -54,6 +54,11 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo return MAT_v4.matopen(rawfid, swap_bytes) end + if fs < 128 + close(rawfid) + error("file < 128") + end + # Check for MAT v5 file seek(rawfid, 124) version = read(rawfid, UInt16) From f7fd6fbae9476083049ea9403f3610139f1f47d5 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 31 Aug 2023 14:47:29 -0500 Subject: [PATCH 91/91] add struct comment --- src/MAT_v4_Modelica.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index a23bc12..477f1e5 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -152,6 +152,9 @@ function readAclass( filepath::String ) end #open end +""" + Struct to record variable names and their start/stop file positions. +""" struct VariableNames # names::Vector{T}(undef,undef) where T<:AbstractString names::Vector{String} @@ -457,7 +460,6 @@ function readVariable(filepath::String, name::String) :: Dict time = readVariable(ac, vn, vd, di, "time") varn = readVariable(ac, vn, vd, di, name) return Dict(["time"=>time, name=>varn]); - end """

!+|{-#`x1Ax?aIEy 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 Date: Sat, 10 Jun 2023 19:33:01 +0530 Subject: [PATCH 27/91] Removed Pkg Dependency --- Project.toml | 1 - test/runtests_modelica.jl | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index b7d7067..74c05da 100644 --- a/Project.toml +++ b/Project.toml @@ -6,7 +6,6 @@ version = "0.10.4" BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" -Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index d501155..e970e1b 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -6,12 +6,7 @@ # test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) # These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -using Test -cd(joinpath(@__DIR__,"..")) -include("../src/MAT.jl") +using Test, MAT #OpenModelica v1.19.0 bbOM = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_om1.19.0.mat") From 4676b4f3f4d569ffc7454d954792870670d40970 Mon Sep 17 00:00:00 2001 From: Brian Quadras Date: Sat, 10 Jun 2023 19:36:13 +0530 Subject: [PATCH 28/91] Fixed typo --- test/runtests_modelica.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index e970e1b..4e22a5b 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -3,7 +3,7 @@ # test/v4_Modelica/BouncingBall/BouncingBall_res.mat - simulated with OpenModelica v1.19.0 # test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat - simulated with Dymola v2021 # test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with OpenModelica v1.19.0 -# test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) +# test/v4_Modelica/FallingbodyBox/FallingBodyBox_dymola2021.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) # These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents using Test, MAT From ea31d0d0587474fa87fc80c4931877644a74b236 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 7 Feb 2023 15:06:10 -0600 Subject: [PATCH 29/91] pr132 run-able --- src/MAT_v4_pr132.jl | 322 +++++++++++++++++++++++++++++++++++++++++ test/runtests_pr132.jl | 37 +++++ 2 files changed, 359 insertions(+) create mode 100644 src/MAT_v4_pr132.jl create mode 100644 test/runtests_pr132.jl diff --git a/src/MAT_v4_pr132.jl b/src/MAT_v4_pr132.jl new file mode 100644 index 0000000..1952779 --- /dev/null +++ b/src/MAT_v4_pr132.jl @@ -0,0 +1,322 @@ +# Copied from pr132 of https://github.com/JuliaIO/MAT.jl/pull/132 + +# MAT_v4.jl +# Tools for reading MATLAB v4 files in Julia +# +# Copyright (C) 2012 Simon Kornblith +# Copyright (C) 2019 Victor Saase (modified from MAT_v5.jl) +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# MATLAB's file format documentation can be found at +# http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf + +module MAT_v4_pr132 +using BufferedStreams, HDF5, SparseArrays +import Base: read, write, close +import HDF5: names + +round_uint8(data) = round.(UInt8, data) +complex_array(a, b) = complex.(a, b) + +mutable struct Matlabv4File <: HDF5.H5DataStore + ios::IOStream + swap_bytes::Bool + varnames::Dict{String, Int64} + + Matlabv4File(ios, swap_bytes) = new(ios, swap_bytes) +end + +const mLITTLE_ENDIAN = 0 +const mBIG_ENDIAN = 1 +const mVAX_DFLOAT = 2 +const mVAX_GFLOAT = 3 +const mGRAY = 4 + +const pTYPE = Dict( + 0 => Float64, + 1 => Float32, + 2 => Int32, + 3 => Int16, + 4 => UInt16, + 5 => UInt8 +) + +const tNUMERIC = 0 +const tTEXT = 1 +const tSPARSE = 2 + +const imagfREAL = 0 +const imagfCOMPLEX = 1 + +read_bswap(f::IO, swap_bytes::Bool, ::Type{T}) where T = swap_bytes ? bswap(read(f, T)) : read(f, T) + +function read_bswap(f::IO, swap_bytes::Bool, ::Type{T}, dim::Union{Int, Tuple{Vararg{Int}}}) where T + d = read!(f, Array{T}(undef, dim)) + if swap_bytes + for i = 1:length(d) + @inbounds d[i] = bswap(d[i]) + end + end + d +end + +function read_bswap(f::IO, swap_bytes::Bool, d::AbstractArray{T}) where T + readbytes!(f, reinterpret(UInt8, d)) + if swap_bytes + for i = 1:length(d) + @inbounds d[i] = bswap(d[i]) + end + end + d +end + +function checkv4(f::IO) + M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, false) + if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 + swap_bytes = false + return (true, swap_bytes) + else + seek(f, 0) + M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, true) + if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 + swap_bytes = true + return (true, swap_bytes) + end + end + return (false, false) +end + +# Read data type and number of bytes at the start of a data element +function read_header(f::IO, swap_bytes::Bool) + dtype = read_bswap(f, swap_bytes, Int32) + + M = div(rem(dtype, 10000), 1000) + O = div(rem(dtype, 1000), 100) + P = div(rem(dtype, 100), 10) + T = div(rem(dtype, 10), 1) + + mrows = read_bswap(f, swap_bytes, Int32) + ncols = read_bswap(f, swap_bytes, Int32) + imagf = read_bswap(f, swap_bytes, Int32) + namlen = read_bswap(f, swap_bytes, Int32) + + M, O, P, T, mrows, ncols, imagf, namlen +end + +# Read matrix data +function read_matrix(f::IO, swap_bytes::Bool) + M, O, P, T, mrows, ncols, imagf, namlen = read_header(f, swap_bytes) + if ncols == 0 || mrows == 0 + # If one creates a cell array using + # y = cell(m, n) + # then MATLAB will save the empty cells as zero-byte matrices. If one creates a + # empty cells using + # a = {[], [], []} + # then MATLAB does not save the empty cells as zero-byte matrices. To avoid + # surprises, we produce an empty array in both cases. + return ("", Matrix{Union{}}(undef, 0, 0)) + end + name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) + if T == tNUMERIC || T == tSPARSE + real_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) + if T == tNUMERIC && imagf == imagfCOMPLEX + imag_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) + data = complex.(real_data, imag_data) + else + data = real_data + end + datamat = reshape(data, Int(mrows), Int(ncols)) + if T == tNUMERIC + return (name, datamat) + elseif T == tSPARSE + if size(datamat,2) == 3 + return (name, sparse(datamat[:,1], datamat[:,2], datamat[:,3])) + else + return (name, sparse(datamat[:,1], datamat[:,2], complex.(datamat[:,3], datamat[:,4]))) + end + end + elseif T == tTEXT + if mrows > 1 + charvec = UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows))) + charmat = reshape(charvec, Int(mrows), Int(ncols)) + data = [String(charmat[i,:]) for i in 1:mrows] + else + data = String(UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols)))) + end + return (name, data) + end +end + +# Open MAT file for reading +matopen(ios::IOStream, endian_indicator::Bool) = Matlabv4File(ios, endian_indicator) + +# Read whole MAT file +function read(matfile::Matlabv4File) + seek(matfile.ios, 0) + vars = Dict{String, Any}() + while !eof(matfile.ios) + (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) + vars[name] = data + end + vars +end + +# Read only variable names +function getvarnames(matfile::Matlabv4File) + if !isdefined(matfile, :varnames) + seek(matfile.ios, 0) + matfile.varnames = varnames = Dict{String, Int64}() + while !eof(matfile.ios) + offset = position(matfile.ios) + M, O, P, T, mrows, ncols, imagf, namlen = read_header(matfile.ios, matfile.swap_bytes) + f = matfile.ios + + name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) + varnames[name] = offset + imag_offset = 0 + skip(f, mrows*ncols*sizeof(pTYPE[P])) + if imagf == imagfCOMPLEX + skip(f, mrows*ncols*sizeof(pTYPE[P])) + end + end + end + matfile.varnames +end + +names(matfile::Matlabv4File) = keys(getvarnames(matfile)) + +# Read a variable from a MAT file +function read(matfile::Matlabv4File, varname::String) + varnames = getvarnames(matfile) + if !haskey(varnames, varname) + error("no variable $varname in file") + end + seek(matfile.ios, varnames[varname]) + (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) + data +end + +function colvals(A::AbstractSparseMatrix) + rows = rowvals(A) + cols = similar(rows) + m,n = size(A) + for i=1:n + for j in nzrange(A,i) + cols[j] = i + end + end + cols +end + +function write(parent::Matlabv4File, name::String, s) + M = Int(parent.swap_bytes) + O = 0 + P = 0 + for p=keys(pTYPE) + if eltype(s) == pTYPE[p] || eltype(s) == Complex{pTYPE[p]} + P = p + end + end + if pTYPE[P] != eltype(s) && Complex{pTYPE[P]} != eltype(s) && + !(s isa AbstractString && pTYPE[P] == Float64) && + !(s isa Vector{String} && pTYPE[P] == Float64) + error("invalid value type when writing v4 file") + end + if s isa AbstractSparseMatrix + T = tSPARSE + elseif s isa AbstractString || s isa Vector{String} + T = tTEXT + else + T = tNUMERIC + end + write(parent.ios, Int32(1000*M + 100*O + 10*P + T)) + + mrows = 1 + ncols = 1 + if s isa AbstractVector && !(s isa Vector{String}) + ncols = length(s) + elseif s isa AbstractMatrix + if s isa AbstractSparseMatrix + mrows = nnz(s) + ncols = 3 + if eltype(s) <: Complex + ncols = 4 + end + else + mrows, ncols = size(s) + end + elseif s isa Vector{String} + ncols = length(s[1]) + mrows = length(s) + elseif s isa AbstractString + ncols = length(s) + end + write(parent.ios, Int32(mrows)) + write(parent.ios, Int32(ncols)) + + imagf = 0 + if eltype(s) <: Complex && T == tNUMERIC + imagf = 1 + end + write(parent.ios, Int32(imagf)) + + namlen = length(name) + 1 + write(parent.ios, Int32(namlen)) + + write(parent.ios, Vector{UInt8}(name)) + write(parent.ios, UInt8(0)) + + if s isa AbstractArray && T == tNUMERIC + write(parent.ios, reshape(real(s), length(s))) + if imagf == 1 + write(parent.ios, reshape(imag(s), length(s))) + end + elseif s isa Number + write(parent.ios, real(s)) + if imagf == 1 + write(parent.ios, imag(s)) + end + elseif s isa AbstractString + floatarray = Float64.(Vector{UInt8}(s)) + write(parent.ios, floatarray) + elseif s isa Vector{String} + floatarray = Matrix{Float64}(undef, mrows, ncols) + for (i,strel) = enumerate(s) + floatarray[i,:] = Float64.(Vector{UInt8}(strel)) + end + write(parent.ios, floatarray) + elseif T == tSPARSE + rows = rowvals(s) + cols = colvals(s) + vals = nonzeros(s) + write(parent.ios, pTYPE[P].(rows)) + write(parent.ios, pTYPE[P].(cols)) + write(parent.ios, pTYPE[P].(real(vals))) + if eltype(s) <: Complex + write(parent.ios, pTYPE[P].(imag(vals))) + end + end +end + +# Close MAT file +close(matfile::Matlabv4File) = close(matfile.ios) + +end diff --git a/test/runtests_pr132.jl b/test/runtests_pr132.jl new file mode 100644 index 0000000..ee4eea2 --- /dev/null +++ b/test/runtests_pr132.jl @@ -0,0 +1,37 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +include("../src/MAT.jl") +include("../src/MAT_v4_pr132.jl") + +mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + +@testset "checkv4" begin + # function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Bool, ff::Bool, compress::Bool) + # function matopen(fname::AbstractString, mode::AbstractString; compress::Bool = false) + # mat = MAT.matopen(mat1s, "r" ) + + rawfid = open(mat1s, "r") + (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) + close(rawfid) + + @test isv4 == true +end + +rawfid = open(mat1s, "r") +(isv4, swap_bytes) = MAT_v4.checkv4(rawfid) +mat = MAT_v4_pr132.matopen(rawfid, swap_bytes ) + +@testset "getvarnames" begin + # function getvarnames(matfile::Matlabv4File) + @show gvn = MAT_v4_pr132.getvarnames(mat) + # gvn = MAT_v4.getvarnames(mat) = Dict("name" => 71, "Aclass" => 0, "dataInfo" => 448328, "data_2" => 501296, "data_1" => 488197, "description" => 117126) +# ...and this is not correct + @test false +end + + +close(rawfid) \ No newline at end of file From a08031a5db0c40163a97f73b986146457d296970 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 7 Feb 2023 15:06:36 -0600 Subject: [PATCH 30/91] pr164 run-able --- src/MAT_v4_pr164.jl | 59 ++++++++++++++++++++++++++++++++++++++++++ test/runtests_pr164.jl | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/MAT_v4_pr164.jl create mode 100644 test/runtests_pr164.jl diff --git a/src/MAT_v4_pr164.jl b/src/MAT_v4_pr164.jl new file mode 100644 index 0000000..20f3479 --- /dev/null +++ b/src/MAT_v4_pr164.jl @@ -0,0 +1,59 @@ +module MAT_v4_pr164 + +import Base: read, write, close + +mutable struct Matlabv4File + ios::IOStream + varnames::Dict{String, Int64} + + Matlabv4File(ios) = new(ios) +end + +const V4_ELTYPE = [Float64, Float32, Int32, Int16, UInt16, UInt8] + +matopen(ios::IOStream) = Matlabv4File(ios) + +function unpack_header_type(type::Int32) + T, P, O, M = digits(type; pad=4) + iszero(O) || error("file is not a v4 MAT file (magic digit not 0)") + iszero(M) || error("only little endian v4 MAT currently supported") + iszero(T) || error("only full numeric matrices supported for v4 MAT files") + + P in 0:5 || error("invalid eltype digit in v4 MAT file: $P") + eltype = V4_ELTYPE[P+1] + + return eltype +end + +function read_one_mat!(mat::Matlabv4File) + type, mrows, ncols, imagf, namlen = read!(mat.ios, Vector{Int32}(undef, 5)) + eltype = unpack_header_type(type) + + iszero(imagf) || error("no imaginary matrix support") + + name = String(read!(mat.ios, Vector{UInt8}(undef, namlen-1))) + + # one null byte before start of matrix data + Base.read(mat.ios, UInt8) + + @show mrows, ncols + value = read!(mat.ios, Matrix{eltype}(undef, mrows, ncols)) + + return name => value +end + +function read(mat::Matlabv4File) + seekstart(mat.ios) + + results = Dict{String,Any}() + while !eof(mat.ios) + name, value = read_one_mat!(mat) + results[name] = value + end + + return results +end + +close(mat::Matlabv4File) = close(mat.ios) + +end # module diff --git a/test/runtests_pr164.jl b/test/runtests_pr164.jl new file mode 100644 index 0000000..dea51b4 --- /dev/null +++ b/test/runtests_pr164.jl @@ -0,0 +1,48 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +include("../src/MAT.jl") +include("../src/MAT_v4_pr164.jl") + +mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + + + +@testset "unpack_header" begin + rawfid = open(mat1s, "r") + # (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) + + # From 'Version-4-MAT-File-Format.pdf': + # Each matrix starts with a fixed-length 20-byte header that contains information describing certain attributes of the Matrix. + # The 20-byte header consists of five long (4-byte) integers: + @show type, mrows, ncols, imagf, namlen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + # P indicates which format the data is stored: 5 = 8bit uint + # T indicates matrix type: 1 = text matrix + + @show eltype = MAT_v4_pr164.unpack_header_type(type) + + + close(rawfid) + + # @test isv4 == true +end + +# rawfid = open(mat1s, "r") +# (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) +# mat = MAT_v4.matopen(rawfid, swap_bytes ) + +# @testset "getvarnames" begin +# # function getvarnames(matfile::Matlabv4File) +# @show gvn = MAT_v4.getvarnames(mat) +# # gvn = MAT_v4.getvarnames(mat) = Dict("name" => 71, "Aclass" => 0, "dataInfo" => 448328, "data_2" => 501296, "data_1" => 488197, "description" => 117126) +# # ...and this is not correct +# @test false +# end + + +# close(rawfid) \ No newline at end of file From 20fd446e0ba15e85e30cace1a4a265b4d0fd17b3 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 7 Feb 2023 15:07:43 -0600 Subject: [PATCH 31/91] started reader write --- src/MAT.jl | 14 +- src/MAT_v4_Modelica.jl | 18 ++ src/Matlab_MATFileFormatV4.pdf | Bin 0 -> 23557 bytes ...aUserGuide_v1.21.0-dev-211-gcd8969a225.pdf | Bin 0 -> 85780 bytes test/readwrite4.jl | 6 +- test/runtests.jl | 9 + test/runtests_modelica_manual.jl | 126 +++++++++++ test/runtests_modelica_readMatrix.jl | 205 ++++++++++++++++++ 8 files changed, 369 insertions(+), 9 deletions(-) create mode 100644 src/MAT_v4_Modelica.jl create mode 100644 src/Matlab_MATFileFormatV4.pdf create mode 100644 src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf create mode 100644 test/runtests_modelica_manual.jl create mode 100644 test/runtests_modelica_readMatrix.jl diff --git a/src/MAT.jl b/src/MAT.jl index a11efee..4b7a738 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -59,6 +59,7 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo error("File \"$filename\" is too small to be a supported MAT file") end + # Check for MAT v5 file seek(rawfid, 124) version = read(rawfid, UInt16) @@ -188,12 +189,13 @@ end ### v0.10.0 deprecations ### -export exists -@noinline function exists(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) - Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) - return haskey(matfile, varname) -end -@noinline function Base.names(matfile::Union{MAT_v4.Matlabv4File,MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) +# import HDF5: exists +# export exists +# @noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) +# Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) +# return haskey(matfile, varname) +# end +@noinline function Base.names(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) Base.depwarn("`names(matfile)` is deprecated, use `keys(matfile)` instead.", :names) return keys(matfile) end diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl new file mode 100644 index 0000000..f739642 --- /dev/null +++ b/src/MAT_v4_Modelica.jl @@ -0,0 +1,18 @@ +module MAT_v4_Modelica + +import Base: read, write, close + +mutable struct ModelicaV4 + ios::IOStream + varnames::Dict{String, Int64} + ModelicaV4(ios) = new(ios) +end + + +function read(mat::Matlabv4File) + seekstart(mat.ios) + + +end + +end #module \ No newline at end of file diff --git a/src/Matlab_MATFileFormatV4.pdf b/src/Matlab_MATFileFormatV4.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8cbe7e72d5a33ded36139939783ed74ba7646eae GIT binary patch literal 23557 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 M97s}9O-~*4Kg~M4yZ`_I literal 0 HcmV?d00001 diff --git a/src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf b/src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9f0db31aadd2215647a07e05568a3cf7135473da GIT binary patch literal 85780 zcma&N1F$H~vL(E2+qP}nwr$(CZJce}XWO=Ywr%@A-+lMKm=`nu#B_92XRXYr=<4cN z5vwYbR6#_HmXVGXinQgb_5+HEfq;R)-pC4yhlhY(#?;Q-#e#r|@h_$XML15N4Qw3uS{hU+u+S`u zCfjLi-hgiwCDdveuchfk;+gaI;!RJg*+^@R_rxN)A1<;py4P`%#6%4EnaT7z8Im)# zT0~DQb}QdQw5lkeHY{NQ^g|7qej9KYM7@j_N6^&K>+Mi!fF~r?cv&lr%PYf&k_)6pH{Sa7n{9XcE=+%Cax~#sFc7x1N&xr zurRwmKzfV%X1{muk9s(2+vRW=s0arvkA7`9ftfA$-Tw9haZ;j_m<ep*^H&CTKG09&LE4haxB`ZD=P-6#AGJ7NZ?9*< zA-lRwa?&RPdE95hk#)q{w0_Zzz1S-M@Y(KRw*A=C$qk=x$X)MOn$PR&_UXs$F0TLR z@7*t{o|tw<6=07H77+zQjJ=4epG1^TND8Czq+LH701O4}92Oj9EyOT03M+S=3ofo% z4T6)sQrgr&2C2vXX+VcG#AA||icC62R_T1Kkd1yMK${t&5TiXb5C(A0ud^@kq;~Lh zgh>0ZE6}^!Xef-%jFUT<+Bju_Qer%jva@60GqlmOG%%hNEF9=S$#NmLiYD36pSoCC zfPPg$j|{(p<%fseOxS^P!k;M03GRHCdw;)gU;&&XsIvMn}dPo$o-qQqbNoz-1eR4i5o zm79#Dd7~GqEZ|_HqQEjaofd*-@nfB@kPh|@xx2_3l&WrZGqec22?owN286$4H1Q1v zSeF|+MZ6&IL?N$W^vzW$goWqR=lq0tps3&|Oe5OhNN&?TF?rk7&1W{tpA_~-5WaVg zOu-BB^F;KWse-J09Vx`Ax@;ad_p(p5MO*gj;Q3=HN?J7*W=4-+Jaa^vR>SdQSdzl` z{t$HowKQ=ggRqrD%839nFhAiLmQyUE%L;LO9J$=PGPVt{Rib7(ukle9SC|)eLfL+KZNf&KF9=7DEW-QL6V;7oySc2e zIFSU4?g#ISH2Up0PCusDC#kHutJh>V&o~k5L8TGE<(%MK7^ls&F^cN1Q7P(JOs zjR+HgT$+J5Cz2{C=@k4hmAEMCMF0;_4pE#dPd{RLb$)sNflV3;pO?A z^U(>HTKdrM1?eYv-Y2IgY5XrYMK4N60&I?+MJwh@C1X20tm3B6Bw_udmvCk`DZW-$lcl>$SQD9~Yz^@&Db9;LAA#*6OOERP#B2`&UDx4sLUma)H zR3NN_&RNWm%XNwF$DQzMDcf~ZRiiK;tLu4QV*`R9Q)SH**R3ydSpa6K~O-AGL&v?B6BdLfM*GZdJci)H#U`j zw}Ie8`ql_c;iqP-MYT(7ZP-T z1Papc5WB?>EetoH_(Issy3tUihNkgPGEcdM>SB)-!7V4uS>yB>{JM9YzL^#mf{y)` zY_yWi*D&WFvxe!rq0)%exsrW6GE)535`2r}sC$9nzzYFD5ddj}7o!@&JyPHFdJJ3t zcb}IWRaoYY)`*F*XuY|LIdfqrbxCTWFZMs)TLO*qr5UeOWf6;t&?*s@4?+9T!m4w)}a{(1W z5Q3yh;jj`ZB=xrwJbA)GO5ha%Lo<~*t^_;J#CYSK}Ep z<|S2bx)UGm<$XRLXqdF=1kj3Bt!lc6F96gdZ9K6BehH$-wi&zPXA`+}xTm(C-?Gj! zsbbbA*steZR>7+*xF{)_%QYj?s(I880gp)3p=t@O}%=1 zm^6LdwqC6+oq7GHYca7SlY?M2i#e`mK;b#$o98g-6hJYdXk2u1Z}MO(gIKRONhkz@ z5E#Hx#o=FlD?=q!_dSx1`yVo6AWMZKokiW;H&&={0CG9MjJJ)Bn3sw#PUQIk;Zq#G zwmAaY-#rFS{IFe%kX zU$fA%>~3JQ@}hO4HAZVDeO|aDv_9)1PdQfsONAt0Ko>z+o{?SPu5Cb6K7C8%+Uop# z$A!%nESl+EfyQ4M-()d}aU0k$z+dV24X-EOecKuuqN;1@U%nKlp)Y}e_bJQ}{!KSq zP3s&m-^qwG0s1_U-^qz_Y;izSeu#Ao9&Zu?rtK7Ucz|0bvL~;Uw!=za2nMOfQ+_)9 zz01p`()7&GjS+%jCkm&&De=j1-f~=h%tm|cxpcpQSoj_)Z0k1HSX>aJur}M$6qX#1 zMlNLY0K3>zQD_}wb;R6U8G&%q!`P(ZZ6x>u6Aut)3`F>16Au#gv{q)bnev`jT_Jgk zA0arnU#`Y$9ndRrD+MuDw1L+~X}6FJx(L==X>QMS=nEQ@j@EYP-w}H1`U+Lp0rTMD zIhM2y1Zgy?OgL~RdviBmTNax>qRc~b7<-ZE>R*Eh;p2a*cPeeU)MS4~TINr(q z)HCfn7)!eutK;hETjGh@ffCCaz;ZIUXyESz^2Y(ZDh0{MM~=#OqxJj-#g(gSLVYPF zbXTO+kd#bziG-t+5uc4lR!ERaqaZ^d0f zQ=vrXUEFRpYPXxq(EsD=47Q6?&KAD~Gzq5%BFl7&A>pvs_5l|rIIgQo-P_VA-wE!r zXRrhclWVie+pQ_gBO1vK`yIx#DN{8*`<&;0-!t>zeTlDViu=CzWO^X&(te`Xglo|( z8#TO70!)}41@nJ8ts#CVlG~bjF|D*hG;57 zR{a6A#D(c7YW>bv(=N(=b+vQbuJ9?tjA8%x8>~c{yatD!E`8&hYg^0#3y0AjInV?x zE(86V!f#GEU3y+)%7=@HJ*V5lq5!6rM8f*GpRnkQ0DbV|D3CpFIxIh|FZ|igUnx+g zb|(LpZhx(RRNud(-ap1)vB%8H!17PTKV3%7|CWXS(t)BLF5=2Ae>ETh56?fq-;3Um z;cth4URaQTk$~RV@NfH98UA(rS4%HuZ|CyAM4Qnv&@vD(G5tqc{xgCZE#p5J^Z&${ z{=r!OC&v5_#`-@omVYp||B12wgR%by`}-z;@Bd7d<1hRlrK{rUU`jwQZ)BzHV*9TN z{#*L8{P%eDas-SV|8j|0Iyt)#Ff#wA_kUN!R~fq2hioWeH(%5srUl&*)i?1-Xl$Jb zXjoZtZfC?3Z80{fW9r8X|#5@S7TC{f< zNHG&44y2?;5eSl)fF6+~t7HZe&X(~bQYro`tMCPPQ_VLJ!jv7`-gmk85{h2YVfM|f zU9(tfULMZ`zfH5r%}NU2=$<}gOB$FqHDb~N%Mv3PM&kNLnP^E>1{%(@rNlwK?i;%n zhmLr5NMi$n{@KUio4F%Xix9Woge`PcP78O6udY{)$6t|tI-dG%{AFj8lhCzR>UNbS z+UTBPRmYUNYM$@Z5$xTs9j>z8-Lz?Ry5>7Ad{1=xsSJ&N8eI9P7ie{#W?#Yj9nrGK z`NN_I8&@0pJZRSLcN(k&-ze8V&$cgp>h2T*Ke&hr0w3U*zfNF2M==dXF%dQliZau1 zDCHWAq5usfNlU|&%t}?n8dg}!jl;XW5&$!hM*0K-@w3T+1I(Y4Sa-E@)U#cVR$R5{ z@$rT53@mbeVF~MfB4`+6W~P$!J!dAAaUPaXkOd(MyfL5@%^DKbCLt4*Y$~AkPw%@u z`jp&-BL>`UePWNxyV?GJPHQK4Km&ii9N4KDhVmw7BWVe${?dcGyHcgd8>a1gflQew43#* z+?)fga`#qFz^R;;;k&Uv;JeD_z|4$&W35L{^REJhKO{DXgOV9ui8ykW#3jVylqOit z6Gn@2Dl^`jZq={IwuG>@qSsjGMQ^oO=DiVlFE1Y1Dc3ZuOFs`i|G+T!eF9O1&j1yk z7F`yd7hM;f6PdF)=dzkNvf6Z%+f{)!ZL%<|6Gzu{PH==7rOG(#=NNe1rPFwAedHjCuwz%xq>L znE(_CVI+d}v}?D`X5FlmIpXX&S*;{JM}JH9vqXcp7hhsb z;on!+&o8_7)$BKZ4?8E`T{kb&7=|GUNLL~(``#<=FWnCx2lfPq-tdH0lC!wluiiU7 z(oqS$P=sFNl|9QBb*jAFfePOUE?M1NJ-*A(WpeR-5cER%NvzbJ-u@p$@3s)Bz0jtn z*1rO1Q@Vo`LJ{w;SufgbxHqcZV zbo!d^PVE3s68xBkknjtkWLCrU1F(pE4+;D_K&2ceKnhB))kCru4Si0>C{7P$oPgP& zQ`M)UiQ;mV%LTW?c=u~n4&ozc3<43tur#=AH=#$G%QYEtfVi6xB0qAOr~%h$R`XX1 z&lmYvN51!?{wa3uy$)F!)cUAtUOsnh^QtGWHkt(1f%7T zgz9%00J|KL%T_Dn$6P%-qT38?j~2^Z4m5f_qoNKL%pgMR(#v0^hTtk%H)7M4nOSIT zyl-q&%KeixQLL2t%UNoFdMnTJlSzH(t<*1m;_I?kfWgx?;KCKCc-sZ=A=WxO+4;UENgqwC${JYA36@ z5%&E@Ne?0KH5s52oZhRi@?nV=zvpe*3Tsqlr zftqF6S$n;S&-{s0ySlpWp6~2zweAgtT}5qGPn*4JX@0%!C}LIj6AGRJ7ebSn@o|<=)N`jr-wkJE^ug*N+EuIIn*Jlfi4N&^ zE5MI&jdVH`m>5th;eJT~AwkDCu5S4oW#X3t4yLLrVaKOw>F`u(JKd&n2?c`nC94u8 zWhj8?2!s~bn|Pwc4bUf8`x~y8gVNfORGu2Aj)#-`oRdsiZ1+&C4pG(Rm-~nF^Pjh8 z*1BbOnjP=aoH)+KC?mD*etoUaOWdNLokdM1;}ZH+`gYWX8f zFeiC#xsDn2CjTxs+pbqFb%59Xjn@_cs5nh3NNpAWq#r|bBizKZ4<%#C;r56JV3zy@IW6LoSws;1eLEoQX zt*-3Gli$2H%3}-qBF_^)FzeMm-jIU#WFC;^Hc-ZiUpB@>jey%fc-o8l#+^o=mcp80 zlDlgO11lDW(16<360_#_Y`z`H`pZmlZ0Y9OSCV~cux7h`Zp-H^twIX918QWds2=59 zwG+S0&cL#t4XtxI7hS*0A!=xX<<98Hx5NVNYsP}vB2M2!4m35nt`NL6!2@6ozZtV( zbD3c3%kC)kH7e-DooLcOx(fZ^076zHT8ZA@k{J_kGPk9+hdc!s((fE|<+1P}ebkOUWN4 z4Y-Qlwc?guiMqE|5Q*PctFDk;C|Pg4n^rAZ(JuSY!k>f+Y91Zk376PR8*XelNf^T= z5%qiPGjQbQQbucln-x+o($lQ90nii3ta;#nocqij5qXD>L`MgMG&D>AP_W&Dd(pLw zmSjw4r<=#(L;@#kv6i@8?Qz)Vk#i&EkIrP5S#qMRW=Ovkcvxzkp@LKYk@Hdq5$aFb zA(zt6Q2C+cmGCQJN9oC=7|yO*)DBK$imVT>eTr&hJg;;EM{l#?JaRK@ z154M*CtEd9K|PXPU4-kNQR3|ORb^v~+|9ZnKs0tE1=@AuhWU z6;9l4@bHq>?Ljt7)Qpex`7KhMr);7ig5i?H$;sUs9VGXIwbWjW#R9C z47m)sElpThG!hW_TVP-$z*4EpNTpJ4BsCO#U#gq+!DQ>47ojcc8J+=D6P)cLu#$;=|)2KcW#wVnX)o1Ge7^Lo9mBoZx+8lWGw$r zPv@3NV>7X0d{HF4MY>2RjfFE5ELrtjT9rz*n43mLi$+YiMUz_eh5`(tHA6{ruo}&3 zJ+M^Ar4QN1=>GoQKEWD9U@bud0)aBGnx>>o2*6w#8y-*tAu!cp_yH7cAPI<=vFo3( zh=3<`ETDsBOQ&VL;;FbHYoN9B^d*82J9~7f>A_ z$NJ27%$hRp&r3*2I%=RqXHb0ul zZ~VZ${ToIJY5i(w)WR~$NRieT7u2Qu<$y#thy5U0>A2BCdQio0Nz95dfes`MIM){2Q^*4I;kdTyf0+Q`GL>l(4U<;rOQCB|2!jcl?J$yQguPKI*GU#qTvFQ+|Bfp5csB-0I?K z(Z?xmhnPzfHAYyui6z(e&lyB&oIoMkvp6e08xfvw2DVDHmAjw9&WPKP*%*DsXP{^p z*6)huQ#$r;aeXSdu4=ey`BtHRi|<1PkH1N54Hc(6(gvM6eRQcf?eL&^o8MsN?rAn-ZO!+<(T+Phf%xuIU+I3-veWziGm%%vVBByx zS{6_0-{N$&qPjiF_%UT^qrdLFnKi3_`FXp??Z3U=LHMR~&kc<4D&p;>{Lw|FIjIA_ zGcC9{pAaFqjN{Hher`9ZdC{ZXSR|9iJEVK8X%O&-kY3v}gw6akOvC${?r|gLPmC`! z#g(PG1YcjSnY{LldWj7=LtX%CBt{Y}EX|$dooZZ&NJSL zM_>zNGplV2w)w+ry4Kqzxz_G0hv(8|z4&00R%0<;F6-sh8`AfY_V^^9_-3l{@93wA|T$GH1OfSHfUZ@`- z5muGL3N;p!8g)-`!r{BK^XCgyidd*nW_I$$Y=?LilT`~xa zg{mNG2^2NU9|mz@4U);0S{0BQJPL@h8>8MQ0WrbS3<|0t0bD-yf*?fskp?}}ndZD0 z^Ye#b@`ETJt&4}en2&{8mk1235T<^5jJ87a*q=1hiy;iCoYEA=L_5-t;U`G-_?}2s=ybIpeyMqzAXqMzok*E4a|(F%TeEi9Yfa- zaiX%ma>u=>C~1dn6h86uTW#-uGTCNKNf!w)c{hZ#bL% z@B-dgqobbSWKjdR$fKQB&MWpj;^Iwue2uVBWam-KBG{ zJ$^BcPd2;J-C8PhuKsD0zUpRp_}B*8c7=!CqQwL(GU#>FeSrVNwKbyyRv?iaATyMDvanVHt?KW(z;55yA}>Ct|sV@$U!MPJ6g@Wmp$7$t0# zPuT}0d_9S`Vt~gac_U23MO8m1P#@H9;F`de_iPEar7lyJ0L&evOL?NjGb6dVyWEF) zC`0aQBe+-^pp6mr0R)zeNiucVgY#SuI9%XiFwsW0q8?=z*Ul;9MfX#q4SP9aLT_!S zQDFk5_BO@l9PimB{#dA+X-UuJRNZZjoV(fVd5zOwR#X`^x{CRTazO`4ZFCak5F5eb$VsOzB4KNJjcEgkR^Dg&V%c5Hy#o!| zHTzK9Itiv=$*Iga&d~W?`GiW7sxuAl2526)%H|YQoc4_E;X7y8L8Ij!B6YA3xwf=} zq~!G=R&Xl`3?7)SQc^E6FX6NolXliL|2U$Q89=_XPRC0v7cg6wk{AwLq7xwlP4H^O zbS_sMvirLv(uT)Ql#~$p0V%1P?De2yEMc^pMyU9{+B{HY$d|4wqES}SRb>DOs}C>y%|bXsF)I zG3+k2CW10%9E1)PZvUoay1Cs&OCShL-`qtkKSYPoI!gKg;r>8 zA=t%nT%$zb4q?HYveX2A0!4~e1me`iQo?d{m&(FiHA+P)a|#rT1zq(-ML3b<2Pg~Z z=f>}RyhW=Q5#GW+y#yL;6cS4{GfI*82BY*s3HlQ-2yq2<(8@Yc9oCF2Rfr#iUY zD65v_PdyTBif9yfq#&Z(3h(=DWFlRHieCw7`XbRRk(nm_) zd7Q(!tWa8G&1e>P#*c`ann%Rr`Ik?->S3xJ&)o)@%>{tmQwuV-%J(Out3S8nQ#O%1 zwJKBZM9nO%!Jh|Hv~iz(|qCLoVKx_*yVrVKh_&_gTF4m+0W0au}?dfu)WqumZi4=Y#B zrBq17>;1?**G}22dc5uX`Txq1ol~NlY$dLWG&-6P;%_nTSiLT9oZ`a8^k3J_DqvHp z&+A6+{WbNjU`Sqv)sahC zjruJ!XX+u}zv{bVSl7LCd#e?%mPh+SV|fE(IL`kA0(v@@mAumnPGy0We)>&GP_b#Z zt+~-ZS|}GkTW`0g&~EfJ*}T&Z{xynr1u7p(!}MurmZT&AyP?5bvF$ z-W#O^O3HA$z*lRc2YjAJcHTv?#lrmsm9dAnI&D=+t5F;&+^_l~Oq*8g&!A)I@*z%K zHp@>ts9#}4L6d8L=SSRUV@E|xL&+JtaQxSCEKSw$wT6~*C(C4-rISXw=)mNuvTZus zrB_u!g{`LZBK-B*`+L{Q5p`{^-op~^MP9xxcUytk$k^}LL1(j;X~Z{ELO$VK#91<^ zA=Rc=q-AG>ogqiaKr&g_SSFYV*Pmi4>!kugQ;HN$7 zsrKDnk6zeyh2?-=2~JpPDGakrH%$jk8_aFLUIGy?DZMMHd_u(1W|bu0JRIV);YG`g z=ff&-rF6faF?6aoQenrPf*fWPb*jm3u5F$N79S2iS#3RD6nc4YiDg1x()B098~8|j zLwf}23=+xnbh@MM5WkXS6PXf`?7m28J5N`0iTP)<0lxRzS@t%ok%~1*SGDbh8EDQ$ zyBBm7R+d&h-po(wdw1~g^xBytT-#^x_lD=`4L|Z8dd!T?%k0wCitO`a7XFLeD?h7V z1Q!$NXtdp??CVKwMXdGfrz1+AcOG=n3WnSZl$BNXZGDu=wT>p&RFUbL+9!Et>`N!r zyKh!-Bs9@`acy^R5OwptckmlL_;xE9B{J`C7VA{n$#rNKUFN$u5b2p98xKXw)(Dp? zr7={g%o)12>?ygKeBu{sPxCo`WyMRIq}lnYzsD?@Km;L@TSz0Yytg-0Eup@A5fzbR&1Hp&*mI*TCJ`I9ch{0 z_R(T6osew}M$Vkdc2?vlshc>50WE7OrJV!plL$#T-lXn>?+!0!bGH2f)PJ6_TKT;m z_o2setJ3Luy4(Bwo1wT=RaJaihBZG^X|=Bh;d4JOM`yPlV*FRy+<6`zR^fO2stN9O zD;+0$a3{^hbGNoG_rpiGU8Q-i$@1+z?gGo)gYB&4uMPFUx6MLz_pFJY)F@rVr~<00 zWjSP6sgBL)hi#XfJj;wE-RN7?8Wh_?H)>B;F{v^;(n!%Zyk$%cnH|Kyrs1_&I$+Gg z23bncAvIE463@&j!Whk9k-`b3^oUNJ>&+Dgelc=L;;)$k%8Nj#<<7*Gv;zBvy=d?d z4+=2{)P&;4X^Y@uqSV+0QALyVJX-&vG>sJE86Z8QCHiVCD~|ExwUnW2tnPdmPil;c zi7Qj1=hD$rjJ5tjo^;T>2WauMbU7$3SyX$a^x^en&{UZ(LT6)P;lsA2tIXAV(wWs> zOX*7R;Upfty&K~1b=toGT}GltdI_tV;!aXD5#u~XMeHpf8XB*@XBxNR(N-e1P7`m2 zeP6$R^XM@Ntd{Okz+RqpA2qo+?8a<6^Pci;#J#7*bU$a$#%(p`DoT8&noLb`(h$_p z@An9|tddipDoGP`{9qNJ6!L>B236<^T?H-_sv^nrGJsKlQ7c8s?ziAZW5+^mPOmQq zqCpqU5+!$JAqo`dLi40KgH_qGBcXgU$yrdC;vTpE=H$%#Wc$YTTWPfW7+;uUx!Olp z>w4`xRA+OWzNG1Mu+_!Vjo)og`s9o=;N1!ESKBIbx69M^H*=I;Kk4Q0>(?i4z?#;Z z9!0JTo>L=vVbso4N(skA>_$u7B78APGYQMAZ;7>>)G@enabae>&>B_iie2UE=Gzsf z>p#6eY6r=m+&v$XGk<36)z5NLjf2YKV^yn?9WD922CV^|gvRK0s|&KBh3cz!LYkN$z!(G^NZ&V`XSEzG|Jo7nR;hKL^Inohh7Rj` zf6mrwmHg z=9fB)T2jYjawN}a^z;EMn~^NL476vhmFh|OTr4@f;mX!h|E-c~D1HQ{@7JGbtLu*5 z=CU}w%0El%?dSYh^|~9K-+8fpScklMu1-@;-xCbnqN?K9{85mOWxiJal7vsH?>7Bd zGAm_r@OWYY`mRtcj&C)n*cuz_wb`X;IjwMY1fk7er{Y^4{Dfy^D6H0L3|eYpDqM29 zv;ZiRfyPft-)v}NY@2db;TaZab-#l&aV6iO0I4mM%A=ajui#zssIvTUe-hkX)2%{( zC#F-ogSQkd+Q~iVdX>EN2IWm#A32mwAgJdOmCvx{NKl7u6W=Ie)nN_xK~XF7tCg#D|Vil@P_k?j0qx^1!?p-WRV?OeWK3*2g<|{Tz!o zwb}E2`K*p?Y?W{5NR>FuDv$#^g*}!@fj_Gc+JSv1>}DihVYhVWsN08-Kh}T#EGvUx zceeo9gD+0-OZUtRcu9Sy{AUwA%)Bcae|Yef<1<&{#~1rsZ}d=_f0(vLmc68@4@P7R z@FQJ!s&fn-KO(r}Q$1a&E8CvRkkDj{1%7Zhqd=zF=;_xTBp%FLF#;VYQUb_X0@{LD zUv$^U^k*ma*U#C)r#-jC@*&-Nw7@b$zU(~2BY*MPOF))`Y!~g$Zgv;B^{`&P;Q7|i zMat3HZM^>L)zkyWS<8|OxL3`!y0t=Q_mt*q)L(fd~X)q*XBMRB$i-w|!O2>2A`owfAV#b)5|$0~w0 z^5nG}iN59ap!WmWhPC~U87K#&73(&NuPeAIs%nR`@9(Ur3+DMkoIvoy-Za2-2iAX? ze>n5cY?tb&Xqz`*N-sF!c&Ggx5}9Q{nDhZ5BM=1@|{NZ&2O{Ho&Gh;2@Jl6Vi>iv_Y)(JOP$yU6DLs z;Vrc7Ty}mDMEr@?dq%!EyZ^R(B7MW%24W8mCoDy0|))Vm4|Cyb>`}^7Zj}AL7X~MV7rkevQ4CgL1Y{SHAl&B7wDnbe<8NeNgno5m_ zsG!$!<;+?5bKADFb$^pip^e;;#_~Rba8hV0LxFQ?^W| zVkHj38p*MtlY49Ay=#z-$@oKE9G_d9Xtg1rbG?E#Z%TqySZ5r;X7a|;+&>tEgsowJ zbyT}YlQ1N{<{&&2NZeQ!##X4->63n%a6?Dw0pN+Kw-CG|NdYv;d4cFxoVA-5LsCZ? z`Fejp`0G%lCocOc*Ela$tl2;aI57|mM6l?}bXbWA*u#U8wG{9iwzha)M{V{Lb7LI- zR;Na9K@^gxqU*MW3-r8(bh!M98Q;nS#ME2>M@cAz6Bb14>a3TN{C3pH(8J z4q}W>m)Vx73GeEG_+Kfd{$wyb&)1M{pCMI*f1xCjctAt)^a|sQ>ya-FgMnXw^w=@5 zt@+v+oj?VehxR9qjP&qkAlVQQp0!)l zMcr4!JpvXS0TnpzB*F19c?6Ryk)EX@)dGy5|6YYD1&xmAtFZ+Us_O68BWfh5H~fG= zLr%)-o)@tupdwKN0V$hnNQ!8|smQ%*z$#Q#AhxpsCj)0?_7>^uJc$jty9Sl!nh6G+ zxUsvzC}B`V&c?-c6xJQ}#6$V5A(g14B*DZpo{)GfEJoMbmJd(dtXyq61jM-O3`g7` z!>1XhILk?;I#P;vh>DLAmizkzC%F<*zSZoPOdi!HWm+V<=bk9vL}JL1;cYr4ENB(E zXpw>w;Av^!X$+Qiy)TH@GQK@3*^>prg56d8J7Cvr#)Eg)2JX?N2gWj*B1v+f50H<(C-NiE>JD}+zTjcmv! zR}YRZGe$HLFD9s4zlam3l-MYsEI z#RVyX0qL@uEJQqHaD0<8`C|tzaLiX&GPT_-U1wr#aUqzYP6E;+$PwyFICG<1 zUc`7XJC-xKOPzlBps-A_r74yMXI@CgIgO{tT;6R4Q3X|4ULcgIP@o>;&~406KtcIs z*cHU#HP~&8n^6$WF5QM_(e$Y8|(Y=D_5SWz3%Inl1WD^=eihQB`sl>qb0#IB?6MP&LFl3C<(~95` zhAwu(C$+k~(={ZCKi3t(bh zrdgH9*G&uw*0kuNs7|>;+r~(lPeCJO49qmBa+2uLhvAwygs1@WI~IU0!#`BqXVc{6 z&&@0HPxb`5SIHpKuE9yC#Zll_-245Vn|5jMC6K}gpK{y)+3`cdE+m|VpB2i!9v!Xc z$|mw}Qu^D@&*UWE4d>)>hjDkn6Ljl!1vNm574)m)x~&wNuyD_t&=Ppew@a0{q!jh^2{Jk$4f@#41ig zClWLYbAhf*?+KvlubnOOu?TjdPN@;rGMgO&f3%-hI|tdIE|>=!XV8+JoEJJ~JWQw- zwI&fC8bVW_BvU_8<1aS$%q8S@>w|lnLvxzkf=x&l zHpZ?v2HF5%>;*OcBOF!U(Z8AyI7<%bqItV`9iEx1G(%?2^b@jiN5cY+GaAal^DfEg z4wo(F&jy(2h{6m#YdAmOsd>#Hw1Pz!*I^OB_=s7=A_}VuTXnO&t=8@GNMcz`FH8 z$OE^BjqnXT00KgpRuYyJ*21u&f18i?jY;-Kp0Q`W7H9hh(ouukDj!&OO_7^YHOM~& zv}B4xnrp#Ojl17Bio9KZ4g}q=_sSOvCY||)dacpdg=D_ZKKNdRM?8de?X3EhwpW>yPp0ohP+A1oz?F_k+qS z4vX)nDUKiJvF@wVYe{Rm#WC(PZsn8b)^Gnezw<|BAa~FKN(lR01_$|^F{D+u0V{f9 z^UdGLTDqFI@E-f{7&Qj!%{2f2SKfzaP5{*TDuFUfV- z?e>1Uqq-rtun<2Wg5GJ|(k*#k8=B|*Gy6>nw@-cm2z64lj-=P3gc?RSrvjKMwHVv7 zg|DZ2K!dhL@JSZFYc1wM(0&SEf*0VA!QRg8-CvwrdHpzc2>aW>PS=5e{47=2+oD?H z0|R{^FKYpjo&EyD+ijD>mhO(7<+{K?FT@rfuK;+bZ9<)=r`xIsu3cOD!`r~XiBEYu zmaS=5A#dB;v(D~<5l=5q%6nB`>J?o+HT(&Q$yHD9Oj~$0(qn&eH*Dwzug4Zlv~01G z*9+&dW3V!pW;L0=a@G$|V7&N8=Q_3m(v~ZhvtU|;DwL#6omi(OmLQa^=(sav9hGoW z)TFgbn$@tE)U35zWLUSQlxrR#vxs)nn}dL7*UBb2uUI(6)h9R=xNx>t&;}rViGidU z4Eqr1Gjbt7vHF*?@+sh0$WUx^afvbfNuf$0t=KUfuOQ9XFBPx;OwpcQa)j3udkH*V z5rB>?_X)$Vi)Xk{VZoYA%RREuonn-Hyf>POBM%#S{rE`~0qA72+poi@6S%sD;F@e> zwd50HBs?{3P{J(KToy08Sg_)1$%d;f7W~`Hw?T#Vxx?z1Kwxj=c2q6j=DBaGDwNNB z%~aqvB8$Gt<;RwnBdI4vSo2a|omQ@()h3TNI5zoCMm52_Z3}%2#dZ-q{3%^y^ozmb zwm51J{sMo&_4yv7H753!>S*jk})oq{Re9-zz$ocQTgClJE;Kco-;r@Z2C_AetN z69fBy5{Oy<59Ro*|L^7ax=E9E!3+qZFW*q~i=rTF0oag)s%K=;d`r#v0+)oMRxnH^ z{ky9N*di+C4^KKeTa9z0;v4kKrR(LL+c}zn>Rm ziK1slUG+|#Zoeq9#LV#Tob(2$*yZhb=7STI%{)HuHy1xDr(?^$AG1xw6V;lukc0MUqcyY~FiN54U@{ zb^DK&HgD)6N~wx0^F~EkTvIvd5?M8v`GfE?A!VkxZ{s=u(5KLBh^u%Ubwrej)W#cN z)=0!?1{>VS8YiIn=)KooY=Ci-U2tY_M}uUi*;rYupKBDPhg&c}^@rau4vjUyb(paK z9?0tlUyNb7TGmPzxDB^hTUf0ujmbLOL3LE5U_V-~tezGe<5@k*I`6U=ouNQ;o-X;H z4u#_1J!^gD@VLKlHmoA-M-=bLH1#Vpdi`-Vg6@1%0Cq|BBsv9PL>WX_D=s*d-4~OGqn9%t0Jc;tf{U>EpBS>WNv9lD`anD z^4}3+hPIYAo&*&Cc2fSA!^Y6unShz;zoUeN>^%sy31}HP83<^Z8JGzenK)SqI5{{7 zbpI72>0)SOX)I`G{{M0I&QX$d&$@7Xn$yN~_q2^_RNJ;~P21MAZQC}cZB5&@ZQHls z_jk^{=dN|Xf4-_(tM-nJy(8jTnOT`x@x->C-YY zu>UI%)VCEgGBYv#Quzw(pGG~k#7OXOlDxwIWaj+8 zq4>{)P6m$ua&?sKos9lr?ffeh(04HUmtXY%4a+~I9YIrl`~OBjO8>w549pDw&hdYv zO~CxWnMriQ)?all%&bia=v2+D`K=wy{#)?xI8`%4M^lF{AQ_o~0Cswoe`!ho9Jc>? zGqSJ)=$RPVS^hc1|N9MOW&1BxAR`-q;ol*w^b7z7Rt7d!<}XCp0jx|c^z2LoEQ~Aw zCKe_(rhf=f^aRZ8>|g2(|88Mo1+X)*e_{V6XJKUld|6@!G7zx*Px*fv{6F7pz%MV^ zn1Ekq{t>d#vwls;_T~4#-T&w@&@%!7|E0plzz$$zWcrVp|7>PqV*=2#G17k-`)4*L z_J2M7KPLWXiX#yb5y-~K_C=2PQUv@XXZb>lor!_zD+Ctcm$ffnzxH7k zW>x?z!+!zC%t#NQXZRN$W(G#Uzd!*Afb6Va*wX*w$3OV7GPANV0122`SO9G7O#eXn zKLh#y(`IM<@&WiSkpCW11awL^%GPH8R81lH_ssr3_j=}kXm9_00{Abk2=o7@z5PEc zul!H02qOyvJ^la171?ih^T6p}r{}3_YmL@kUucO_NwOM#Sc$WG4lC?aMIo zni;#tHpMe}=;r19L2`aEcJ}pp*9Pz2xtXD*W9l{(RJ+XdQyubYuKRx+}~!#?pJx-EGLbzwk@D(muo#wyOUh1+zCVE z+|ldjt%9LCC7_XJh!R)Xh*SN-r5^W`BEg@C;22@wr>?bt!05~PbkR(yl9_ID(8DXfoP zC8v}hPEBQ1RLLZiSIE1KNUAknO_`6|2@JR&8*5QQf_>t5EZ$ds=W%)JiQ}xSCTiNk zv)?>ESo4z^WNMilYj@BzlHRc*k0A<L2@@#%Ha;jUX7b3hNhiF)bD=X+@p=U-_tib^+ReAK*+5AqX2BUEk3e=S-Ze~gWhQ~m_C z@svYKDEJthg96)oDOSYhAuUdUVrT$5d51|Cu@qg1jTf-72RIMJ7x{3dT_l!d)um3Vm53)-W<<7I8-q{;&{=yc|Ca|_b5dL?MK#PB1Cx8 z%BsS4Oy@FhghmE{vC!S6Gh+)!(S{%@QgEja5hiN^C;KJfM<6T&m;xVlV%0E*(;q+V zOL7wwG|%Y!%_O;uoAx>F7LB>1-0KEATIz~qVT0~-5dx;^4u}Q!1Z!c2*?1@h!UdkmOqL*`J4JK>I%n_HiCP3mU zoeX+LsSyJFpxK`JD+<{MV!U&Mwndhdp+}aou-{P}p!#tZwC=c>0ORvL1xyIEs6qj@ zi1cdrM-NT+8a*(J5DSbaQjquP)O}kKi)xGTo5w;~g5UNEwjWXv2{n5eD6&qnQ7)pc zf#dIUjXxBKj>g@(77OVq&(6E#U9nV7*gx|rEYv#M$!ZMDqF6D<+sBh? zKSQzY=zP!O@nF9@8XsILNim}|#|Q}K%IQ*&zy`Jo3bmUwv*12~~*lU^5{boa<^k&0uORbI1VoZv&kNpX85)WbXkgl#*nUGvxJDrpD zZd|t%(SUgB7!-W3#CDWz1~!ofR$my{#LoviT}nZ}ogkGaXD<;odR(wPO)u<9mI?JE z9M%K+O{7Sb>ads`e-aIjo;7mDo-4mzu@o+uJl~d`-aI2rr)^z?K{YRKsW~%;5wE+YT}0(`oay;YW4T3`WR>aZCf?Pzm-M}Lqy78h zXl5OJXPUiLtKi0pC4#Wd`%1Wnt<7MT`t=vJa0?zC&021Sjdo`@!>nF;OBR)H1y<*a z-u))!!(NxQ+vt(#<1FQdP4lQ6YS50hbg!67ejCM%9O_9#XE{B9Xz2)d0RtfFA}Ke6 z9Za3?^6;4nya7c&kZYfX#|T7b=Q(c|n-C2aSNOqz$gLq@HY~jIAp4j<%Uh6`Wa~+$ zEP&~Bbok2M^M@{3GU;gRC`%Hh$2T=@eet9)$E3T4m`)n>92)fWGhm_HM!E#*h2Jl5 zTo4P8d&(KUt3XhB=d3}Dq(#%PC=(_DJLuf*Y4|(}l&tXB^#KGkzG$K$khfcds&4&I zTOAHCDiC#;V1;35-cMUE$=SUqI&Squ(=^?_6wcAIg(P%U9>?2m-Mn|htO%0GxvB&M)XTj|f@2G8{?WB#CasEOzqG>xugZoli3Wq>sY^=|CePT5B^ z&~cSDycU8R6*NRFVKoGhkBpcjj8tvEycBq1WmeoP_FIt2RUx33EGWRJzTPR=m9{gr zBv_Fj<(sT@5nvmF|C)-Iga>Qs%OMxUrr>~|q#33xEicMD);i{@kt)Kr4;obh*9-F4 zwb$Xu65dXAh<7JMJ`X>r#K{}YLo>1ded9V{y%=P){F{py&T_N{-eM8Jl{T%=7h8RW z+X9WGT2UZHN(skCYyeCcV!-Iha{A6g>HaeT)nN^NaD@HIXfvLZI;jw0dNq=hCYu-`b|N`CM! zX_MCSAHZIGWRav+Izl*jKg_hDHbx9K=P0!*Bo}Fv?ezz(OS@N2fx;C16RC+ZMT$(N z)&k)0PK#i5fY#tMf=V7oC!sDuzzbv&iz} zCS>FIJ4{wh#?!R>mOw4YH|Y~a^cGOe8CaP}N<-rZ+-j)Ng9m{9r2YE2^%bGC)x2qx zm}&P=<6wH$oCJmvzWJ*HwA;BWC>_G!EKZv0 zG&v`9+73Cb&ch|wmvw{69I;WYt&9?=tZ0VVJ@3gnKcoGS*t3E`kOE zZ>ftWTXC#pYG4fO^bcgmh+z@zb4@6eo6)^CGPb(?q>mi^_?@Ii+q9yHX~ejMO#9&4 zqhki+XRuJr{pl-|5zB&=;K^aF+HP;sKk-$y>RuZbe=u49)r1=C~(r0>+IJ42e}vsj0v zgEjJfM-O;S5k1iDIJwm%&-OrRtMi^p2~f&(=*D)!yzB^TDaIeHqq~#+EHf%Rx;Inm zCKpouIs>1GBH!uAXeJg95hwQE#&EmA7PjPC=H_Xp&QvQ}Dm@(0DV`}koIa*NjW33X z3|>QrXcbYS=u1Z3`epM~ju;xGy?I^AFI;$ICG=ySc`+7sgkYm9X)kxFPb81-ukTy- zT|**F%M#@}Bg|`HQ{gfvC^IVwnP-I_Q2ypf%D-;+1@$3elCkP_oM5ZxBpPJ={%tfF zck&AC=2iW2nhR@pt3`w^$!$B_D2#x;Jn1h z=uN>hf441T)*Adg*gKX$SIYd|5gH@HpNA$4a79ee-&GuxbPd8`XIzgG4SEdlnCZ_`RBebBy6Y3{=paSGg{wesWBQcBR`yQy+)5Owv-gTO{O6*kZ& z{}}2l<)1yDBf_=)4hs2{DvFy02K%1-*41^ZobQW%b~?MgaFJn2yTgX8Z9E(66w87K zq(5gj6Gxob$ti3~!pDs}qOeYald3iN??A(emRTNT`3}cR;0mubU>`yJ6L_T2NMxUR z`n~qE8cwblU}#Y=7{a643ZPG`3F>i)Gj!x*39@M7IW90!D3e)Z#jajex6n_cE!J#A zHZlg4RI9hnDgOY@B&~}UR6@*sk#N#jEnQS3NMUVA_U}lE=J3Mh&V-2!-(nV0LM+i( zM{weCmgiYdDW^}*+2gBzElF10pp2ac1gI7{W&@W(GNmndDSen@x^PMD4KZt4ZV&i% z(+u=as06MGc9n0GKIRk~A@Ia;rRk5gJ<;-ws9W6#r%UcE3a)A*NS?JP!h{Eb=;+wq zC$3O<96i-j72hEFnf-B#o!!7&iYY(bVod0{^h?=ad~@0w_2S|cHW(3?NHizHk`pj1 zzWzgC##|0f!hJ-4LF%_FsIMhmez~i(>9E_m?M&$1ztouVULO60?wQ#97~P+%YSXrX z9RAcUIXZHAqgwRYY!|s^UY7=yR3)BWUV6{f$XU0cN~2xpeiv)C{V_4ok!U~e>$zGb zkHu_VS4pU_dU!}}ExYNwVtJ-2(lltzQ5h8wCaGlp{THpVMCw(RAZadNtNWolUaPzUrw=ry4p3RQYJz$H-g{7@57Z_#~A`h_;g_;>8d+y{hk86`w~+L_io zN2c}cnQ)m@MCN}79&V`#!ObgEtc&gvLm{rg)sX3P2_9&YpssdHA;}`}1&MV0g*Q6% zp^av*%b&=gzyh0^Sf)<;o&M0EPE9k)lBM?Ryu6$Du5y-YBx)p=I!?o(dz*rF=XCk^ z{5&nIi7u~vOZBzvQ@1SDZW-jyC4HmJzl~?xJREOUFTKm?J>D7>| za-W=-*u?xb30fgxFrRD?1Jh3sFahdj1ZQJXNmLK+1`m^wfj{7o8L320&Gf>Crk8V; zRw-3^C{&<5?dtUDZ0o(%p-uPu9075{1Bhj}v6U{o=*us}=#xEl!{Pl7F9LjUYXL`V zF)BPc`!dfX`OhI3pUWW_&fZjcMq!__zcd;r2kNcI-G9Ph&z7AdKuIv+MOUssT49P` zDNKw;G_#;5t>CBl^-(+JK64F+tfxZ_0I&jH=+G~Xg zoMsK>Gtc9f>8n-<%03zu(&Q zx5|S@C;B;TDkED4z#^r<6D(6lHH^aDV}d(q;BB$!&4)Xn>krcH3PGTI@%T8`maK+; z(KeuWJ&x-LzV^2Mloy?>ooHOR)O!4eJMr<#sl6LXt1XE!)@CCAsYcasBsc;D;!nOw zmx81!2X=|%xP%cZQ0?<4I*&Fh!=I>?^XDpKVO8=UXnUOL?nNqiyGWB=&$Aa+bFAG)ry$!h?__eD`B^#pP%EfS_k>pFa z0@mHv?>S_0r%sgk%l{VEee3Nk$(S3sZ)hx7tF)SLT5a8rTuFSHZ;ZTgK6l==-bYwD zbz;S;My-ClgwevhGea=fK{phRhPHm7<*3xXo1}dO@A2=UPl4;9hgMQ)Uv`-5b^U(Q z!#anR5oAB^#34g}^ahGhUaNqZBlQamk$`N_c_2kiB41YRo+IXupD27#Ly*s=4kd9H zSPrBQ==X%41G8=djnHQyyo(%LYYxwg9D2`-LjMyp$TP_MIsW^Wx7=3PO9_9UbBAJF z4u&D@3PU+8^cs~>*Pw2sBmXOkrK;Kt)q00L+JR5LJ&hfmjOo?|*!fvyR~#ag zA7S_lg)~xv_(svV5I!WL-}7@HhYgRgox8$nDgI1>jBg<7B76MZ2$ zIOK%LvwclQDuPt;WcD|FX*Su4(vplK3QnPdUv`#JlF*Sy=7o)rzAS-whULm1-C_ z$$DWD#6(&LA(K^!hMc|8^<_T(;lvCdJ{0yRXB?R%O#Jjf4t;@fyez8W@zQebSBjx4h$$nv=?@SK3O8=T>qWe_o~CV! zzu<53Q{Cmik9qNAE?+nhw`J+NZ}ZrZ93t=JA#gLHU@#PFV3!TO0SlaW4t(OoOX?wGB|fDNduggF8kXe0oB|m01~Kb7}9ZfYH|3FcS;aU z5Ee{wZ8nE295C0!95+`pKu{J$$dsdPT#(O*LF866-Q1nI^VempHmmIU&&LiTj4g5n z%p|VL5Vkf69&2QYfobaICJg@vdmia=w-F( zv&5&EL>%yLMBSjqw}}Sx;wCw>uAhYrn}!B~7{2=w%Vbdbao6y2>1D^JB1V(!eF4NU z79uz|%i9LR;#R{Zf>PN%nl+Sf(U$GGXR5AF#Iw1ovt74iplkes=l{P48Edn ztl~<>(WQdcm$hjJm8AXh-fFFRnlo|3!wG%w>@LB&E`3fh;eq`_VJ2SZz2OP$|)a} z4vG9A;1b%y_o2Ki+bbi0(V99n7(;%5?HFivwb+>n2~ll731z#C2A3RWQyhbiZ%Fe{ zObNp3Ar#kIwM^*4%Nz3^4O;8k<7Id%<>&fAOhLi4(Z^xX=*L+0Em275EgGWgu~R-# z#7sHFpz0%G9L%YKQ4{DIyam&0>olk+o0KAT$=;L!<7N*Q-wB260G1bZDPK^Aqn9d= zzy@=VwHD<6xY4rgzh1r~Vwm$m+HoW5RuPI%B*k=~L5NOQ&W?<9m|y12()O}>-NAq) z$54M+dt%HlGpfHGj@Bvaxr+LUT)1~jE52rn*7Ydzkf>(ZWOpqIekR$&f|r%F_g)Pe zo9cPWjr}4sG`mSF;l@4FCd48E^xks*W0Q}yAy(7Y9Kz?&KjL`d6)R z*Qia5uHqR9)zg`XG=ccpQ*}Wy|ePKMJW<*%l+}&}WEkoW8#ArmKJfrjJ>} z1w-Qx@fm@&A=a9tF=0IF zV4pe@21@XxXsNx3EiHo{@Gj_+ zdW<{9<(ZP~Qbwr<8CUFCw78NOivAjcW}XI`qIgZK2?COxVkwFBpFV)MUrR4gOL}Pf z?R(HA+Pyq%?W;_WnsC=xt`WSP6E_}TDqA{B0a{-tEXAN63qLSodlI?M%o`ntoSx20pvh4jwx*Wa59ub>zO-JCI{2y}Wdj ztCcZYNMl`jzmBy%dELRxeGUN8dPIWsyhW->gworWmvYIwzY}J4js)u866-WNc81lyCdR!gboeCgCA(1%WUI~ z?YzUn_YU@aO3TJFr^0`T2N8-8=_m17`M4RrSd3A<-E;^Qe}WPApND;wS_#itJPN6$ z((8@l6EBddcq9kY?4N9NhWtjVt*&06l;6p)g_s~*Kak!X1&`)SbcJMw6yPT?_hgkf zi;pq-CJ}vvXxnI?M~M_kyBczSN^-MM#62du2aOuKB($IaR$zi(w=aemvH8WAS=$n0 zk^mz}v^O-WSsr9$sT-OWgVqicSH!MU5`qB5p`Z~+ zDa;M{p}xbGtlLdf-t74@S1ZYWkw$YF8v%9Rd2#HbHJVYl-#E%p*YNE#4Ds8~@XXoC z_nmo}z{x5*Gdsz_ha1z(()x_kGea7`wo<6mls1M(J5TY%Ueg}eG#J@W?5jo?!V43} z%G2cxbZrh`0@Vx*Ot>%>5E*tFyx=YW3`_BWKzh&2A+%jU=%x})doE0F&f{-`EL%^; zl-w#sG8(B=HK7HBsYt?+0{D2mLX+~Xkm2|21JxUlJ~L}a4iWp&i-d-XvA!o!AUtQ1 zem5T40AtbLk%f{{UqF=sZ|`qRJ8d1Op6J)q%iU^>O4ir4sGMJg>FTG)bx@f>M1wEr z{oI75!)nff2)r5Ei^Hb#L259si+YhcF7%n1OBFM$P&~(4e}>E#?C5Ldo~4L~C#@i! zI)t~A`K(+S3g;#*&?6S{C}|Od;or^hbrS0(?!koj*TC$?{Cx#9g`B9_x zpflS_qp8YyM{drhot4V+@&XsE}&$t z%+?x6|2`!?EqVFWoK?KNh*4 zuP)%XM6;u`80p>Wx73!!Gnhu$GrYpcfu%rZcK&f(PpjgNoq?#9AJz@GK3xKi?Hscb zFIT?e8QQ62OL2=>CY_v7ERH!5(lYLGyHmTmHI*l*Bua#073viB1ht{}twSTdQJ__f z#1NS|hS3JFqnGmrKt$q`FyAv#Qp+C-3f5B?5yT<=nKpvL&idv^YKTXz$YCJ>Q%jX5 zD$ODDw+&h&&as1E(7!Yn^zJBreI#?_U4mu(+lxH^d>*G!F>n_l&zDkx3f{%!s81~p zNQ`N-7E_drnn{pKqL_o>KZ``KU-e9WSQMOMg7`+#X7cjZY6 zt1%K-+a+@UgaV783+b64(VBeR8FqnlX!ui6haP9iUZNo_-|1q=b=bAk3YV)^H#y-C5oP*^WuerrnXzvTHG(?>ZK~=E z%3GH&DrO1Wzw^++$Eg-y7(EJY6sH?Z%Hz`aN2+FwJeO_yiRSZ6%SJoXt=iuje^V1}^tWUj@;&gpIsFkoqi7l#uk=`c z@iR?`jF2}<5!#d|jMp=e`eJj1V3A%QQyQ>Jq4w&}m@D}%@Kj1rMjC_ooVn+2Bs3`u z-6rfAzC-%&pWYHrf6R=OkqUQ)Fm9~OFVq`H*4viUn%7j$FP_D#%sDa%x4O)_S*1L4 ztL8pYNo=!as!oHY!HJ6W5&Kr^K(5dw&D_BFmI?6mWT!rBoWM<$$(MwkM`k6_hdq_c zg?CXkl<6yvCRXv+N8`XFVCW`dS*~Q(yY_iXOAo&%HrLkrS(zhqXVgB}QZ82G&jAfR zZ}kfc-+IDepL<4<$QrW-AZ}(nyF?wKsQuj!y&lHE+&~w2N@joH-JO=-QF=ya&H5LN z_k)YU^95O@iOmLuV=&2rChn;!G%L0j3Y(W&lvx~`o|vl77Dzn$-e!|wAk;*c%8?H* z5p`4~Cl+$liI}lx_#W3x&#d&ODx4hm#Tx=s;cQZnTE;xxQ*Q%lUe9~p?w(Blp*}AC{r0fmL~5RvCb?Q` zH2PWCQu7|PAz4KiyQ8z{ml559clQJ{wac#**_p1no?vh&scppFGw%Dbr6;HChE@7j z#*bdE0E7PZdmGD~HV|;7lV<5)@dQyj<46;BYsWBgg{V~a5Q+#*j$22yz^`19pJVW| z?jXj$Ol=Ug2JS%a%)@~1d;-<-xytc(@gQR=X%jsn1!W;q|T!`TYRI-7&bf z!jU7!vWeI4G{-u@;DjK@hVZ&~3j2z(Aybh;ywQ>|XEUZ>O_4@tb&Ux9a3NaXrP7v6 zq|;jL8U)&f?M{-YtHwHATwm&0$5G3bwT=uJ1-&~AFEKyc4dq{>c~qTCZ)>R^y~|4r zecc{H&vaF)b>>SIf9w!`hdhPY7;p=?X z^E>|F;xZ_owxfCXNn8fqz$+R-;KNzJU?3wy|u4mmtss>!KZG>O;kryWBR-_*gbQiURqRNq`MW24HouX1kqY)}}l*mAL^R7-Sz0Tn7*;x1P&CU{H4~&^r`fyg zz5}8fF-oRqNzBFSwyx6B4io;Hxs_*4L4$C8CvX#6w{Q&3TG2WEWD~dZtk-_qqY-7e zCZpAx@IMUANsKlQjtx!R-se?y=eNlm(m{;#(kK00S|7tXXso7gL8S&s9Ea>-Z}^>! zCrL#w3vNm_T+FwC59C*<8NzQG0!ErO-7o4vp7OonEXxB+I(zpA;b62wl$;K}Nl-iF zg$|MyS$fRvk;R~x*!DdR0)V2H?PUDeDM?vDL0Llh-m0q0W5Rve4FH58y$1NpnO#ha zomSlM;KcUoUdQyB)RZdYG@b~)VMFK0v{q)dc*Z!8QZh2a0=!DR@EYH#6hmY@rc@%f z!zT;ezH4uClVc*K(Z!uDaO}s4eIlWNH^B}NX6L4yR1c+L{(kvU~O~_T*Q{6QU5EU zviUNXLfvg+C%w793Z}3MXjyD);i;;Btqf93$a(I2ATb&AJ%7&|mHT{)tSfrg zS8}1kW+_kZK^fS(ZzQM+cQ|naM>gU_zmfeV=Zwc6mTdfwfGU%mP<=tbH{7dB_%lR$ z2UVm3GrW{4qM@!RDO^B)3Y=FeB;HxPdhZ_oz7H=tu8|jqXa4VW+O4|e=Y`*??>pGu zOZ$nIIN+HP8dfC~`)uMEcPuE;45s%^zR|_tb~;vs3=?^&h>6BN%IpIz%rjwnp>vh` zg?d*akt=1>C75Qm0emzl@{)o_%0}DV=n7)&FnkGkUK(Mai# z*fQ*Rjm>Mb*gofMt@bMyJpHT zdwbz*{@vWO?|;+DJkeYAU|+%DK6)%S*OsO|uc}JITQy&NN#d@WY%s;fP74b!e8=Je z=*%BP4`>vSRp!TcHdHZ?a&Tgv>jq%Piq(xDP&mqS{=JWs&S1jZsr)`>o4M0nh`)Fjc* z%48_Isv@y7vGHV@Snh=P(b^?kT~mBo;0RM}jcI3mjIFEp9(J$F?9Qo)K~c{ltwzEA z$Lr&8rGY1My>*uv(#Jgoq=dB+FEU`+In;bCU-i*t z%-90Of1yi~NUqInRYoohVEpDZ@Z4ao8HImO%G~h!JkayDq}wRRQqs1U`@#Fk^4v1ukwrGKNA`38M-CnYfo|)v`Z^# zau*$H%G*ZY1kjzHS$|5v((%|HpW$3~X1$MgxLj@qTWvm1yy{}PyWaKEov%0@4y(3e z2%YTAN86n0KHhVR;`J?h?)S3dbzPs1XGZJrJftUItiK5TI$TUaXI*1HXz9_)J2Fz;(0Nv8}DhWS$l!Gp>GH z0kalA&1MBcnXWJ6?lM6g=m4+M7!>OlF>W3;IEI@p0kAk_a~Y51l>ww=TS~t_6=`PQ zMy)^e$3I3zZ4C0X-}P%Zz2!Mf?=WkR>U4gr-~)6UFXAkZxGTm!-a9spivI*wI81a_ zhKxCaCQ8orcJmslsq_qy#fgpPNig<@%L)l)!cd#4drE23nl+Nob(AISBU`Bv7TDtq& zc*kNrX%gMe?<7_z+_hF+J2OHhHs<*~FO+4CJV!Uk@&ZWVIe-(-5vqRYr#>cC0*KQl z7xE=?E!va`qbH+Jr_60(Hv7>Syjm61xBjODu~F?7Z?*Xp69k2{yX%Qvi>=Iru4?KT4W< zG2Bi2$3ykv_&7Y)A&W;orj`Uk@AEEv^f}<$8(79}e8a^DWTAiNdO2zE;la<_>W4DR z;UqO}2hW`z1iClT`m%@c;6=PjgKOHI8CVx8T=8K8_wfzxYglg^lC`-FH5Xwc;dezB z+SV$@PE)VplbOHX^#zIUO3@Av2deD63?XQan_`&rc8<$bl8^A~zgiEE(xGV9fsWC~ zoe?Nf0T<6cMN&U&^ewmo_Qhzj!m3Nwke3u%DxF={hA7#%v@pwWXA8N#s?lQ83IL(; z*4u2j;q%IrWoyd*Vcg38P8oaEU}pJp^U0CP(V~!(&5!81`ki14 zHykqy(9Ia-TnsH#sQ!e7_E>_2d}4fFOd)*nOgE842x|^O@m~FZ9vOB8Sz4`UoQ)5x zI2tSe_{^|afUO*@GB^G;uvEwqSaWXNR;=y-=_oKeP@C9K7$&ORn`5GQXCmA7x^FpV zasj~ZA8VcQx^Mf3bU#qBc}D<~ghvN~@;#?NCS8TA5$8`M-&_a8T1u~8b}Hf!fuvNc zyq^}y-Z}H*j?%ImueV0=@6_EEfX9iCn()fW$ZC4P?>u2EbA^?wC_?|Ov|m!i`HQRhGAjp@PUI@&%j)ueU6p1%^Q&_$WuLrP<-|kn zD%vNe;Tn{Li#~?_8+TB{*Fpm6__#Jn$gvU50?~xr!)uxiK@fs|w(b-Ja|m3^nY!A1 zhB8djOBXqLjPACV4i}0v$kB8Cy(q>_(sUi6?1fKV-s@#A_A)v?)fH2|m-L7>MURUQ z%g#$I%$H4e(j-feRli*r0AxajGvL#G-MGjG z4D6!g(A)tO!k^ewnD|wM!*_q~^e76M$pjX@(e9bn1Tfv+cr|7|Ems$gvGa1KN*KuY z^K642_D8JLODqAz}e5=C=nb;~1 zTb8}SbAFg6)m8TA{ixo^aUtM%uPYl3mk6u1Pza~R_&K;dro34$^z04sOZ)-x?MIY4 zlqfzl0>+PNFD8H09Z-*-8^MS`UOlf}#Mkn-$d77BKOk`Lb7Tsp9qh69x#Rg3gKd-k z_eZuj{n{?;leGt8Ct@f0%_e^LWAIb3th%2}MQo^mk7TLGKN>br5t zjM`-nu;D)mMn5PaB!i zrS*B8iE>}Pqpvl9cWw$?{>3uJ6;95d%uSag)(CLF?YI>57Vz%TprOYz2A8{267LE2mVC{Pz7+Y zyi$+2m>%4-zp~clFIxd^3~k73I%Qdqa1VKwKjS^0y&>1%f~mpHe2hP6UyUE6`@e|| zeLxt`hG#*QWJ4@;qWpviu?89XfWNx-yrXHwA(%$AJ>p*ly%fnY+l_}t2LH8EcHbdJ;5r{Tq{~7swxPoQIeAa z!UYxRw=g|*qF486?0uN67}6m>l3=co$(e*K6TxfYVQxTGwmI{6L(=868?db;xh zp-JO`mBM!r&e6i_6nm)XbXk1Tx%^TKW0-jqqp>>FT`V`aiaNyTTZtO72C13!H}eLw z!KOvZe3N0@a(!kKQMF#&&pU4yoShe)QQ3UGtdc^sxSoUJBnQIto9uQJh7_EX z=5`0(%oCpXL8q5hFW&71NMQ8wZ0!<4_mRzGu$&u$f zCz%2`>QT(6(sN7f>IAf@qPJ3)@Cu_X44*Z~fh%lH|M{vaGFxMOrFWaRpMTHs1On~H z!}$kts87DIhVK-~Qeb7IJpN#gCqfkR!t8}-~xGNHe z?l_s?=PR8*k$_j5_t-zoHi;T+ek|1zu)3cLnWJ;(D4)6?gM%Hdyxd4Xf42?FH+flo z0b|#5sXVeGMU97AS!1uCY`iZ0sInbsOw0H|m66)fuIty9QN)s9 zCGa!r{n~GRrnAEiZ1ppIR@32-oTjp)BK>$Y5sOt)Vuj+$^EQAOc7NswI%kj6Odv#t z!hNzO{q@ZcW)HMyU!v6FxmCyeb%;iKPvb^bq67?D7kh~(&V=SK7%E8_Ptz~{+BcV8 zafnJ{VohilaGT<_VtQH%4a9qCN72`UP-%Hef_rOH6u!tiU*-2oA&^|aS_AA8D9MbU zB1)&xb17P<(SGYj9oSM`Q^iRp#9>u~H~(fSQpPFwsVIf8=8qW(Z1H|sD#HSFIr-U- zi#p1Hpdq4q*D&3#Osmdy(ae9(c z$N^bHSR!+#;4Z?~x0qZye(HuPN6mkl^=`>$y?Cn(z5W>!<`gkj_C@X>*bS(!O8rmUIqK)tcTF+{?vCusRi2qm z(W!{D?vJZpR5f!@a1nFgAJq4#8fK?{Fq!o}_2lBe85gTa&Z*2mV@RhK`jDo?Cn3n@ zYCBm8U{Jjlqt}T4q7k?VJ+a2X2gHT3%09g|^~BoBc0s_C((?4vgwGHc4aW8pAm)Bs zhH8}hb2SJ0nZz?ANo3$BH_Ce+aOg-hC-n^LkU%qm1$G%sd zW%6CbBAXe4^`eDy<^bXGv*dy>W5M5`@@KkLx`|q{z4fR&8o`Ssa|J~lnk0RBj{c%4 zs)23!7_i2!A0WesNIv@OQL#j(h`u2ZcgW^5{b9!3XfMJK@YTI?d7>=)wk8KoKtt5P zgGI3@uPDXw_x=tutC`51s*mFJ4=!wRXiPp0}xo}%pb9@plD+%l)lr*^?;dQ44$8gpA5c1_Fe5I8`JnpA~; zx(>1gD-_tilj6L(eu!zcm;M>C#-4q8dRti?w>#<7WovYqi8o)NvJRpAmX>>WhQLX;|Bf4D#L3-^|nClU}z|dVjVcj2N`>}i06^V9lt|XW<0&MIt#@7>^ z){t|WYr>D{Y}2@?xj1*tp99Ti(qn8bGEB*RuA4>#-UYYnb#3h1+Pav6M}WIhaz0fN znnyZ~M&%OyE!+ijz4&whi+yiN+Pj`H>;!`C?`f!uCese0aF##N=f0JRZJ@M%@*3Vv3oWnK5Q& zX2;CT6vxcW%*@Qp%xuTZ%zQgDJF~Oj&VKK{(v_~REVb;@-KVPlr(D&RpY!}C<5ux= z^(V4S1qp)srr;h>QD2DfB7j&goD{`vQ=S<4mJ>ct^S5kPfM25df?Om}P7XX_0^Cl4 zSd(XJfsq)?9y|gfzOIq{BJ2?|bml~OzQco#Q05&1q>$aU?%-L&+u%di6Q-yH^T3&Y zbf(hyvBv%|{l)K`d|{InMc`R`8@RI{MI_dJCO@LKAn(n9dMtnCa!~1l@oS>~@knV& z@a`jBrR0OnOMdYk>AU8KyveZ(dn;2MlB&E%baxPzEPTQ(5VgSLNmC7$M=1$kN9?|? z8O<;l>)9SzmdE~3XHYYe%h@9a6^5axBdz?{c$DGph2h3^EbY%SOGAp6-~s8lPB7{8PH&T@|w| z7ahAD!OStmx;=T!?|ben7T0|(K$(e}kvHP1w<+KBZ)**N+;Q?Bu}5tniAhtt%3`L5 z!x>cA4z<8%lq8ymPGrexw~^neM+3dKje2=D(JrwV31D+@^e(9$sfcgIafVH5Jm-;sY_U|g~cZP>s2BWlNMOSP}QUiBw9wU;7k zM{A3<&%PquSMb31qG;I%@%V)asB71<>fXS_XcH6UlAPlO+{QS?^z_HRmZM!pxrz0* zoZAFx`)_jvi1%`wbsJp-4?T-SFvV=|O!BydV$MrplLQ#VXyXSE#pLWvGP#6~+Iya@ z{y>7$^m~4}Uvt4Z?l|xDfYzjN#*z5;QmDr1*eby3IZKA^eR@FG;d}v4`GWogzvFdYnb@2Q-t91a|=OVR*lh?GPI=aC(!Mud{@3_BcApaL@yN8DFm_FVcEOQ+k}8Glbm_;U4nwL@_bZ(SMK}B(6$>nq{nt z!&;DeZiG5-xA$$md3?1wZ?|n0`g!NkwZ&cwcYlttdf&xe-})m$AS}EL6ZeuQ%96QEpX$~sitaD&{Yx%+wyVh44xTnuscWpb>fQ&X#<{T|vedZuUA z%^zVgx2Iv_jJt1F+`~im{6`R#CWRY7>-qKkM|%(|)rJd6>>*C}`MAebQ1N=~fGQF^ z%u`^YoRB~Z6+Wc9Ky4xc{y=SkaNV*C zyWe;RE#%e9TeV8P-UhLB#=UzyhX>7OLDaW-ZQWZBOQpMx1b=eApL?XJ0NhHd)kcE8 zR_O*jdndqi?VVoB;1RsmMB&Jf=spCom}7h%bI@wN?v+mM$$d^$-m=X(mL3tO9KEx} z0xQ1BZhMLCBo-nC>V)of1sR>CZHfIxrm0gt`6p3jQ_+ z|B^%Sa034_2w#fb=Y`RC)bn5!K=-ca&Xh*08fCMpK5 z-`JG(de^4jwc~?rVu2(J&lN&M^`T9&eEhNPi0qB%_)MvzPyv}`M8y*hO;2$$f_tkr zOB3dvjjTsSN`}F8^KOghQ)+w+Cq**dk9~#WVTx>^YWtuz2(_yTDM7`<%W;4;v1J|Knfif^s;VrJOXcS0VNd``e4Tk&AH%Y% z6UmbcpVe&25ndY=l>Y^t1*&jsN(5U(L+VK2!hoU?hCJ8U)#cl~fuO?c>n9w>1B1Vr zGry+6KjsX;)brfO#aaK2ChIpp&|if?^nVow{j&0^-GuqkS_tmP7Evn&lEktpTvs$&x4H2 ztN>FJE;AhiHOue9D}Y?6Du{qDg}5JAj)E9{6h-$ds9qo%+z#jzgePaaRH~N z{@s7|M?aWZXaUwIfEVkRIf{Xr^_QRO*Mh~u2+()^-iKeu8Gc!>0FJ1CYPf!#fB|5j z`sL{Q$GDhj|8!RUGCJfIi!ziYUdSm^;h_*H&-HhOBd-y~ds1^{9$ zKxD)(DHc5|ppd^h%z(?v3Mks|6=z}teEW~$FaS#SYiz&dUcafu01Yq!F6u827y~XN zpe<&A-wPl_V`QNJLq5j9$OP!&Z@MlfmOuLiP>WFmIs|whE(-$-H3Kc+>I0-|>2I>wnf@19>;Nkx^S^97W3F!Q3Uf^dYHsH)6Z+>a z#MdHs=_f9P3^5Y)F(g^~{PYmsks#o#zyidCoV{Ovbdm)#$w>6!`uz}*fij+~pU;U2 zO#hA@a&LNUVpb;-aZAqS8l3KGdRF&p;#MqS+g@kX-7}y5HvYJOwq#aO2XL?~+^cOg zAo#-bsntSFi5#rVa$GnLGIg_`vktS4l z!7^#G{xoZG>?lgp4o$!bNNLx^n7_MwzuT1L_)HG*q0|r#nF7{ zKu=}^>h^4QuU*TRI|=UADHD5Rwngu|Y8l6O8587LKX3H~*wI5CcY@g#c)6F4vieoz zAW+$OP`Lp=F1=*rpz+CGj{1~$r49V9(Lj?F5QIMI4wQsdC@B!H3N=hY;Xr%vtmN@# z#9)5?fENl)>nK(Q7i3I=CL4_3rMa2EYFYPcO*fV-6!rjbAIVXKT?-Y_2!}3Rtu@YV zTyW}GQ7{wI79Cy!bB8KIzm^$&uSYUX^GsM0FpB*C@jxo6x$f3w3~hdv`(pG@(f?7T$=0b!BkY>Li; zZ(#EcTE~aaf7c@<0`dJWs_IObSDW-gK!zwxSWIV(e}x2cGm0#7~g5#Q7b5=Wf&CvrQ%epq#Ed!UCIjMSW z@T1qQ85g&gli6{I`aIXdJi1l7=KIg;ri!=gCn?Mn4b1$V_9Qkd9ykoNK^HAZv`6H${)eU_4%%2KHUzu+HzBuqZLa5({H>1i`; zMmn%@@1V~P?--w~E+*UsLrAfvb;81_C=`)=J#vU@EttWJJZf<%jFJM$I@(EAXR#7$ zOQ}n9?4$dEf|CNjsnXKi>N9jUw{RA|q2Euhx$J(j&8q$gtmr2f1}lUqsT(t{q=O@g zoH*Q_Lnetf_T-U6<|Sp2!@DEGm3x8Wt1z$gVt{bej|ya2G8%^lulV|OWp9%)L?MB^ zT4YiuiDl<@+Jcp|eMgm!kboO&4_5U0Aar7+nYc_{3+~ynW~FL!o_RfMt_ZRHxO(bY z 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 literal 0 HcmV?d00001 diff --git a/test/readwrite4.jl b/test/readwrite4.jl index 16b8f47..c751125 100644 --- a/test/readwrite4.jl +++ b/test/readwrite4.jl @@ -3,7 +3,7 @@ using MAT, Test function check(filename, result) matfile = matopen(filename) for (k, v) in result - @test haskey(matfile, k) + @test exists(matfile, k) got = read(matfile, k) if !isequal(got, v) || (typeof(got) != typeof(v) && (!isa(got, String) || !(isa(v, String)))) close(matfile) @@ -20,7 +20,7 @@ function check(filename, result) """) end end - @test union!(Set(), keys(matfile)) == union!(Set(), keys(result)) + @test union!(Set(), names(matfile)) == union!(Set(), keys(result)) close(matfile) mat = matread(filename) @@ -46,7 +46,7 @@ cd(dirname(@__FILE__)) for filename in readdir("v4") #println("testing $filename") d = matread("v4/$filename") - matwrite("v4/tmp.mat", d; version="v4") + matwrite4("v4/tmp.mat", d) check("v4/tmp.mat", d) rm("v4/tmp.mat") end diff --git a/test/runtests.jl b/test/runtests.jl index 159a125..de33258 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,14 @@ + +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + + using SparseArrays, LinearAlgebra include("read.jl") include("readwrite4.jl") include("write.jl") + + + diff --git a/test/runtests_modelica_manual.jl b/test/runtests_modelica_manual.jl new file mode 100644 index 0000000..d3ee86c --- /dev/null +++ b/test/runtests_modelica_manual.jl @@ -0,0 +1,126 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +# include("../src/MAT.jl") +# include("../src/MAT_v4_Modelica.jl") + +mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + + +@testset "manual" begin + rawfid = open(mat1s, "r") + + # This reader is collected from two references: + # (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) + # Matlab_MATFileFormatV4.pdf from https://www.eiscat.se/wp-content/uploads/2016/03/Version-4-MAT-File-Format.pdf + # OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf from https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/technical_details.html + + # Start by reading the header, following Matlab: + # Each matrix starts with a fixed-length 20-byte header that contains information describing certain attributes of the Matrix. + # The 20-byte header consists of five long (4-byte) integers: + @show type, nrows, ncols, imagf, namelen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + + # M indicates the numeric format of binary numbers on the machine that wrote the file. + # Use this table to determine the number to use for your machine: + # 0 IEEE Little Endian (PC, 386, 486, DEC Risc) + # 1 IEEE Big Endian (Macintosh, SPARC, Apollo,SGI, HP 9000/300, other Motorola) + # 2 VAX D-float + # 3 VAX G-float + # 4 Cray + @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 + @show numberFormat(M) + + # O is always 0 (zero) and is reserved for future use. + # @show O + + # P indicates which format the data is stored: + # 0 double-precision (64-bit) floating point numbers + # 1 single-precision (32-bit) floating point numbers + # 2 32-bit signed integers + # 3 16-bit signed integers + # 4 16-bit unsigned integers + # 5 8-bit unsigned integers + @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 + @show dataFormat(P) + @test dataFormat(P) == uint8 + + # T indicates matrix type: 1 = text matrix + @enum matrixFormat numericFull=0 text=1 spars=2 + @show matrixFormat(T) + + # nrows The row dimension contains an integer with the number of rows in the matrix. + @show nrows + # ncols The column dimension contains an integer with the number of columns in the matrix. + @show ncols + # imagf The imaginary flag is an integer whose value is either 0 or 1. If 1, then the matrix has an imaginary part. If 0, there is only real data. + @show imagf + # namelen The name length contains an integer with 1 plus the length of the matrix name. + @show namelen + + # Immediately following the fixed length header is the data whose length is dependent on the variables in the fixed length header: + # name The matrix name consists of namlen ASCII bytes, the last one of which must be a null character (’\0’ ). + @show nameuint = read!(rawfid, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + # pop!(nameuint) #trim \0 + # @show name = String(nameuint) + @show name = replace(String(nameuint), '\0'=>"") + + # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. + # @show realint = read!(rawfid, Vector{UInt8}(undef, 1*nrows*ncols)) # UInt8 from P is 8 bytes long + # @show realint = read!(rawfid, Vector{UInt16}(undef, 8*nrows*ncols)) # UInt8 from P is 8 bytes long + # @show realint = read(rawfid, 32) # UInt8 from P is 8 bytes long + @show realint = read!(rawfid, Matrix{UInt8}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + + # imag Imaginary part of the matrix, if any. If the imaginary flag imagf is nonzero, the imaginary part of a matrix is placed here. It is stored in the same manner as the real data. + if imagf==1 + @show imagint = read!(rawfid, Vector{UInt8}(undef, nrows*ncols)) # read the full namelen to make the pointer ready to read the data + end + + # The variables stored in the MAT-file are (in the order required by OpenModelica): + # Aclass + # • Aclass(1,:) is always Atrajectory + # • Aclass(2,:) is 1.1 in OpenModelica + # • Aclass(3,:) is empty + # • Aclass(4,:) is either binTrans or binNormal + + Aclass1 = replace(String(realint[1,:]), '\0'=>"") + Aclass2 = replace(String(realint[2,:]), '\0'=>"") + Aclass3 = replace(String(realint[3,:]), '\0'=>"") + Aclass4 = replace(String(realint[4,:]), '\0'=>"") + @test Aclass1 == "Atrajectory" + @test Aclass2 == "1.1" + @test isempty(Aclass3) + @test Aclass4 == "binNormal" || Aclass4 == "binTrans" + + #now to read the next matrix header... + # name: + # Is an n x m character (int8) matrix, where n is the number of variables stored in the result-file (including time). m is the length of the longest variable. OpenModelica stores the trailing part of the name as NIL bytes (0) whereas other tools use spaces for the trailing part. + @show type, nrows, ncols, imagf, namelen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + T, P, O, M = digits(type; pad=4) + @show numberFormat(M) + @show matrixFormat(T) + @show dataFormat(P) + + @show nrows + @show ncols + @show imagf + @show namelen + nameuint = read!(rawfid, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + @show name = replace(String(nameuint), '\0'=>"") + + realint = read!(rawfid, Matrix{UInt8}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + @show varname1 = replace(String(realint[:,1]), '\0'=>"") # note :,1 implicit transpose + @show varname2 = replace(String(realint[:,2]), '\0'=>"") # note :,1 implicit transpose + @show varname3 = replace(String(realint[:,3]), '\0'=>"") # note :,1 implicit transpose + @show varname4 = replace(String(realint[:,4]), '\0'=>"") # note :,1 implicit transpose + @show varname5 = replace(String(realint[:,5]), '\0'=>"") # note :,1 implicit transpose + + close(rawfid) +end + + diff --git a/test/runtests_modelica_readMatrix.jl b/test/runtests_modelica_readMatrix.jl new file mode 100644 index 0000000..95f652a --- /dev/null +++ b/test/runtests_modelica_readMatrix.jl @@ -0,0 +1,205 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +# include("../src/MAT.jl") +# include("../src/MAT_v4_Modelica.jl") + +mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + +struct MATrix + name::String + # data::T where T<:AbstractMatrix + data::Any + # type +end + +@enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 +@enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 +@enum matrixFormat numericFull=0 text=1 spars=2 + +""" +Read a single data matrix from the opened MAT file +""" +function readMATMatrix(ios::IOStream) + # The 20-byte header consists of five long (4-byte) integers: + type, nrows, ncols, imagf, namelen = read!(ios, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + + nameuint = read!(ios, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + + # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. + realint = [] + if dataFormat(P) == uint8 + realint = read!(ios, Matrix{UInt8}(undef, nrows,ncols)) + elseif dataFormat(P) == uint16 + realint = read!(ios, Matrix{UInt16}(undef, nrows,ncols)) + elseif dataFormat(P) == int32 + realint = read!(ios, Matrix{Int32}(undef, nrows,ncols)) + elseif dataFormat(P) == single + realint = read!(ios, Matrix{Float32}(undef, nrows,ncols)) + elseif dataFormat(P) == double + realint = read!(ios, Matrix{Float64}(undef, nrows,ncols)) + else + error("Unknown data format for P=$P") + end + + # imag Imaginary part of the matrix, if any. If the imaginary flag imagf is nonzero, the imaginary part of a matrix is placed here. It is stored in the same manner as the real data. + if imagf==1 + error("imaginary not implemented") + # if dataFormat(P) + # @show imagint = read!(ios, Vector{UInt8}(undef, nrows*ncols)) # read the full namelen to make the pointer ready to read the data + end + + @show matrixName + + return MATrix(matrixName, realint) +end + +""" + # The variables stored in the MAT-file are (in the order required by OpenModelica): + # Aclass + # • Aclass(1,:) is always Atrajectory + # • Aclass(2,:) is 1.1 in OpenModelica + # • Aclass(3,:) is empty + # • Aclass(4,:) is either binTrans or binNormal + + +""" +function parseAclass(mat::MATrix) + # @show mat + # Aclass1 = replace(String(mat.data[1,:]), '\0'=>"") + # Aclass2 = replace(String(data[2,:]), '\0'=>"") + # Aclass3 = replace(String(data[3,:]), '\0'=>"") + # Aclass4 = replace(String(data[4,:]), '\0'=>"") + Aclass = [ replace(String(mat.data[1,:]), '\0'=>""), + replace(String(mat.data[2,:]), '\0'=>""), + replace(String(mat.data[3,:]), '\0'=>""), + replace(String(mat.data[4,:]), '\0'=>"") ] + return Aclass +end + + +""" +name +Is an n x m character (int8) matrix, where n is the number of variables stored in the result-file (including time). +m is the length of the longest variable. +OpenModelica stores the trailing part of the name as NIL bytes (0) whereas other tools use spaces for the trailing part. +""" +function parseVariableNames(mat::MATrix) + varNames = [] + m,n = size(mat.data) + for i in 1:n + push!(varNames, replace(String(mat.data[:,i]), '\0'=>"")) + end + return varNames +end + +""" +Find the index of the given variable, returning -1 if not found. +""" +function getVariableIndex(variableNames, name) + vecAll = findall( x->x==name, variableNames) + n = length(vecAll) + + if isempty(vecAll) == true + return -1 + else + if n>1 + error("Found $n instances of variable [$name], but variables should be unique.") + end + return vecAll[1] + end +end + + +""" +description +Is an n x m character (int8) matrix containing the comment-string corresponding to the variable in the name matrix +""" +function parseVariableDescriptions(mat::MATrix) + varDesc = [] + m,n = size(mat.data) + for i in 1:n + push!(varDesc, replace(String(mat.data[:,i]), '\0'=>"")) + end + return varDesc +end + +""" +dataInfo provides indicies to access variable data + +dataInfo +Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). +• dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). +• dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. +• dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. +• dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. +""" +function parseDataInfo(mat::MATrix, varIndex::Int) + # @show mat.data[:,varIndex] + return Dict("locatedInData"=>mat.data[1,varIndex], "indexInData"=>mat.data[2,varIndex], "isInterpolated"=>mat.data[3,varIndex], "isWithinTimeRange"=>mat.data[4,varIndex] ) +end + +""" +data_1 +If it is an n x 1 matrix it contains the values of parameters. If it is an n x 2 matrix, the first and second column +signify start and stop-values. +""" +function parseData1(mat::MATrix, varIndex::Int) + @show size(mat.data) + @show mat.data[varIndex,:] +end + + +# using Profile + +@testset "matrix reader" begin + matio = open(mat1s, "r") + seekstart(matio) + ret = readMATMatrix(matio) + Aclass = parseAclass(ret) + @test Aclass[1] == "Atrajectory" + @test Aclass[2] == "1.1" + @test isempty(Aclass[3]) == true + @test Aclass[4] == "binTrans" || Aclass[4] == "binNormal" + + ret = readMATMatrix(matio) # name + variableNames = parseVariableNames(ret) + @test variableNames[1] == "time" + @test variableNames[2490] == "world.z_label.color[3]" + @test getVariableIndex(variableNames, "time") == 1 + + + ret = readMATMatrix(matio) # description + variableDescriptions = parseVariableDescriptions(ret) + @test variableDescriptions[2490] == "Color of cylinders" + + # @allocated readMATMatrix(matio) # dataInfo + ret = readMATMatrix(matio) # dataInfo + @show dataInfo = parseDataInfo(ret, 1) + + ret = readMATMatrix(matio) # data_1 + + + # itime = getVariableIndex(variableNames, "time") + # # ditime = parse + # parseData1(ret, itime) + + # parseData1(ret, iphi) + # # parseData1(ret, ihfaf1) + + # iphi = getVariableIndex(variableNames, "revolute.phi") + # ihfaf1 = getVariableIndex(variableNames, "head.frame_a.f[1]") + + ret = readMATMatrix(matio) # data_2 + close(matio) + + @test true + +end From f2a602ca3417026988e6f30acab0a75b11b51d3b Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Wed, 8 Feb 2023 16:53:55 -0600 Subject: [PATCH 32/91] restore readMatrix dev file --- test/runtests_modelica_readMatrix.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/runtests_modelica_readMatrix.jl b/test/runtests_modelica_readMatrix.jl index 95f652a..d0fb41c 100644 --- a/test/runtests_modelica_readMatrix.jl +++ b/test/runtests_modelica_readMatrix.jl @@ -24,8 +24,9 @@ end Read a single data matrix from the opened MAT file """ function readMATMatrix(ios::IOStream) + @show startP = position(ios) # The 20-byte header consists of five long (4-byte) integers: - type, nrows, ncols, imagf, namelen = read!(ios, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + type, nrows, ncols, imagf, namelen = read!(ios, Vector{Int32}(undef, 5)) # 32bits=4byte, * 5 qty #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... T, P, O, M = digits(type; pad=4) @@ -35,6 +36,7 @@ function readMATMatrix(ios::IOStream) # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. realint = [] + @show dataFormat(P) if dataFormat(P) == uint8 realint = read!(ios, Matrix{UInt8}(undef, nrows,ncols)) elseif dataFormat(P) == uint16 @@ -57,6 +59,7 @@ function readMATMatrix(ios::IOStream) end @show matrixName + @show endP = position(ios) return MATrix(matrixName, realint) end @@ -157,8 +160,6 @@ function parseData1(mat::MATrix, varIndex::Int) end -# using Profile - @testset "matrix reader" begin matio = open(mat1s, "r") seekstart(matio) @@ -176,6 +177,7 @@ end @test getVariableIndex(variableNames, "time") == 1 + @show position(matio) ret = readMATMatrix(matio) # description variableDescriptions = parseVariableDescriptions(ret) @test variableDescriptions[2490] == "Color of cylinders" From b328f3828ab2dd5138962e84cb8ba5ae0506f649 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Wed, 8 Feb 2023 16:54:40 -0600 Subject: [PATCH 33/91] temp commit, need to get a smaller reference model --- test/runtests_modelica.jl | 479 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100644 test/runtests_modelica.jl diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl new file mode 100644 index 0000000..909ebf7 --- /dev/null +++ b/test/runtests_modelica.jl @@ -0,0 +1,479 @@ +cd(joinpath(@__DIR__,"..")) +import Pkg +Pkg.activate(joinpath(@__DIR__, "..")) + +# test only MAT_v4 +using Test +# include("../src/MAT.jl") +# include("../src/MAT_v4_Modelica.jl") + +const mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + + + +# The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices +# The first matrix, Aclass is narrowly defined + +function isLittleEndian(dtype) :: Bool + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(dtype; pad=4) + # @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 + return M == 0 +end + +@testset "isLittleEndian" begin + @test isLittleEndian(0) == true + @test isLittleEndian(1000) == false + @test isLittleEndian(2000) == false + @test isLittleEndian(3000) == false +end + +function dataFormat(type) :: DataType + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + # @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 + if P == 0 + return Float64 + end + if P == 1 + return Float32 + end + if P == 2 + return Int32 + end + if P == 3 + return Int16 + end + if P == 4 + return UInt16 + end + if P == 5 + return UInt8 + end +end +@testset "dataFormat" begin + @test dataFormat(0000) <: Float64 + @test dataFormat(0010) <: Float32 + @test dataFormat(0020) <: Int32 + @test dataFormat(0030) <: Int16 + @test dataFormat(0040) <: UInt16 + @test dataFormat(0050) <: UInt8 +end + +function typeBytes(type::T)::Int where T<:DataType + if type == Float64 + return 8 + end + if type == Float32 + return 4 + end + if type == Int32 + return 4 + end + if type == Int16 + return 2 + end + if type == UInt16 + return 2 + end + if type == UInt8 + return 1 + end +end +@testset "typeBits" begin + @test typeBits(Int32) == 4 +end + +struct Aclass + filepath::String + isTranspose::Bool + positionStart::Int + positionEnd::Int +end + +""" +Reads the Aclass matrix, returing if the data is stored binNormal or binTranspose +""" +function readAclass( filepath::String ) + open(filepath, "r", lock=false) do matio + seekstart(matio) # always start from the start, don't assume the position + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # 32bit=4byte * 5 qty + catch e + error("caught error $e while reading $filepath") + end + + if !isLittleEndian(dtype) + error("Only the little-endian encoding is implemented, cannot read $filepath") + end + + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + name = replace(String(nameuint), '\0'=>"") + if name != "Aclass" + error("First matrix must be named Aclass, is instead [$name]. This likely means that [$filepath] is not a Modelica MAT v4 file.") + end + + # real: the Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. + fmt = dataFormat(dtype) # read the format type before reading + realint = read!(matio, Matrix{UInt8}(undef, nrows,ncols)) + + Aclass1 = replace(String(realint[1,:]), '\0'=>"") + Aclass2 = replace(String(realint[2,:]), '\0'=>"") + Aclass3 = replace(String(realint[3,:]), '\0'=>"") + Aclass4 = replace(String(realint[4,:]), '\0'=>"") + if Aclass1 == "Atrajectory" && Aclass2 == "1.1" && isempty(Aclass3) && Aclass4 == "binNormal" || Aclass4 == "binTrans" + return Aclass( filepath, Aclass4 == "binTranspose", startP, position(matio) ) + end + end #open +end + +@testset "Aclass" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + @test ac.positionStart == 0 + @test ac.positionEnd == 71 +end + + +##### the NAME matrix######################################################################################################################## +struct VariableNames + # names::Vector{T}(undef,undef) where T<:AbstractString + names::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableNames(ac::Aclass) + open(ac.filepath, "r", lock=false) do matio + seek(matio, ac.positionEnd) #skip over Aclass + startP = position(matio) + + #read the matrix header + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "name" + error("trying to read matrix [name] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realint = [] + try + realint = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + #pull the names out of the matrix + vnames = [] + for i in 1:ncols + push!(vnames, replace(String(realint[:,i]), '\0'=>"")) # note :,1 = implicit transpose + end + + return VariableNames(vnames, startP, position(matio)) + + end #open +end + +@testset "readVariableNames" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + # @show vn + @test length(vn.names) == 2490 + @test vn.names[1] == "time" + @test vn.names[3] == "revolute.w" + @test vn.names[30] == "der(alignElastoBacklash.frame_a.r_0[1])" + @test vn.names[2490] == "world.z_label.color[3]" + @test vn.positionStart == 71 + @test vn.positionEnd == 117126 +end + +function getVariableIndex(vn::VariableNames, name::String) + vecAll = findall( x->x==name, vn.names) + n = length(vecAll) + + if isempty(vecAll) == true + return -1 + else + if n>1 + error("Found $n instances of variable [$name], but variables should be unique.") + end + return vecAll[1] + end +end + +@testset "getVariableIndex" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + @test getVariableIndex(vn, vn.names[3]) == 3 + @test getVariableIndex(vn, vn.names[30]) == 30 +end + +struct VariableDescriptions + names::Vector{String} + descriptions::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableDescriptions(ac::Aclass, vn::VariableNames) + open(ac.filepath, "r", lock=false) do matio + seek(matio, vn.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "description" + error("trying to read matrix [description] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + vdesc = [] + for i in 1:ncols + push!(vdesc, replace(String(realread[:,i]), '\0'=>"")) # note :,1 = implicit transpose + end + + return VariableDescriptions(vn.names, vdesc, startP, position(matio)) + end #open +end + +@testset "readVariableDescriptions" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + @test length(vd.descriptions) == 2490 + @test vd.descriptions[1] == "Simulation time [s]" + @test vd.descriptions[3] == "First derivative of angle phi (relative angular velocity) [rad/s]" + @test vd.descriptions[30] == "Position vector from world frame to the connector frame origin, resolved in world frame" + @test vd.descriptions[2490] == "Color of cylinders" +end + +struct DataInfo + info + positionStart::Int + positionEnd::Int +end + +""" +dataInfo provides indicies to access variable data + +dataInfo +Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). + dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). + dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. + dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. + dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. +""" +function readDataInfo(ac::Aclass, vd::VariableDescriptions) + open(ac.filepath, "r", lock=false) do matio + seek(matio, vd.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "dataInfo" + error("trying to read variable [dataInfo] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + dinfo = [] + for i in 1:ncols + push!(dinfo, Dict("name"=>vd.names[i], "description"=>vd.descriptions[i], "locatedInData"=>realread[1,i], "indexInData"=>realread[2,i], "isInterpolated"=>realread[3,i], "isWithinTimeRange"=>realread[4,i] ) ) + end + return DataInfo( dinfo, startP, position(matio)) + end #open +end + + +@testset "readDataInfo" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + # @show di.info[3] + @test di.info[1]["isWithinTimeRange"] == -1 + @test di.info[3]["locatedInData"] == 2 + @test di.info[30]["isInterpolated"] == 0 + @test di.info[2490]["isWithinTimeRange"] == 0 +end + + +""" +read one variable from the thing +to read a variable, we need its index, then to look up whether it is in data_1 or data_2 +""" +function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) + open(ac.filepath, "r", lock=false) do matio + seek(matio, di.positionEnd) #this follows the VariableNames matrix + # @show startP = position(matio) + + println("\ndata_1:") + @show data1HeaderStart = mark(matio) + # read data1 header: + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + @show dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + @show data1MatrixName = mark(matio) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + @show matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "data_1" + error("trying to read matrix [data_1] but read $matrixName") + end + + @show data1MatrixStart = mark(matio) + #read the matrix data_1 + @show fmt1 = dataFormat(dtype) # read the format type before reading + # realread = [] + try + # realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + @show position(matio) + @show toskip = nrows*ncols*typeBytes(fmt1) #817*2*8 = 13072, 488197+20+7+13072 = 501296 + skip(matio, toskip ) + @show position(matio) + catch e + error("caught error $e while reading $ac.filepath") + end + + # read data2 header: + println("\ndata_2:") + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + @show dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + @show data2MatrixName = mark(matio) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + @show matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "data_2" + error("trying to read matrix [data_2] but read $matrixName") + end + + #read the matrix data_2 + @show data2MatrixStart = mark(matio) + @show fmt2 = dataFormat(dtype) # read the format type before reading + + # so, having found both headers and matrix starts, need to calculate the variable's position and length + println("\nlocate variable [$name]:") + @show varInd = getVariableIndex(vn, name) + @show di.info[varInd] + if di.info[varInd]["locatedInData"] == 1 #data_1 + @show pstart = data1MatrixStart + di.info[varInd]["indexInData"] * typeBytes(fmt1) + else #data_2 + @show pstart = data2MatrixStart + di.info[varInd]["indexInData"] * ncols * typeBytes(fmt2) + @show pstop = data2MatrixStart + di.info[varInd]["indexInData"] * ncols * typeBytes(fmt2) *2 + @show pstop-pstart + seek(matio, pstart) + # readreal = read!(matio, Vector{fmt2}(undef, pstop-pstart) ) + # @show readreal[1:100] + + read100 = read!(matio, Vector{fmt2}(undef, 100) ) + + seek(matio, pstart) + readns = [] + for i = 1:100 + append!(readns, read!(matio, Vector{fmt2}(undef,1))) + skip(matio, typeBytes(fmt2)*ncols ) + println( "$i] " , vn.names[i] , ": " , read100[i], " : ", readns[i]) + end + end + + end #open +end + +@testset "readVariable" begin + mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + ac = readAclass(mat1s) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + readVariable(ac, vn, vd, di, "time") + # readVariable(ac, vn, vd, di, "revolute.w") +end + + +; + From 58567b04ce408d41512a4184fa0fb2b5bfa8b5ba Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 10:27:09 -0600 Subject: [PATCH 34/91] transpose read working --- test/Modelica/BouncingBall/BouncingBall.mo | 29 ++++++++++++ .../BouncingBall/BouncingBall_res.csv | 32 +++++++++++++ .../BouncingBall/BouncingBall_res.mat | Bin 0 -> 1761 bytes .../Modelica/FallingBodyBox/FallingBodyBox.mo | 16 +++++++ test/runtests_modelica.jl | 43 +++++++++++------- 5 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 test/Modelica/BouncingBall/BouncingBall.mo create mode 100644 test/Modelica/BouncingBall/BouncingBall_res.csv create mode 100644 test/Modelica/BouncingBall/BouncingBall_res.mat create mode 100644 test/Modelica/FallingBodyBox/FallingBodyBox.mo diff --git a/test/Modelica/BouncingBall/BouncingBall.mo b/test/Modelica/BouncingBall/BouncingBall.mo new file mode 100644 index 0000000..3e5b213 --- /dev/null +++ b/test/Modelica/BouncingBall/BouncingBall.mo @@ -0,0 +1,29 @@ +model BouncingBall + parameter Real eff=0.77 "coefficient of restitution"; + parameter Real grav=9.81 "gravity acceleration"; + Real height(fixed=true, start=111) "height of ball"; + Real vel(fixed=true) "velocity of ball"; + Boolean flying(fixed=true, start=true) "true, if ball is flying"; + Boolean impact; + Real v_new(fixed=true); + Integer foo; + +equation + impact = height <= 0.0; + foo = if impact then 1 else 2; + der(vel) = if flying then -grav else 0; + der(height) = vel; + + when {height <= 0.0 and vel <= 0.0, impact} then + //when {impact} then + v_new = if edge(impact) then -eff*pre(vel) else 0; + flying = v_new > 0; + reinit(vel, v_new); + end when; + + //copy files from C:/Users/BenConrad/AppData/Local/Temp/OpenModelica/OMEdit/BouncingBall + annotation( + experiment(StartTime=0, StopTime=1, Tolerance=1e-3, Interval=0.1) + ); + +end BouncingBall; diff --git a/test/Modelica/BouncingBall/BouncingBall_res.csv b/test/Modelica/BouncingBall/BouncingBall_res.csv new file mode 100644 index 0000000..1864caf --- /dev/null +++ b/test/Modelica/BouncingBall/BouncingBall_res.csv @@ -0,0 +1,32 @@ +name,time,eff,grav,der(height),der(vel),flying,foo,height,impact,v_new,vel,,,, +data,0,1,1,2,2,2,2,2,2,2,2,,,, +,0,0.77,9.81,0,-9.81,1,2,111,0,0,0,,,, +,0.1,0.77,9.81,-0.981,-9.81,1,2,110.9509495,0,0,-0.981,,,time,0 +,0.2,0.77,9.81,-1.962,-9.81,1,2,110.8037994,0,0,-1.962,,,height,111 +,0.3,0.77,9.81,-2.943,-9.81,1,2,110.5585494,0,0,-2.943,,,,0 +,0.4,0.77,9.81,-3.924,-9.81,1,2,110.2151994,0,0,-3.924,,,,0 +,0.5,0.77,9.81,-4.905,-9.81,1,2,109.7737494,0,0,-4.905,,,der(vel),-9.81 +,0.6,0.77,9.81,-5.886,-9.81,1,2,109.2341993,0,0,-5.886,,,,0 +,0.7,0.77,9.81,-6.867,-9.81,1,2,108.5965493,0,0,-6.867,,,foo,2 +,0.8,0.77,9.81,-7.848,-9.81,1,2,107.8607993,0,0,-7.848,,,flying,1 +,0.9,0.77,9.81,-8.829,-9.81,1,2,107.0269493,0,0,-8.829,,,,0 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,time,0.1 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,height,110.9509495 +,,,,,,,,,,,,,,der(height)/vel,-0.981 +,,,,,,,,,,,,,,der(height)/vel,-0.981 +,,,,,,,,,,,,,,der(vel),-9.81 +,,,,,,,,,,,,,,,0 +,,,,,,,,,,,,,,foo,2 +,,,,,,,,,,,,,,flying,1 +,,,,,,,,,,,,,,,0 +,,,,,,,,,,,,,,time,0.2 +,,,,,,,,,,,,,,height,110.8037994 +,,,,,,,,,,,,,,der(height)/vel,-1.962 +,,,,,,,,,,,,,,der(height)/vel,-1.962 +,,,,,,,,,,,,,,der(vel),-9.81 +,,,,,,,,,,,,,,impact/vel,0 +,,,,,,,,,,,,,,foo,2 +,,,,,,,,,,,,,,flying,1 +,,,,,,,,,,,,,,impact/vel,0 +,,,,,,,,,,,,,,,0.3 +,,,,,,,,,,,,,,,110.5585494 diff --git a/test/Modelica/BouncingBall/BouncingBall_res.mat b/test/Modelica/BouncingBall/BouncingBall_res.mat new file mode 100644 index 0000000000000000000000000000000000000000..8e7f76e1823f2de740c26dd2869522b7fbdec7d2 GIT binary patch literal 1761 zcmcgqJ!lj`6dr%_;}51V7$oAYoCJeW%w1t6vq2DS1W^lb(POhWdl|XxUUv5qQz%$O z5wWx|h)pa6EreJY5N-u60>oB4s-pOu;u)+(p6<9eleAgJZD^1gS1$gxx z8BMM|-@F;kCl}LuarW>1t@AA=)|o~@O$-=PSvF*7w6^RYT8pc4VLQT=n}l=(Q_=jgVsyFyBN4FxDtWz#88b z0qb@ufU(BsOTfC53Sg|!5COg57y;|n^Kl$&T7%knnc^VMBzRCgw)Nf2SDu;re)`${ ztf@wq`C8+6G`>8p_m7lISt&Zx`C%z-M(mOR~nQGroy`MN6Q$IDEikp2__xjAA*7KR)r!2ECxmQn2 zoYs$<;0x!o10N6dS?0kTyB6O}^v1+#byR=8@y5c^xt?DI{l7B0Kbh41S$K{3!Tqi) lH|Jgy%zH15C&9ygF>zWy)UWQ#ZzsC17j&Pqt3Lj>&L8j$T&Ms5 literal 0 HcmV?d00001 diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox.mo b/test/Modelica/FallingBodyBox/FallingBodyBox.mo new file mode 100644 index 0000000..6e74314 --- /dev/null +++ b/test/Modelica/FallingBodyBox/FallingBodyBox.mo @@ -0,0 +1,16 @@ +model FallingBodyBox + inner Modelica.Mechanics.MultiBody.World world annotation( + Placement(visible = true, transformation(origin = {-70, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); + Modelica.Mechanics.MultiBody.Parts.BodyBox bodyBox(r = {1, 0, 0}) annotation( + Placement(visible = true, transformation(origin = {10, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); + Modelica.Mechanics.MultiBody.Joints.FreeMotion freeMotion annotation( + Placement(visible = true, transformation(origin = {-30, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); +equation + connect(world.frame_b, freeMotion.frame_a) annotation( + Line(points = {{-60, 10}, {-40, 10}})); + connect(freeMotion.frame_b, bodyBox.frame_a) annotation( + Line(points = {{-20, 10}, {0, 10}}, color = {95, 95, 95})); + +annotation( + uses(Modelica(version = "4.0.0"))); +end FallingBodyBox; diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 909ebf7..37bc920 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -443,35 +443,46 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d if di.info[varInd]["locatedInData"] == 1 #data_1 @show pstart = data1MatrixStart + di.info[varInd]["indexInData"] * typeBytes(fmt1) else #data_2 - @show pstart = data2MatrixStart + di.info[varInd]["indexInData"] * ncols * typeBytes(fmt2) - @show pstop = data2MatrixStart + di.info[varInd]["indexInData"] * ncols * typeBytes(fmt2) *2 - @show pstop-pstart + @show pstart = data2MatrixStart + (di.info[varInd]["indexInData"]-1) * ncols * typeBytes(fmt2) + pstop = pstart + ncols*typeBytes(fmt2) + # @show pstop-pstart seek(matio, pstart) - # readreal = read!(matio, Vector{fmt2}(undef, pstop-pstart) ) - # @show readreal[1:100] - - read100 = read!(matio, Vector{fmt2}(undef, 100) ) + # readreal = read!(matio, Vector{fmt2}(undef, 30) ) + for i = 1:30 + ps = position(matio) + readreal = read!(matio, Vector{fmt2}(undef,1)) + println("$i [$ps] = $(readreal[1])") + end - seek(matio, pstart) + # 'transpose' reading, that is the data is saved time, var1(time), var2(time),... time2, var1(time2),... readns = [] - for i = 1:100 - append!(readns, read!(matio, Vector{fmt2}(undef,1))) - skip(matio, typeBytes(fmt2)*ncols ) - println( "$i] " , vn.names[i] , ": " , read100[i], " : ", readns[i]) + nvar = 9 # == nrows == number of variables listing data2 as their location + bytesPerBar = 8 # typeBytes(fmt2) == Float64 + for ivar = 1:nrows + for ind = 1:ncols + seek(matio, pstart + (ivar-1)*bytesPerBar + ((ind-1)*nvar*bytesPerBar) ) + readns = read!(matio, Vector{fmt2}(undef, 1) ) + println( "$ind] : " , readns[1]) + end end end end #open end +using JSON @testset "readVariable" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) + # mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" + matbb = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = readAclass(matbb) vn = readVariableNames(ac) vd = readVariableDescriptions(ac,vn) di = readDataInfo(ac,vd) - readVariable(ac, vn, vd, di, "time") - # readVariable(ac, vn, vd, di, "revolute.w") + # println(JSON.json(di.info, 2)) #get the data 1/2 info + # readVariable(ac, vn, vd, di, "eff") #data1 + # readVariable(ac, vn, vd, di, "grav") #data1 + readVariable(ac, vn, vd, di, "time") # data0 + # readVariable(ac, vn, vd, di, "height") #data2 end From ee9123634ff160c3f50461018a152361caae3f8f Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 11:46:48 -0600 Subject: [PATCH 35/91] can read all data in BouncingBall --- .../BouncingBall/BouncingBall_res.csv | 31 ++-- test/runtests_modelica.jl | 138 +++++++++++------- test/runtests_modelica_readMatrix.jl | 2 + 3 files changed, 101 insertions(+), 70 deletions(-) diff --git a/test/Modelica/BouncingBall/BouncingBall_res.csv b/test/Modelica/BouncingBall/BouncingBall_res.csv index 1864caf..d4a1a70 100644 --- a/test/Modelica/BouncingBall/BouncingBall_res.csv +++ b/test/Modelica/BouncingBall/BouncingBall_res.csv @@ -1,25 +1,22 @@ -name,time,eff,grav,der(height),der(vel),flying,foo,height,impact,v_new,vel,,,, -data,0,1,1,2,2,2,2,2,2,2,2,,,, -,0,0.77,9.81,0,-9.81,1,2,111,0,0,0,,,, -,0.1,0.77,9.81,-0.981,-9.81,1,2,110.9509495,0,0,-0.981,,,time,0 -,0.2,0.77,9.81,-1.962,-9.81,1,2,110.8037994,0,0,-1.962,,,height,111 +name,time,eff,grav,der(height),der(vel),flying,foo,height,impact,v_new,vel,,t=0,time,0 +data,0,1,1,2,2,2,2,2,2,2,2,,,height,111 +,0,0.77,9.81,0,-9.81,1,2,111,0,0,0,,,,0 +,0.1,0.77,9.81,-0.981,-9.81,1,2,110.9509495,0,0,-0.981,,,,0 +,0.2,0.77,9.81,-1.962,-9.81,1,2,110.8037994,0,0,-1.962,,,der(vel),-9.81 ,0.3,0.77,9.81,-2.943,-9.81,1,2,110.5585494,0,0,-2.943,,,,0 -,0.4,0.77,9.81,-3.924,-9.81,1,2,110.2151994,0,0,-3.924,,,,0 -,0.5,0.77,9.81,-4.905,-9.81,1,2,109.7737494,0,0,-4.905,,,der(vel),-9.81 +,0.4,0.77,9.81,-3.924,-9.81,1,2,110.2151994,0,0,-3.924,,,foo,2 +,0.5,0.77,9.81,-4.905,-9.81,1,2,109.7737494,0,0,-4.905,,,flying,1 ,0.6,0.77,9.81,-5.886,-9.81,1,2,109.2341993,0,0,-5.886,,,,0 -,0.7,0.77,9.81,-6.867,-9.81,1,2,108.5965493,0,0,-6.867,,,foo,2 -,0.8,0.77,9.81,-7.848,-9.81,1,2,107.8607993,0,0,-7.848,,,flying,1 -,0.9,0.77,9.81,-8.829,-9.81,1,2,107.0269493,0,0,-8.829,,,,0 -,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,time,0.1 -,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,height,110.9509495 -,,,,,,,,,,,,,,der(height)/vel,-0.981 -,,,,,,,,,,,,,,der(height)/vel,-0.981 -,,,,,,,,,,,,,,der(vel),-9.81 +,0.7,0.77,9.81,-6.867,-9.81,1,2,108.5965493,0,0,-6.867,,t=0.1,time,0.1 +,0.8,0.77,9.81,-7.848,-9.81,1,2,107.8607993,0,0,-7.848,,,height,110.9509495 +,0.9,0.77,9.81,-8.829,-9.81,1,2,107.0269493,0,0,-8.829,,,der(height)/vel,-0.981 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,der(height)/vel,-0.981 +,1,0.77,9.81,-9.81,-9.81,1,2,106.0949993,0,0,-9.81,,,der(vel),-9.81 ,,,,,,,,,,,,,,,0 ,,,,,,,,,,,,,,foo,2 ,,,,,,,,,,,,,,flying,1 ,,,,,,,,,,,,,,,0 -,,,,,,,,,,,,,,time,0.2 +,,,,,,,,,,,,,t=0.2,time,0.2 ,,,,,,,,,,,,,,height,110.8037994 ,,,,,,,,,,,,,,der(height)/vel,-1.962 ,,,,,,,,,,,,,,der(height)/vel,-1.962 @@ -28,5 +25,3 @@ data,0,1,1,2,2,2,2,2,2,2,2,,,, ,,,,,,,,,,,,,,foo,2 ,,,,,,,,,,,,,,flying,1 ,,,,,,,,,,,,,,impact/vel,0 -,,,,,,,,,,,,,,,0.3 -,,,,,,,,,,,,,,,110.5585494 diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 37bc920..2db8577 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -80,8 +80,8 @@ function typeBytes(type::T)::Int where T<:DataType return 1 end end -@testset "typeBits" begin - @test typeBits(Int32) == 4 +@testset "typeBytes" begin + @test typeBytes(Int32) == 4 end struct Aclass @@ -364,109 +364,126 @@ end @test di.info[2490]["isWithinTimeRange"] == 0 end +struct MatrixHeader + type::Int + nRows::Int + nCols::Int + hasImaginary::Bool + lName::Int +end """ read one variable from the thing to read a variable, we need its index, then to look up whether it is in data_1 or data_2 """ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) - open(ac.filepath, "r", lock=false) do matio + display(ac) + + + + open(ac.filepath, "r", lock=false) do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix - # @show startP = position(matio) println("\ndata_1:") - @show data1HeaderStart = mark(matio) # read data1 header: - # The 20-byte header consists of five long (4-byte) integers: + data1HeaderStart = mark(matio) dtype = 0 nrows = 0 ncols = 0 namelen = 0 includesImaginary = 0 try - @show dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte catch e error("caught error $e while reading $ac.filepath") end + mh1 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - @show data1MatrixName = mark(matio) + data1MatrixName = mark(matio) nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - @show matrixName = replace(String(nameuint), '\0'=>"") + matrixName = replace(String(nameuint), '\0'=>"") if matrixName != "data_1" error("trying to read matrix [data_1] but read $matrixName") end - @show data1MatrixStart = mark(matio) - #read the matrix data_1 - @show fmt1 = dataFormat(dtype) # read the format type before reading - # realread = [] + #skip dataMatrix1 + data1MatrixStart = mark(matio) + fmt1 = dataFormat(dtype) # read the format type before reading try - # realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - @show position(matio) - @show toskip = nrows*ncols*typeBytes(fmt1) #817*2*8 = 13072, 488197+20+7+13072 = 501296 + toskip = nrows*ncols*typeBytes(fmt1) #817*2*8 = 13072, 488197+20+7+13072 = 501296 skip(matio, toskip ) - @show position(matio) catch e error("caught error $e while reading $ac.filepath") end # read data2 header: println("\ndata_2:") - # The 20-byte header consists of five long (4-byte) integers: dtype = 0 nrows = 0 ncols = 0 namelen = 0 includesImaginary = 0 try - @show dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte catch e error("caught error $e while reading $ac.filepath") end + mh2 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) #read the matrix name - @show data2MatrixName = mark(matio) + data2MatrixName = mark(matio) nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - @show matrixName = replace(String(nameuint), '\0'=>"") + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "data_2" error("trying to read matrix [data_2] but read $matrixName") end + data2MatrixStart = mark(matio) - #read the matrix data_2 - @show data2MatrixStart = mark(matio) - @show fmt2 = dataFormat(dtype) # read the format type before reading + #with the positions marked, read the desired variable + # println("\nlocate variable [$name]:") + varInd = getVariableIndex(vn, name) - # so, having found both headers and matrix starts, need to calculate the variable's position and length - println("\nlocate variable [$name]:") - @show varInd = getVariableIndex(vn, name) - @show di.info[varInd] if di.info[varInd]["locatedInData"] == 1 #data_1 - @show pstart = data1MatrixStart + di.info[varInd]["indexInData"] * typeBytes(fmt1) - else #data_2 - @show pstart = data2MatrixStart + (di.info[varInd]["indexInData"]-1) * ncols * typeBytes(fmt2) - pstop = pstart + ncols*typeBytes(fmt2) - # @show pstop-pstart - seek(matio, pstart) - # readreal = read!(matio, Vector{fmt2}(undef, 30) ) - for i = 1:30 - ps = position(matio) - readreal = read!(matio, Vector{fmt2}(undef,1)) - println("$i [$ps] = $(readreal[1])") + #read the matrix data_1 + fmt1 = dataFormat(dtype) # read the format type before reading + # @show di.info[varInd] + + # dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. + if di.info[varInd]["isWithinTimeRange"]== 0 #linear interpolation + seek(matio, data1MatrixStart) + realread = read!(matio, Vector{fmt1}(undef,10)) + display(realread) + #data format is: time(tInitial), var1(tI), ... varN(tI), time(tFinal), var1(tF), ... varN(tF) + # seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt1) + ((ind-1)*nrows*typeBytes(fmt1)) ) + # readns[ind] = read(matio, fmt1) + + readns = Vector{fmt1}(undef, mh1.nCols) + for ind = 1:mh1.nCols + seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt1) + ((ind-1)*mh1.nRows*typeBytes(fmt1)) ) + readns[ind] = read(matio, fmt1) + end + return readns end - # 'transpose' reading, that is the data is saved time, var1(time), var2(time),... time2, var1(time2),... - readns = [] - nvar = 9 # == nrows == number of variables listing data2 as their location - bytesPerBar = 8 # typeBytes(fmt2) == Float64 - for ivar = 1:nrows + elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 + #read the matrix data_2 + fmt2 = dataFormat(dtype) # read the format type before reading + + if ac.isTranspose == false + # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... + readns = Vector{fmt2}(undef, ncols) for ind = 1:ncols - seek(matio, pstart + (ivar-1)*bytesPerBar + ((ind-1)*nvar*bytesPerBar) ) - readns = read!(matio, Vector{fmt2}(undef, 1) ) - println( "$ind] : " , readns[1]) + seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt2) + ((ind-1)*nrows*typeBytes(fmt2)) ) + readns[ind] = read(matio, fmt2) end + return readns + else + error("reading binTranspose not implemented, lack test data") end + else + error("variable [$name] is located in an unknown location") end - end #open end @@ -478,11 +495,28 @@ using JSON vn = readVariableNames(ac) vd = readVariableDescriptions(ac,vn) di = readDataInfo(ac,vd) + # println(JSON.json(di.info, 2)) #get the data 1/2 info - # readVariable(ac, vn, vd, di, "eff") #data1 - # readVariable(ac, vn, vd, di, "grav") #data1 - readVariable(ac, vn, vd, di, "time") # data0 - # readVariable(ac, vn, vd, di, "height") #data2 + + eff = readVariable(ac, vn, vd, di, "eff") #data1 + @test length(eff) == 2 + @test eff[1] ≈ 0.77 + @test eff[2] ≈ 0.77 + + grav = readVariable(ac, vn, vd, di, "grav") #data1 + @test length(grav) == 2 + @test grav[1] ≈ 9.81 + @test grav[2] ≈ 9.81 + + time = readVariable(ac, vn, vd, di, "time") # data0 + @test all(isapprox.(time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) + + height = readVariable(ac, vn, vd, di, "height") #data2 + @test isapprox(height[1], 111, rtol=1e-3) + @test isapprox(height[2], 110.9509, rtol=1e-3) + + vel = readVariable(ac, vn, vd, di, "vel") #data2 + @test isapprox(vel[2], -0.981, rtol=1e-3) end diff --git a/test/runtests_modelica_readMatrix.jl b/test/runtests_modelica_readMatrix.jl index d0fb41c..742e9e5 100644 --- a/test/runtests_modelica_readMatrix.jl +++ b/test/runtests_modelica_readMatrix.jl @@ -205,3 +205,5 @@ end @test true end + + From e0df0c0b0842ac7ba431ef81dfcc8727d80305f5 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 12:39:17 -0600 Subject: [PATCH 36/91] readMatrixHeader() --- test/runtests_modelica.jl | 134 +++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 53 deletions(-) diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 2db8577..95cbbe0 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -370,6 +370,31 @@ struct MatrixHeader nCols::Int hasImaginary::Bool lName::Int + name::String + format::DataType +end +""" +Reads the matix header, assuming matio's position is correct to read the header +""" +function readMatrixHeader!(matio::IOStream) :: MatrixHeader + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading matrix header") + end + + # data1MatrixName = mark(matio) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + + fmt = dataFormat(dtype) # read the format type before reading + + return MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen,matrixName, fmt) end """ @@ -379,38 +404,40 @@ to read a variable, we need its index, then to look up whether it is in data_1 o function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) display(ac) - - open(ac.filepath, "r", lock=false) do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix println("\ndata_1:") # read data1 header: data1HeaderStart = mark(matio) - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - mh1 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - - data1MatrixName = mark(matio) - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - if matrixName != "data_1" + # dtype = 0 + # nrows = 0 + # ncols = 0 + # namelen = 0 + # includesImaginary = 0 + # try + # dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + # catch e + # error("caught error $e while reading $ac.filepath") + # end + # mh1 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) + mh1 = readMatrixHeader!(matio) + if mh1.name != "data_1" error("trying to read matrix [data_1] but read $matrixName") end + # data1MatrixName = mark(matio) + # nameuint = read!(matio, Vector{UInt8}(undef, mh1.lName)) # read the full namelen to make the pointer ready to read the data + # matrixName = replace(String(nameuint), '\0'=>"") + # if matrixName != "data_1" + # error("trying to read matrix [data_1] but read $matrixName") + # end + #skip dataMatrix1 data1MatrixStart = mark(matio) - fmt1 = dataFormat(dtype) # read the format type before reading + # fmt1 = dataFormat(mh1.type) # read the format type before reading try - toskip = nrows*ncols*typeBytes(fmt1) #817*2*8 = 13072, 488197+20+7+13072 = 501296 + toskip = mh1.nRows*mh1.nCols*typeBytes(mh1.format) #817*2*8 = 13072, 488197+20+7+13072 = 501296 skip(matio, toskip ) catch e error("caught error $e while reading $ac.filepath") @@ -418,25 +445,26 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d # read data2 header: println("\ndata_2:") - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - mh2 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - - #read the matrix name - data2MatrixName = mark(matio) - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - - if matrixName != "data_2" - error("trying to read matrix [data_2] but read $matrixName") + # dtype = 0 + # nrows = 0 + # ncols = 0 + # namelen = 0 + # includesImaginary = 0 + # try + # dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + # catch e + # error("caught error $e while reading $ac.filepath") + # end + # # mh2 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) + + # #read the matrix name + # data2MatrixName = mark(matio) + # nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + # matrixName = replace(String(nameuint), '\0'=>"") + mh2 = readMatrixHeader!(matio) + + if mh2.name != "data_2" + error("trying to read matrix [data_2] but read $(mh2.name)") end data2MatrixStart = mark(matio) @@ -446,36 +474,36 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d if di.info[varInd]["locatedInData"] == 1 #data_1 #read the matrix data_1 - fmt1 = dataFormat(dtype) # read the format type before reading + # fmt1 = dataFormat(dtype) # read the format type before reading # @show di.info[varInd] # dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. if di.info[varInd]["isWithinTimeRange"]== 0 #linear interpolation - seek(matio, data1MatrixStart) - realread = read!(matio, Vector{fmt1}(undef,10)) - display(realread) + # seek(matio, data1MatrixStart) + # realread = read!(matio, Vector{mh1.format}(undef,10)) + # display(realread) #data format is: time(tInitial), var1(tI), ... varN(tI), time(tFinal), var1(tF), ... varN(tF) - # seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt1) + ((ind-1)*nrows*typeBytes(fmt1)) ) - # readns[ind] = read(matio, fmt1) + # seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*nrows*typeBytes(mh1.format)) ) + # readns[ind] = read(matio, mh1.format) - readns = Vector{fmt1}(undef, mh1.nCols) + readns = Vector{mh1.format}(undef, mh1.nCols) for ind = 1:mh1.nCols - seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt1) + ((ind-1)*mh1.nRows*typeBytes(fmt1)) ) - readns[ind] = read(matio, fmt1) + seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*mh1.nRows*typeBytes(mh1.format)) ) + readns[ind] = read(matio, mh1.format) end return readns end elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 #read the matrix data_2 - fmt2 = dataFormat(dtype) # read the format type before reading + # fmt2 = dataFormat(dtype) # read the format type before reading if ac.isTranspose == false # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... - readns = Vector{fmt2}(undef, ncols) - for ind = 1:ncols - seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(fmt2) + ((ind-1)*nrows*typeBytes(fmt2)) ) - readns[ind] = read(matio, fmt2) + readns = Vector{mh2.format}(undef, mh2.nCols) + for ind = 1:mh2.nCols + seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh2.format) + ((ind-1)*mh2.nRows*typeBytes(mh2.format)) ) + readns[ind] = read(matio, mh2.format) end return readns else From 678c17dbc2504b6d5dccf166af7beb363c8e4e5d Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 13:00:19 -0600 Subject: [PATCH 37/91] moved into MAT_v4_Modelica --- src/MAT_v4_Modelica.jl | 388 +++++++++++++++++++++++++++++- test/runtests_modelica.jl | 491 +++----------------------------------- 2 files changed, 416 insertions(+), 463 deletions(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index f739642..f883311 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -1,18 +1,392 @@ module MAT_v4_Modelica +# The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices +# The first matrix, Aclass is narrowly defined -import Base: read, write, close +function isLittleEndian(dtype) :: Bool + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(dtype; pad=4) + # @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 + return M == 0 +end + + +function dataFormat(type) :: DataType + #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... + T, P, O, M = digits(type; pad=4) + # @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 + if P == 0 + return Float64 + end + if P == 1 + return Float32 + end + if P == 2 + return Int32 + end + if P == 3 + return Int16 + end + if P == 4 + return UInt16 + end + if P == 5 + return UInt8 + end +end -mutable struct ModelicaV4 - ios::IOStream - varnames::Dict{String, Int64} - ModelicaV4(ios) = new(ios) +function typeBytes(type::T)::Int where T<:DataType + if type == Float64 + return 8 + end + if type == Float32 + return 4 + end + if type == Int32 + return 4 + end + if type == Int16 + return 2 + end + if type == UInt16 + return 2 + end + if type == UInt8 + return 1 + end end +struct Aclass + filepath::String + isTranspose::Bool + positionStart::Int + positionEnd::Int +end + +""" +Reads the Aclass matrix, returing if the data is stored binNormal or binTranspose +""" +function readAclass( filepath::String ) + open(filepath, "r", lock=false) do matio + seekstart(matio) # always start from the start, don't assume the position + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # 32bit=4byte * 5 qty + catch e + error("caught error $e while reading $filepath") + end + + if !isLittleEndian(dtype) + error("Only the little-endian encoding is implemented, cannot read $filepath") + end -function read(mat::Matlabv4File) - seekstart(mat.ios) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + name = replace(String(nameuint), '\0'=>"") + if name != "Aclass" + error("First matrix must be named Aclass, is instead [$name]. This likely means that [$filepath] is not a Modelica MAT v4 file.") + end + # real: the Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. + fmt = dataFormat(dtype) # read the format type before reading + realint = read!(matio, Matrix{UInt8}(undef, nrows,ncols)) + Aclass1 = replace(String(realint[1,:]), '\0'=>"") + Aclass2 = replace(String(realint[2,:]), '\0'=>"") + Aclass3 = replace(String(realint[3,:]), '\0'=>"") + Aclass4 = replace(String(realint[4,:]), '\0'=>"") + if Aclass1 == "Atrajectory" && Aclass2 == "1.1" && isempty(Aclass3) && Aclass4 == "binNormal" || Aclass4 == "binTrans" + return Aclass( filepath, Aclass4 == "binTranspose", startP, position(matio) ) + end + end #open end + +##### the NAME matrix######################################################################################################################## +struct VariableNames + # names::Vector{T}(undef,undef) where T<:AbstractString + names::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableNames(ac::Aclass) + open(ac.filepath, "r", lock=false) do matio + seek(matio, ac.positionEnd) #skip over Aclass + startP = position(matio) + + #read the matrix header + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "name" + error("trying to read matrix [name] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realint = [] + try + realint = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + #pull the names out of the matrix + vnames = [] + for i in 1:ncols + push!(vnames, replace(String(realint[:,i]), '\0'=>"")) # note :,1 = implicit transpose + end + + return VariableNames(vnames, startP, position(matio)) + + end #open +end + + +function getVariableIndex(vn::VariableNames, name::String) + vecAll = findall( x->x==name, vn.names) + n = length(vecAll) + + if isempty(vecAll) == true + return -1 + else + if n>1 + error("Found $n instances of variable [$name], but variables should be unique.") + end + return vecAll[1] + end +end + + +struct VariableDescriptions + names::Vector{String} + descriptions::Vector{String} + positionStart::Int + positionEnd::Int +end + +function readVariableDescriptions(ac::Aclass, vn::VariableNames) + open(ac.filepath, "r", lock=false) do matio + seek(matio, vn.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "description" + error("trying to read matrix [description] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + vdesc = [] + for i in 1:ncols + push!(vdesc, replace(String(realread[:,i]), '\0'=>"")) # note :,1 = implicit transpose + end + + return VariableDescriptions(vn.names, vdesc, startP, position(matio)) + end #open +end + + +struct DataInfo + info + positionStart::Int + positionEnd::Int +end + +""" +dataInfo provides indicies to access variable data + +dataInfo +Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). + dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). + dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. + dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. + dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. +""" +function readDataInfo(ac::Aclass, vd::VariableDescriptions) + open(ac.filepath, "r", lock=false) do matio + seek(matio, vd.positionEnd) #this follows the VariableNames matrix + startP = position(matio) + + # The 20-byte header consists of five long (4-byte) integers: + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading $ac.filepath") + end + + #read the matrix name + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + if matrixName != "dataInfo" + error("trying to read variable [dataInfo] but read [$matrixName]") + end + + #read the matrix data + fmt = dataFormat(dtype) # read the format type before reading + realread = [] + try + realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long + catch e + error("caught error $e while reading $ac.filepath") + end + + dinfo = [] + for i in 1:ncols + push!(dinfo, Dict("name"=>vd.names[i], "description"=>vd.descriptions[i], "locatedInData"=>realread[1,i], "indexInData"=>realread[2,i], "isInterpolated"=>realread[3,i], "isWithinTimeRange"=>realread[4,i] ) ) + end + return DataInfo( dinfo, startP, position(matio)) + end #open +end + + + +struct MatrixHeader + type::Int + nRows::Int + nCols::Int + hasImaginary::Bool + lName::Int + name::String + format::DataType +end +""" +Reads the matix header, assuming matio's position is correct to read the header +""" +function readMatrixHeader!(matio::IOStream) :: MatrixHeader + dtype = 0 + nrows = 0 + ncols = 0 + namelen = 0 + includesImaginary = 0 + try + dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte + catch e + error("caught error $e while reading matrix header") + end + + # data1MatrixName = mark(matio) + nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data + matrixName = replace(String(nameuint), '\0'=>"") + + fmt = dataFormat(dtype) # read the format type before reading + + return MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen,matrixName, fmt) +end + +""" +read one variable from the thing +to read a variable, we need its index, then to look up whether it is in data_1 or data_2 +""" +function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) + display(ac) + + open(ac.filepath, "r", lock=false) do matio + seek(matio, di.positionEnd) #this follows the VariableNames matrix + + # read data1 header: + # data1HeaderStart = mark(matio) + mh1 = readMatrixHeader!(matio) + if mh1.name != "data_1" + error("trying to read matrix [data_1] but read $matrixName") + end + + data1MatrixStart = mark(matio) + try + toskip = mh1.nRows*mh1.nCols*typeBytes(mh1.format) #817*2*8 = 13072, 488197+20+7+13072 = 501296 + skip(matio, toskip ) + catch e + error("caught error $e while reading $ac.filepath") + end + + # read data2 header: + mh2 = readMatrixHeader!(matio) + + if mh2.name != "data_2" + error("trying to read matrix [data_2] but read $(mh2.name)") + end + data2MatrixStart = mark(matio) + + #with the positions marked, read the desired variable + # println("\nlocate variable [$name]:") + varInd = getVariableIndex(vn, name) + + if di.info[varInd]["locatedInData"] == 1 #data_1 + #read the matrix data_1 + # dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. + if di.info[varInd]["isWithinTimeRange"]== 0 #linear interpolation + #data format is: time(tInitial), var1(tI), ... varN(tI), time(tFinal), var1(tF), ... varN(tF) + readns = Vector{mh1.format}(undef, mh1.nCols) + for ind = 1:mh1.nCols + seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*mh1.nRows*typeBytes(mh1.format)) ) + readns[ind] = read(matio, mh1.format) + end + return readns + end + + elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 + #read the matrix data_2 + if ac.isTranspose == false + # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... + readns = Vector{mh2.format}(undef, mh2.nCols) + for ind = 1:mh2.nCols + seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh2.format) + ((ind-1)*mh2.nRows*typeBytes(mh2.format)) ) + readns[ind] = read(matio, mh2.format) + end + return readns + else + error("reading binTranspose not implemented, lack test data") + end + else + error("variable [$name] is located in an unknown location") + end + end #open +end + + + end #module \ No newline at end of file diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 95cbbe0..76d90b5 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -5,7 +5,7 @@ Pkg.activate(joinpath(@__DIR__, "..")) # test only MAT_v4 using Test # include("../src/MAT.jl") -# include("../src/MAT_v4_Modelica.jl") +include("../src/MAT_v4_Modelica.jl") const mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" @@ -14,192 +14,39 @@ const mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer # The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices # The first matrix, Aclass is narrowly defined -function isLittleEndian(dtype) :: Bool - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(dtype; pad=4) - # @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 - return M == 0 -end - @testset "isLittleEndian" begin - @test isLittleEndian(0) == true - @test isLittleEndian(1000) == false - @test isLittleEndian(2000) == false - @test isLittleEndian(3000) == false + @test MAT_v4_Modelica.isLittleEndian(0) == true + @test MAT_v4_Modelica.isLittleEndian(1000) == false + @test MAT_v4_Modelica.isLittleEndian(2000) == false + @test MAT_v4_Modelica.isLittleEndian(3000) == false end -function dataFormat(type) :: DataType - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(type; pad=4) - # @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 - if P == 0 - return Float64 - end - if P == 1 - return Float32 - end - if P == 2 - return Int32 - end - if P == 3 - return Int16 - end - if P == 4 - return UInt16 - end - if P == 5 - return UInt8 - end -end @testset "dataFormat" begin - @test dataFormat(0000) <: Float64 - @test dataFormat(0010) <: Float32 - @test dataFormat(0020) <: Int32 - @test dataFormat(0030) <: Int16 - @test dataFormat(0040) <: UInt16 - @test dataFormat(0050) <: UInt8 + @test MAT_v4_Modelica.dataFormat(0000) <: Float64 + @test MAT_v4_Modelica.dataFormat(0010) <: Float32 + @test MAT_v4_Modelica.dataFormat(0020) <: Int32 + @test MAT_v4_Modelica.dataFormat(0030) <: Int16 + @test MAT_v4_Modelica.dataFormat(0040) <: UInt16 + @test MAT_v4_Modelica.dataFormat(0050) <: UInt8 end -function typeBytes(type::T)::Int where T<:DataType - if type == Float64 - return 8 - end - if type == Float32 - return 4 - end - if type == Int32 - return 4 - end - if type == Int16 - return 2 - end - if type == UInt16 - return 2 - end - if type == UInt8 - return 1 - end -end @testset "typeBytes" begin - @test typeBytes(Int32) == 4 + @test MAT_v4_Modelica.typeBytes(Int32) == 4 end -struct Aclass - filepath::String - isTranspose::Bool - positionStart::Int - positionEnd::Int -end - -""" -Reads the Aclass matrix, returing if the data is stored binNormal or binTranspose -""" -function readAclass( filepath::String ) - open(filepath, "r", lock=false) do matio - seekstart(matio) # always start from the start, don't assume the position - startP = position(matio) - - # The 20-byte header consists of five long (4-byte) integers: - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # 32bit=4byte * 5 qty - catch e - error("caught error $e while reading $filepath") - end - - if !isLittleEndian(dtype) - error("Only the little-endian encoding is implemented, cannot read $filepath") - end - - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - name = replace(String(nameuint), '\0'=>"") - if name != "Aclass" - error("First matrix must be named Aclass, is instead [$name]. This likely means that [$filepath] is not a Modelica MAT v4 file.") - end - - # real: the Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. - fmt = dataFormat(dtype) # read the format type before reading - realint = read!(matio, Matrix{UInt8}(undef, nrows,ncols)) - - Aclass1 = replace(String(realint[1,:]), '\0'=>"") - Aclass2 = replace(String(realint[2,:]), '\0'=>"") - Aclass3 = replace(String(realint[3,:]), '\0'=>"") - Aclass4 = replace(String(realint[4,:]), '\0'=>"") - if Aclass1 == "Atrajectory" && Aclass2 == "1.1" && isempty(Aclass3) && Aclass4 == "binNormal" || Aclass4 == "binTrans" - return Aclass( filepath, Aclass4 == "binTranspose", startP, position(matio) ) - end - end #open -end @testset "Aclass" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) + ac = MAT_v4_Modelica.readAclass(mat1s) @test ac.positionStart == 0 @test ac.positionEnd == 71 end -##### the NAME matrix######################################################################################################################## -struct VariableNames - # names::Vector{T}(undef,undef) where T<:AbstractString - names::Vector{String} - positionStart::Int - positionEnd::Int -end - -function readVariableNames(ac::Aclass) - open(ac.filepath, "r", lock=false) do matio - seek(matio, ac.positionEnd) #skip over Aclass - startP = position(matio) - - #read the matrix header - # The 20-byte header consists of five long (4-byte) integers: - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - - #read the matrix name - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - if matrixName != "name" - error("trying to read matrix [name] but read [$matrixName]") - end - - #read the matrix data - fmt = dataFormat(dtype) # read the format type before reading - realint = [] - try - realint = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - catch e - error("caught error $e while reading $ac.filepath") - end - - #pull the names out of the matrix - vnames = [] - for i in 1:ncols - push!(vnames, replace(String(realint[:,i]), '\0'=>"")) # note :,1 = implicit transpose - end - - return VariableNames(vnames, startP, position(matio)) - - end #open -end - @testset "readVariableNames" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) - vn = readVariableNames(ac) + ac = MAT_v4_Modelica.readAclass(mat1s) + vn = MAT_v4_Modelica.readVariableNames(ac) # @show vn @test length(vn.names) == 2490 @test vn.names[1] == "time" @@ -210,82 +57,21 @@ end @test vn.positionEnd == 117126 end -function getVariableIndex(vn::VariableNames, name::String) - vecAll = findall( x->x==name, vn.names) - n = length(vecAll) - - if isempty(vecAll) == true - return -1 - else - if n>1 - error("Found $n instances of variable [$name], but variables should be unique.") - end - return vecAll[1] - end -end @testset "getVariableIndex" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) - vn = readVariableNames(ac) - @test getVariableIndex(vn, vn.names[3]) == 3 - @test getVariableIndex(vn, vn.names[30]) == 30 + ac = MAT_v4_Modelica.readAclass(mat1s) + vn = MAT_v4_Modelica.readVariableNames(ac) + @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 + @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[30]) == 30 end -struct VariableDescriptions - names::Vector{String} - descriptions::Vector{String} - positionStart::Int - positionEnd::Int -end - -function readVariableDescriptions(ac::Aclass, vn::VariableNames) - open(ac.filepath, "r", lock=false) do matio - seek(matio, vn.positionEnd) #this follows the VariableNames matrix - startP = position(matio) - - # The 20-byte header consists of five long (4-byte) integers: - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - - #read the matrix name - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - if matrixName != "description" - error("trying to read matrix [description] but read [$matrixName]") - end - - #read the matrix data - fmt = dataFormat(dtype) # read the format type before reading - realread = [] - try - realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - catch e - error("caught error $e while reading $ac.filepath") - end - - vdesc = [] - for i in 1:ncols - push!(vdesc, replace(String(realread[:,i]), '\0'=>"")) # note :,1 = implicit transpose - end - - return VariableDescriptions(vn.names, vdesc, startP, position(matio)) - end #open -end @testset "readVariableDescriptions" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) + ac = MAT_v4_Modelica.readAclass(mat1s) + vn = MAT_v4_Modelica.readVariableNames(ac) + vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) @test length(vd.descriptions) == 2490 @test vd.descriptions[1] == "Simulation time [s]" @test vd.descriptions[3] == "First derivative of angle phi (relative angular velocity) [rad/s]" @@ -293,70 +79,14 @@ end @test vd.descriptions[2490] == "Color of cylinders" end -struct DataInfo - info - positionStart::Int - positionEnd::Int -end - -""" -dataInfo provides indicies to access variable data - -dataInfo -Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). - dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). - dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. - dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. - dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. -""" -function readDataInfo(ac::Aclass, vd::VariableDescriptions) - open(ac.filepath, "r", lock=false) do matio - seek(matio, vd.positionEnd) #this follows the VariableNames matrix - startP = position(matio) - - # The 20-byte header consists of five long (4-byte) integers: - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading $ac.filepath") - end - - #read the matrix name - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - if matrixName != "dataInfo" - error("trying to read variable [dataInfo] but read [$matrixName]") - end - - #read the matrix data - fmt = dataFormat(dtype) # read the format type before reading - realread = [] - try - realread = read!(matio, Matrix{fmt}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - catch e - error("caught error $e while reading $ac.filepath") - end - - dinfo = [] - for i in 1:ncols - push!(dinfo, Dict("name"=>vd.names[i], "description"=>vd.descriptions[i], "locatedInData"=>realread[1,i], "indexInData"=>realread[2,i], "isInterpolated"=>realread[3,i], "isWithinTimeRange"=>realread[4,i] ) ) - end - return DataInfo( dinfo, startP, position(matio)) - end #open -end @testset "readDataInfo" begin mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = readAclass(mat1s) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) - di = readDataInfo(ac,vd) + ac = MAT_v4_Modelica.readAclass(mat1s) + vn = MAT_v4_Modelica.readVariableNames(ac) + vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT_v4_Modelica.readDataInfo(ac,vd) # @show di.info[3] @test di.info[1]["isWithinTimeRange"] == -1 @test di.info[3]["locatedInData"] == 2 @@ -364,186 +94,35 @@ end @test di.info[2490]["isWithinTimeRange"] == 0 end -struct MatrixHeader - type::Int - nRows::Int - nCols::Int - hasImaginary::Bool - lName::Int - name::String - format::DataType -end -""" -Reads the matix header, assuming matio's position is correct to read the header -""" -function readMatrixHeader!(matio::IOStream) :: MatrixHeader - dtype = 0 - nrows = 0 - ncols = 0 - namelen = 0 - includesImaginary = 0 - try - dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - catch e - error("caught error $e while reading matrix header") - end - - # data1MatrixName = mark(matio) - nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - - fmt = dataFormat(dtype) # read the format type before reading - - return MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen,matrixName, fmt) -end - -""" -read one variable from the thing -to read a variable, we need its index, then to look up whether it is in data_1 or data_2 -""" -function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) - display(ac) - - open(ac.filepath, "r", lock=false) do matio - seek(matio, di.positionEnd) #this follows the VariableNames matrix - - println("\ndata_1:") - # read data1 header: - data1HeaderStart = mark(matio) - # dtype = 0 - # nrows = 0 - # ncols = 0 - # namelen = 0 - # includesImaginary = 0 - # try - # dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - # catch e - # error("caught error $e while reading $ac.filepath") - # end - # mh1 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - mh1 = readMatrixHeader!(matio) - if mh1.name != "data_1" - error("trying to read matrix [data_1] but read $matrixName") - end - - # data1MatrixName = mark(matio) - # nameuint = read!(matio, Vector{UInt8}(undef, mh1.lName)) # read the full namelen to make the pointer ready to read the data - # matrixName = replace(String(nameuint), '\0'=>"") - # if matrixName != "data_1" - # error("trying to read matrix [data_1] but read $matrixName") - # end - - #skip dataMatrix1 - data1MatrixStart = mark(matio) - # fmt1 = dataFormat(mh1.type) # read the format type before reading - try - toskip = mh1.nRows*mh1.nCols*typeBytes(mh1.format) #817*2*8 = 13072, 488197+20+7+13072 = 501296 - skip(matio, toskip ) - catch e - error("caught error $e while reading $ac.filepath") - end - - # read data2 header: - println("\ndata_2:") - # dtype = 0 - # nrows = 0 - # ncols = 0 - # namelen = 0 - # includesImaginary = 0 - # try - # dtype, nrows, ncols, includesImaginary, namelen = read!(matio, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - # catch e - # error("caught error $e while reading $ac.filepath") - # end - # # mh2 = MatrixHeader(dtype,nrows,ncols,includesImaginary,namelen) - - # #read the matrix name - # data2MatrixName = mark(matio) - # nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - # matrixName = replace(String(nameuint), '\0'=>"") - mh2 = readMatrixHeader!(matio) - - if mh2.name != "data_2" - error("trying to read matrix [data_2] but read $(mh2.name)") - end - data2MatrixStart = mark(matio) - - #with the positions marked, read the desired variable - # println("\nlocate variable [$name]:") - varInd = getVariableIndex(vn, name) - - if di.info[varInd]["locatedInData"] == 1 #data_1 - #read the matrix data_1 - # fmt1 = dataFormat(dtype) # read the format type before reading - # @show di.info[varInd] - - # dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. - if di.info[varInd]["isWithinTimeRange"]== 0 #linear interpolation - # seek(matio, data1MatrixStart) - # realread = read!(matio, Vector{mh1.format}(undef,10)) - # display(realread) - #data format is: time(tInitial), var1(tI), ... varN(tI), time(tFinal), var1(tF), ... varN(tF) - # seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*nrows*typeBytes(mh1.format)) ) - # readns[ind] = read(matio, mh1.format) - - readns = Vector{mh1.format}(undef, mh1.nCols) - for ind = 1:mh1.nCols - seek(matio, data1MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh1.format) + ((ind-1)*mh1.nRows*typeBytes(mh1.format)) ) - readns[ind] = read(matio, mh1.format) - end - return readns - end - - elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 - #read the matrix data_2 - # fmt2 = dataFormat(dtype) # read the format type before reading - - if ac.isTranspose == false - # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... - readns = Vector{mh2.format}(undef, mh2.nCols) - for ind = 1:mh2.nCols - seek(matio, data2MatrixStart + (di.info[varInd]["indexInData"]-1)*typeBytes(mh2.format) + ((ind-1)*mh2.nRows*typeBytes(mh2.format)) ) - readns[ind] = read(matio, mh2.format) - end - return readns - else - error("reading binTranspose not implemented, lack test data") - end - else - error("variable [$name] is located in an unknown location") - end - end #open -end - using JSON @testset "readVariable" begin # mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" matbb = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = readAclass(matbb) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) - di = readDataInfo(ac,vd) + ac = MAT_v4_Modelica.readAclass(matbb) + vn = MAT_v4_Modelica.readVariableNames(ac) + vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT_v4_Modelica.readDataInfo(ac,vd) # println(JSON.json(di.info, 2)) #get the data 1/2 info - eff = readVariable(ac, vn, vd, di, "eff") #data1 + eff = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 @test length(eff) == 2 @test eff[1] ≈ 0.77 @test eff[2] ≈ 0.77 - grav = readVariable(ac, vn, vd, di, "grav") #data1 + grav = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 @test length(grav) == 2 @test grav[1] ≈ 9.81 @test grav[2] ≈ 9.81 - time = readVariable(ac, vn, vd, di, "time") # data0 + time = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # data0 @test all(isapprox.(time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) - height = readVariable(ac, vn, vd, di, "height") #data2 + height = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 @test isapprox(height[1], 111, rtol=1e-3) @test isapprox(height[2], 110.9509, rtol=1e-3) - vel = readVariable(ac, vn, vd, di, "vel") #data2 + vel = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 @test isapprox(vel[2], -0.981, rtol=1e-3) end From 17c6a46eebaf4ee93c46a41c88a70f01515e6e02 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Thu, 9 Feb 2023 16:10:02 -0600 Subject: [PATCH 38/91] initially useable --- src/MAT_v4_Modelica.jl | 5 +- .../Modelica/FallingBodyBox/FallingBodyBox.mo | 2 +- .../FallingBodyBox/FallingBodyBox_res.csv | 103 ++++++++++++++++++ .../FallingBodyBox/FallingBodyBox_res.mat | Bin 0 -> 776747 bytes test/runtests_modelica.jl | 86 ++++++++++----- 5 files changed, 163 insertions(+), 33 deletions(-) create mode 100644 test/Modelica/FallingBodyBox/FallingBodyBox_res.csv create mode 100644 test/Modelica/FallingBodyBox/FallingBodyBox_res.mat diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index f883311..0e40e35 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -323,8 +323,6 @@ read one variable from the thing to read a variable, we need its index, then to look up whether it is in data_1 or data_2 """ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) - display(ac) - open(ac.filepath, "r", lock=false) do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix @@ -354,6 +352,9 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d #with the positions marked, read the desired variable # println("\nlocate variable [$name]:") varInd = getVariableIndex(vn, name) + if varInd < 1 + throw( ErrorException("Variable [$name] not found in file [$(ac.filepath)]") ) + end if di.info[varInd]["locatedInData"] == 1 #data_1 #read the matrix data_1 diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox.mo b/test/Modelica/FallingBodyBox/FallingBodyBox.mo index 6e74314..3340533 100644 --- a/test/Modelica/FallingBodyBox/FallingBodyBox.mo +++ b/test/Modelica/FallingBodyBox/FallingBodyBox.mo @@ -1,7 +1,7 @@ model FallingBodyBox inner Modelica.Mechanics.MultiBody.World world annotation( Placement(visible = true, transformation(origin = {-70, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); - Modelica.Mechanics.MultiBody.Parts.BodyBox bodyBox(r = {1, 0, 0}) annotation( + Modelica.Mechanics.MultiBody.Parts.BodyBox bodyBox(r = {1, 0, 0}, w_0_fixed = true, w_0_start = {1, 2, 3}) annotation( Placement(visible = true, transformation(origin = {10, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); Modelica.Mechanics.MultiBody.Joints.FreeMotion freeMotion annotation( Placement(visible = true, transformation(origin = {-30, 10}, extent = {{-10, -10}, {10, 10}}, rotation = 0))); diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox_res.csv b/test/Modelica/FallingBodyBox/FallingBodyBox_res.csv new file mode 100644 index 0000000..0bf3c2a --- /dev/null +++ b/test/Modelica/FallingBodyBox/FallingBodyBox_res.csv @@ -0,0 +1,103 @@ +"time","bodyBox.r[1]","bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]","bodyBox.v_0[2]","bodyBox.frame_b.r_0[1]" +0,1,0,1,0,1 +0.002,1,1.417825012934035e-05,0.9999740006763724,-0.0195807445407832,0.9999881789265018 +0.004,1,5.313923834174793e-05,0.9998960030304396,-0.03908714168556562,0.9999491422687813 +0.006,1,0.0001181362617060314,0.9997660114590055,-0.05851542917791717,0.9998841477207114 +0.008,1,0.0002091234527678357,0.9995840332464712,-0.0778657434873069,0.999793156699239 +0.01,1,0.000326099054897985,0.9993500776948991,-0.09713808926719215,0.9996761767497971 +0.012,1,0.0004690560147366672,0.9990641573526586,-0.1163324869886311,0.9995332133673953 +0.014,1,0.0006379874421543658,0.9987262866361137,-0.1354489568144222,0.9993642740782681 +0.016,1,0.000832885617011653,0.9983364820850886,-0.1544875215725002,0.9991693677021003 +0.018,1,0.001053739093578769,0.9978947644247245,-0.1734482153767369,0.9989485035183032 +0.02,1,0.001300538971511691,0.9974011552321307,-0.1923310647500386,0.9987016942036424 +0.022,1,0.00157327094282644,0.9968556808437448,-0.2111361125177071,0.9984289517865712 +0.024,1,0.001871924983280909,0.9962583689451037,-0.2298633885555008,0.9981302939283846 +0.026,1,0.002196481817791961,0.9956092513011041,-0.2485129501878651,0.9978057331188961 +0.028,1,0.002546927054133541,0.9949083620349535,-0.2670848402930672,0.997455289089087 +0.03,1,0.002923239445405512,0.994155737557995,-0.2855791220502004,0.9970789770034005 +0.032,1,0.003325388624709284,0.9933514166140098,-0.3039958856428557,0.9966768052387192 +0.034,1,0.003753358061751959,0.9924954413939879,-0.3223351802805359,0.9962487994557399 +0.036,1,0.004207126617115786,0.9915878565784442,-0.340597068821661,0.9957949831955599 +0.038,1,0.004686673151383015,0.9906287095709687,-0.3587816141246513,0.9953153827223518 +0.04,1,0.005191967386675178,0.9896180499664131,-0.3768889067903736,0.9948100173530883 +0.042,1,0.005722986459759737,0.9885559304269864,-0.3949190149103882,0.9942789168867462 +0.044,1,0.006279709994901068,0.9874424064724534,-0.4128719990247363,0.9937221164673544 +0.046,1,0.006862115072954707,0.9862775361787828,-0.4307479273947147,0.9931396512517375 +0.048,1,0.007470178774776187,0.9850613803166175,-0.4485468682816198,0.9925315590913937 +0.05,1,0.008103821130588527,0.9837940009841819,-0.4662690606761235,0.9918978221147704 +0.052,1,0.00876304148364556,0.9824754647342051,-0.4839145062172048,0.9912385062178507 +0.054,1,0.009447812213827225,0.9811058401953279,-0.5014832872342715,0.990553652409155 +0.056,1,0.01015810452490512,0.97968519863496,-0.5189754895763452,0.9898433031598651 +0.058,1,0.01089387776305806,0.9782136138791815,-0.5363912346772112,0.9891074916422395 +0.06,1,0.01165505381920325,0.9766911620635708,-0.553730756374293,0.988346215882774 +0.062,1,0.01244162634538108,0.975117922605218,-0.5709940731491192,0.9875595489505991 +0.064,1,0.0132535600626235,0.9734939773231635,-0.5881812903054828,0.9867475373857869 +0.066,1,0.01409081969196248,0.9718194106847828,-0.6052925131471765,0.9859102303767453 +0.068,1,0.01495334316027893,0.9700943096511918,-0.6223279274395663,0.9850476528114708 +0.07,1,0.01584106743243119,0.9683187638112045,-0.6392877218369272,0.9841598312436357 +0.072,1,0.01675397185431944,0.966492865632629,-0.6561719577252614,0.9832468374869485 +0.074,1,0.01769201473536401,0.9646167101017287,-0.67298075966213,0.9823087248370926 +0.076,1,0.01865515438498519,0.9626903948376431,-0.6897142522050934,0.9813455492226283 +0.078,1,0.01964331707618472,0.9607140197251685,-0.7063726561112313,0.9803573368013532 +0.08,1,0.02065645250112456,0.9586876875208576,-0.7229561218141588,0.9793441400219822 +0.082,1,0.02169451650984585,0.9566115036712002,-0.7394647812565036,0.9783060201810461 +0.084,1,0.02275745575203531,0.9544855761318363,-0.7558987940078782,0.9772430318838716 +0.086,1,0.02384521563088566,0.952310015452082,-0.7722583233808898,0.9761552310829678 +0.088,1,0.02495774030309547,0.9500849347706286,-0.7885435364311402,0.9750426750737241 +0.09,1,0.02609497267886922,0.9478104498111921,-0.8047546039572244,0.9739054224900613 +0.092,1,0.02725685442191735,0.9454866788781227,-0.820891700500733,0.97274353330004 +0.094,1,0.02844332594945613,0.94311374285196,-0.8369550043462501,0.9715570688014161 +0.096,1,0.02965432300661144,0.9406917650407978,-0.8529447077563223,0.9703460880474092 +0.098,1,0.03088977204827575,0.9382208709649862,-0.8688610307695054,0.9691106430132619 +0.1,1,0.03214961678515921,0.9357011894795664,-0.884704141886301,0.9678508062647256 +0.102,1,0.03343379109670443,0.9331328516383901,-0.9004742389997172,0.9665666427350945 +0.104,1,0.0347422274909309,0.9305159910386045,-0.9161715241190189,0.9652582185295354 +0.106,1,0.03607485710443521,0.9278507438155497,-0.9317962033697258,0.9639256009199849 +0.108,1,0.03743160970239083,0.925137248637621,-0.9473484869936142,0.9625688583400118 +0.11,1,0.03881241367854828,0.9223756467010917,-0.9628285893487162,0.96118806037964 +0.112,1,0.04021719605523505,0.919566081724903,-0.9782367289093201,0.9597832777801381 +0.114,1,0.04164588248335553,0.9167086999454117,-0.9935731282659692,0.9583545824287673 +0.116,1,0.0430984131729114,0.9138036485978986,-1.008837966181234,0.9569020617708101 +0.118,1,0.04457470783647349,0.910851079164642,-1.024031483095255,0.9554257870011156 +0.12,1,0.04607468513507586,0.9078511456196503,-1.039153922601708,0.9539258307547261 +0.122,1,0.04759826680889059,0.904804004006921,-1.054205519016186,0.9524022708158115 +0.124,1,0.04914537332958941,0.9017098128396142,-1.069186510460787,0.9508551861692036 +0.126,1,0.0507159239003439,0.8985687330936636,-1.084097138864106,0.9492846569940074 +0.128,1,0.05230983645582534,0.8953809282013514,-1.098937649961244,0.9476907646571767 +0.13,1,0.05392702766220477,0.8921465640448423,-1.113708293293798,0.9460735917070471 +0.132,1,0.055567412917153,0.8888658089496781,-1.128409322209871,0.9444332218668312 +0.134,1,0.05723090846134186,0.8855388331121092,-1.143040987506299,0.9427697415734511 +0.136,1,0.05891743315753914,0.882165808066411,-1.157603532071766,0.9410832412239502 +0.138,1,0.06062689417026532,0.8787469105544052,-1.172097234005607,0.9393738047246706 +0.14,1,0.06235920240475686,0.8752823184025715,-1.186522360130819,0.9376415208073284 +0.142,1,0.0641142675341053,0.8717722118281508,-1.200879180967436,0.9358864793622561 +0.144,1,0.06589199799925725,0.8682167734316997,-1.215167970732527,0.9341087714309569 +0.146,1,0.06769230100901449,0.8646161881896115,-1.229389007340194,0.932308489198626 +0.148,1,0.0695150825400338,0.8609706434466029,-1.243542572401574,0.9304857259866367 +0.15,1,0.07136024733682711,0.8572803289081588,-1.257628951224839,0.9286405762449859 +0.152,1,0.07322769891176151,0.8535454366329454,-1.271648432815197,0.9267731355447069 +0.154,1,0.07511734742166784,0.849766158927348,-1.285601286354159,0.9248835063490158 +0.156,1,0.07702909193293221,0.8459426929871825,-1.299487814158628,0.9229717849201147 +0.158,1,0.07896283092029514,0.8420752381907177,-1.313308320329912,0.9210380691110128 +0.16,1,0.08091846365462857,0.8381639956960949,-1.327063106609619,0.9190824593507234 +0.162,1,0.0828958882162892,0.8342091689551816,-1.340752478312082,0.9171050571714708 +0.164,1,0.08489500149511885,0.8302109637050734,-1.354376744324351,0.9151059652001923 +0.166,1,0.08691569919044412,0.8261695879595679,-1.367936217106201,0.9130852871500119 +0.168,1,0.08895787581107682,0.8220852520006039,-1.381431212690126,0.9110431278116807 +0.17,1,0.09102142467531353,0.8179581683696659,-1.394862050681345,0.9089795930449794 +0.172,1,0.0931062404511159,0.813788551136279,-1.408229046683594,0.9068947915873949 +0.174,1,0.09521222061667139,0.8095766154277348,-1.421532507944097,0.9047888360444063 +0.176,1,0.09733925001502079,0.8053225818512313,-1.43477277940791,0.902661831866252 +0.178,1,0.09948721786057077,0.8010266716656287,-1.447950193008633,0.9005138895261995 +0.18,1,0.1016560122183165,0.7966891083251946,-1.461065084130027,0.8983451205435111 +0.182,1,0.1038455200038418,0.7923101174701734,-1.474117791606015,0.8961556374740152 +0.184,1,0.1060556269833189,0.7878899269173223,-1.487108657720677,0.8939455539006412 +0.186,1,0.1082862177735087,0.7834287666504196,-1.500038028208257,0.8917149844239283 +0.188,1,0.1105371758417606,0.7789268688107436,-1.512906252253159,0.8894640446525042 +0.19,1,0.1128083835060125,0.774384467687522,-1.525713682489948,0.8871928511935345 +0.192,1,0.1150997299043263,0.7698017976709137,-1.538460651046364,0.88490152757524 +0.194,1,0.1174110933557253,0.7651790976937483,-1.551147522567888,0.8825901910494736 +0.196,1,0.1197423519115858,0.7605166085958401,-1.563774662499517,0.880258960507426 +0.198,1,0.122093384374056,0.7558145728344028,-1.576342434024323,0.8779079572084588 +0.2,1,0.1244640685114575,0.7510732349244313,-1.588851203428083,0.8755373034358889 +0.2,1,0.1244640685114575,0.7510732349244313,-1.588851203428083,0.8755373034358889 diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat b/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat new file mode 100644 index 0000000000000000000000000000000000000000..3cc5fa57da47eb6dc4bd07d71df6cbbe5ea9a676 GIT binary patch literal 776747 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 literal 0 HcmV?d00001 diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 76d90b5..dd37e2f 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -4,10 +4,10 @@ Pkg.activate(joinpath(@__DIR__, "..")) # test only MAT_v4 using Test +using JSON # include("../src/MAT.jl") include("../src/MAT_v4_Modelica.jl") -const mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" @@ -36,69 +36,63 @@ end @testset "Aclass" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) @test ac.positionStart == 0 @test ac.positionEnd == 71 end @testset "readVariableNames" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) # @show vn - @test length(vn.names) == 2490 + @test length(vn.names) == 11 @test vn.names[1] == "time" - @test vn.names[3] == "revolute.w" - @test vn.names[30] == "der(alignElastoBacklash.frame_a.r_0[1])" - @test vn.names[2490] == "world.z_label.color[3]" + @test vn.names[3] == "vel" + @test vn.names[11] == "grav" @test vn.positionStart == 71 - @test vn.positionEnd == 117126 + @test vn.positionEnd == 228 end @testset "getVariableIndex" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 - @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[30]) == 30 + @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[10]) == 10 end @testset "readVariableDescriptions" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) - @test length(vd.descriptions) == 2490 + @test length(vd.descriptions) == 11 @test vd.descriptions[1] == "Simulation time [s]" - @test vd.descriptions[3] == "First derivative of angle phi (relative angular velocity) [rad/s]" - @test vd.descriptions[30] == "Position vector from world frame to the connector frame origin, resolved in world frame" - @test vd.descriptions[2490] == "Color of cylinders" + @test vd.descriptions[3] == "velocity of ball" + @test vd.descriptions[11] == "gravity acceleration" end - - @testset "readDataInfo" begin - mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - ac = MAT_v4_Modelica.readAclass(mat1s) + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT_v4_Modelica.readDataInfo(ac,vd) # @show di.info[3] @test di.info[1]["isWithinTimeRange"] == -1 @test di.info[3]["locatedInData"] == 2 - @test di.info[30]["isInterpolated"] == 0 - @test di.info[2490]["isWithinTimeRange"] == 0 + @test di.info[4]["isInterpolated"] == 0 + @test di.info[11]["isWithinTimeRange"] == 0 end -using JSON -@testset "readVariable" begin - # mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - matbb = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(matbb) +@testset "readVariable: BouncingBall" begin + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) vn = MAT_v4_Modelica.readVariableNames(ac) vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT_v4_Modelica.readDataInfo(ac,vd) @@ -126,6 +120,38 @@ using JSON @test isapprox(vel[2], -0.981, rtol=1e-3) end +@testset "readVariable: FallingBodyBox" begin + mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat" + ac = MAT_v4_Modelica.readAclass(mat) + vn = MAT_v4_Modelica.readVariableNames(ac) + vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT_v4_Modelica.readDataInfo(ac,vd) + + # println(JSON.json(di.info, 2)) + + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") + # display(var) + ret = true + for i = 2:length(var)-1 #last time is duplicated + ret &= isapprox(var[i]-var[i-1], 0.002, rtol=1e-4) + end + @test ret == true + + #point-check values read from FallingBodyBox_res.csv + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") + @test isapprox(var[16], 0.002923239, rtol=1e-3) + + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1,1]") + @test isapprox(var[26], 0.983794001, rtol=1e-3) + + @test_throws ErrorException MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") + @test isapprox(var[33], -0.58818129, rtol=1e-3) + + var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") + @test isapprox(var[72], 0.935886479, rtol=1e-3) +end ; From fd4968338130ef5c72a8a46f23a03870364d80b8 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Mon, 27 Feb 2023 17:09:24 -0600 Subject: [PATCH 39/91] move test files --- .../BouncingBall/BouncingBall.mo | 0 .../BouncingBall/BouncingBall_dymola2021.mat | Bin 0 -> 1426 bytes .../BouncingBall/BouncingBall_res.csv | 0 .../BouncingBall/BouncingBall_res.mat | Bin .../FallingBodyBox/FallingBodyBox.mo | 0 .../FallingBodyBox/FallingBodyBox_res.csv | 0 .../FallingBodyBox/FallingBodyBox_res.mat | Bin 7 files changed, 0 insertions(+), 0 deletions(-) rename test/{Modelica => v4_Modelica}/BouncingBall/BouncingBall.mo (100%) create mode 100644 test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat rename test/{Modelica => v4_Modelica}/BouncingBall/BouncingBall_res.csv (100%) rename test/{Modelica => v4_Modelica}/BouncingBall/BouncingBall_res.mat (100%) rename test/{Modelica => v4_Modelica}/FallingBodyBox/FallingBodyBox.mo (100%) rename test/{Modelica => v4_Modelica}/FallingBodyBox/FallingBodyBox_res.csv (100%) rename test/{Modelica => v4_Modelica}/FallingBodyBox/FallingBodyBox_res.mat (100%) diff --git a/test/Modelica/BouncingBall/BouncingBall.mo b/test/v4_Modelica/BouncingBall/BouncingBall.mo similarity index 100% rename from test/Modelica/BouncingBall/BouncingBall.mo rename to test/v4_Modelica/BouncingBall/BouncingBall.mo diff --git a/test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat b/test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat new file mode 100644 index 0000000000000000000000000000000000000000..dca4f2e2722d74d4e2fdd678f340777238096b2d GIT binary patch literal 1426 zcmbVLO=}cE5N*FdbMqo3(&!~CqONlbX2%`z5XdnnLkOKo&!%OzXPKGovUu?a&;A$%cg^9L2cKYdiARMV;#oWY4S_tuyf>fKNLyA z>Q%my-exMUaxEBNc*1!sITt?XI^{fxIA<~Eyi4v-86=F%8H%-NO17YyRFjzuf+FIL zShS0HO{&Hk$&szfcshrbwhW7W_LX3Ta1eHtZe$77YzaRvYcFfr$=N|fL*<5vZjMZJ znc`KM__1oGD$@C2O7q2J$&?(k`Vmd8d?jhuQ9x&qq$*v9$an#^rP3}PkuO6T3mYe| zDRxbAD_qm<*e^1~Cku=P z_|ynS&g8*IZ1M)98O&fjc$mRwb68^huV5yBK8Gd7z%b-5(u#g&2Gn9R%T)>(+q(A1 z-94PFGxp3iF-jnTR}L#n?&X+$^@@N|=?+Q_ruD(zU@-gb`+@iF`qQ^?J7>PO@dBJ7 zafVjTX0KQKId$MoJ-*+E+jR!DJNgTH_2D)S&E!@#_PyQ1mz0Meoq4l&)&0@=Oue?L z*S3}8IF1`tKY5)u`+c}gXKyg?zTWzXebGEaD<=p7cQEn6d;V}@2X52BnZCOFo@SoR I^8d^E4JY?na{vGU literal 0 HcmV?d00001 diff --git a/test/Modelica/BouncingBall/BouncingBall_res.csv b/test/v4_Modelica/BouncingBall/BouncingBall_res.csv similarity index 100% rename from test/Modelica/BouncingBall/BouncingBall_res.csv rename to test/v4_Modelica/BouncingBall/BouncingBall_res.csv diff --git a/test/Modelica/BouncingBall/BouncingBall_res.mat b/test/v4_Modelica/BouncingBall/BouncingBall_res.mat similarity index 100% rename from test/Modelica/BouncingBall/BouncingBall_res.mat rename to test/v4_Modelica/BouncingBall/BouncingBall_res.mat diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox.mo b/test/v4_Modelica/FallingBodyBox/FallingBodyBox.mo similarity index 100% rename from test/Modelica/FallingBodyBox/FallingBodyBox.mo rename to test/v4_Modelica/FallingBodyBox/FallingBodyBox.mo diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox_res.csv b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.csv similarity index 100% rename from test/Modelica/FallingBodyBox/FallingBodyBox_res.csv rename to test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.csv diff --git a/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.mat similarity index 100% rename from test/Modelica/FallingBodyBox/FallingBodyBox_res.mat rename to test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.mat From b0ab45430973b382804eaab88e56313d42507e14 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 08:20:19 -0600 Subject: [PATCH 40/91] basic bones for reading OpenModelica MAT result files and tests --- src/MAT.jl | 4 +- src/MAT_v4_Modelica.jl | 69 +++- src/MAT_v4_pr132.jl | 322 ------------------ src/MAT_v4_pr164.jl | 59 ---- src/Matlab_MATFileFormatV4.pdf | Bin 23557 -> 0 bytes ...aUserGuide_v1.21.0-dev-211-gcd8969a225.pdf | Bin 85780 -> 0 bytes test/runtests.jl | 2 +- test/runtests_modelica.jl | 121 ++++--- test/runtests_pr132.jl | 37 -- test/runtests_pr164.jl | 48 --- test/v4/testcomplex_4.2c_SOL2.mat | Bin 176 -> 0 bytes test/v4/testdouble_4.2c_SOL2.mat | Bin 103 -> 0 bytes test/v4/testmatrix_4.2c_SOL2.mat | Bin 151 -> 0 bytes test/v4/testminus_4.2c_SOL2.mat | Bin 38 -> 0 bytes test/v4/testmulti_4.2c_SOL2.mat | Bin 240 -> 0 bytes test/v4/testonechar_4.2c_SOL2.mat | Bin 40 -> 0 bytes test/v4/testsparse_4.2c_SOL2.mat | Bin 223 -> 0 bytes test/v4/testsparsecomplex_4.2c_SOL2.mat | Bin 294 -> 0 bytes test/v4/teststring_4.2c_SOL2.mat | Bin 375 -> 0 bytes test/v4/teststringarray_4.2c_SOL2.mat | Bin 156 -> 0 bytes test/v4/testvec_4_GLNX86.mat | Bin 93 -> 0 bytes 21 files changed, 119 insertions(+), 543 deletions(-) delete mode 100644 src/MAT_v4_pr132.jl delete mode 100644 src/MAT_v4_pr164.jl delete mode 100644 src/Matlab_MATFileFormatV4.pdf delete mode 100644 src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf delete mode 100644 test/runtests_pr132.jl delete mode 100644 test/runtests_pr164.jl delete mode 100644 test/v4/testcomplex_4.2c_SOL2.mat delete mode 100644 test/v4/testdouble_4.2c_SOL2.mat delete mode 100644 test/v4/testmatrix_4.2c_SOL2.mat delete mode 100644 test/v4/testminus_4.2c_SOL2.mat delete mode 100644 test/v4/testmulti_4.2c_SOL2.mat delete mode 100644 test/v4/testonechar_4.2c_SOL2.mat delete mode 100644 test/v4/testsparse_4.2c_SOL2.mat delete mode 100644 test/v4/testsparsecomplex_4.2c_SOL2.mat delete mode 100644 test/v4/teststring_4.2c_SOL2.mat delete mode 100644 test/v4/teststringarray_4.2c_SOL2.mat delete mode 100644 test/v4/testvec_4_GLNX86.mat diff --git a/src/MAT.jl b/src/MAT.jl index 4b7a738..84294d7 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -28,9 +28,9 @@ using HDF5, SparseArrays include("MAT_HDF5.jl") include("MAT_v5.jl") -include("MAT_v4.jl") +include("MAT_v4_Modelica.jl") -using .MAT_HDF5, .MAT_v5, .MAT_v4 +using .MAT_HDF5, .MAT_v5, .MAT_v4_Modelica export matopen, matread, matwrite, @read, @write diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index 0e40e35..f62a1f5 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -1,6 +1,52 @@ +# A module to read MAT files written by OpenModelica tools + +# Copyright (C) 2023 Ben Conrad +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Given the often large size of mat files, each read option is atomic, opening and closing the file for every operation with state managed by the user. + +# The OpenModelica MATv4 file takes the basic v4 matrix format and adds some requirments on the contents and ordering of the matrices +# The format is described at https://openmodelica.org/doc/OpenModelicaUsersGuide/latest/technical_details.html#the-matv4-result-file-format, consisting of a series of matrices that first describe the data then store it. +#- Aclass: +# Aclass(1,:) is always Atrajectory +# Aclass(2,:) is 1.1 in OpenModelica +# Aclass(3,:) is empty +# Aclass(4,:) is either binTrans or binNormal, which determines if the data is stored striped (rows of values at a time instance) or tansposed (rows being a single variable across time) +#- name: +# a NxM matrix giving the names of the N variables as int8 characters +#- description: +# a NxM matrix giving the descriptions of the N variables as int8 characters +#- dataInfo: +# a Nx4 matrix describing the data of each variable, with +# dataInfo(i,1) locating the data in data_1 or data_2 +# dataInfo(i,2) providing the start index within the data_ matrix +# dataInfo(i,3) = 0 to indicate that the variable is interpolated +# dataInfo(i,4) = -1 to indicate that the variable is undefined outside the time range +#- data_1: +# is either an Nx1 matrix giving the variable's constant value, or Nx2 giving the start and end values +#- data_2: +# holds the values of the continuously-varying variables in rows of [time1, var1(@time1), var2(@time1), ...varN(@time1), time2, var1(@time2)...] + + module MAT_v4_Modelica -# The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices -# The first matrix, Aclass is narrowly defined function isLittleEndian(dtype) :: Bool #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... @@ -9,7 +55,6 @@ function isLittleEndian(dtype) :: Bool return M == 0 end - function dataFormat(type) :: DataType #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... T, P, O, M = digits(type; pad=4) @@ -55,6 +100,7 @@ function typeBytes(type::T)::Int where T<:DataType end end + struct Aclass filepath::String isTranspose::Bool @@ -106,8 +152,6 @@ function readAclass( filepath::String ) end #open end - -##### the NAME matrix######################################################################################################################## struct VariableNames # names::Vector{T}(undef,undef) where T<:AbstractString names::Vector{String} @@ -283,8 +327,6 @@ function readDataInfo(ac::Aclass, vd::VariableDescriptions) end #open end - - struct MatrixHeader type::Int nRows::Int @@ -294,6 +336,7 @@ struct MatrixHeader name::String format::DataType end + """ Reads the matix header, assuming matio's position is correct to read the header """ @@ -319,15 +362,13 @@ function readMatrixHeader!(matio::IOStream) :: MatrixHeader end """ -read one variable from the thing -to read a variable, we need its index, then to look up whether it is in data_1 or data_2 +read one variable from the file """ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) open(ac.filepath, "r", lock=false) do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix # read data1 header: - # data1HeaderStart = mark(matio) mh1 = readMatrixHeader!(matio) if mh1.name != "data_1" error("trying to read matrix [data_1] but read $matrixName") @@ -388,6 +429,12 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d end #open end +""" +all-in-one +""" +function readVariable(filepath::String, name::String) :: DataFrame + +end -end #module \ No newline at end of file +end #MAT_v4_Modelica \ No newline at end of file diff --git a/src/MAT_v4_pr132.jl b/src/MAT_v4_pr132.jl deleted file mode 100644 index 1952779..0000000 --- a/src/MAT_v4_pr132.jl +++ /dev/null @@ -1,322 +0,0 @@ -# Copied from pr132 of https://github.com/JuliaIO/MAT.jl/pull/132 - -# MAT_v4.jl -# Tools for reading MATLAB v4 files in Julia -# -# Copyright (C) 2012 Simon Kornblith -# Copyright (C) 2019 Victor Saase (modified from MAT_v5.jl) -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -# MATLAB's file format documentation can be found at -# http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf - -module MAT_v4_pr132 -using BufferedStreams, HDF5, SparseArrays -import Base: read, write, close -import HDF5: names - -round_uint8(data) = round.(UInt8, data) -complex_array(a, b) = complex.(a, b) - -mutable struct Matlabv4File <: HDF5.H5DataStore - ios::IOStream - swap_bytes::Bool - varnames::Dict{String, Int64} - - Matlabv4File(ios, swap_bytes) = new(ios, swap_bytes) -end - -const mLITTLE_ENDIAN = 0 -const mBIG_ENDIAN = 1 -const mVAX_DFLOAT = 2 -const mVAX_GFLOAT = 3 -const mGRAY = 4 - -const pTYPE = Dict( - 0 => Float64, - 1 => Float32, - 2 => Int32, - 3 => Int16, - 4 => UInt16, - 5 => UInt8 -) - -const tNUMERIC = 0 -const tTEXT = 1 -const tSPARSE = 2 - -const imagfREAL = 0 -const imagfCOMPLEX = 1 - -read_bswap(f::IO, swap_bytes::Bool, ::Type{T}) where T = swap_bytes ? bswap(read(f, T)) : read(f, T) - -function read_bswap(f::IO, swap_bytes::Bool, ::Type{T}, dim::Union{Int, Tuple{Vararg{Int}}}) where T - d = read!(f, Array{T}(undef, dim)) - if swap_bytes - for i = 1:length(d) - @inbounds d[i] = bswap(d[i]) - end - end - d -end - -function read_bswap(f::IO, swap_bytes::Bool, d::AbstractArray{T}) where T - readbytes!(f, reinterpret(UInt8, d)) - if swap_bytes - for i = 1:length(d) - @inbounds d[i] = bswap(d[i]) - end - end - d -end - -function checkv4(f::IO) - M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, false) - if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 - swap_bytes = false - return (true, swap_bytes) - else - seek(f, 0) - M, O, P, T, mrows, ncols, imagf, namlen = MAT_v4.read_header(f, true) - if 0<=M<=4 && O == 0 && 0<=P<=5 && 0<=T<=2 && mrows>=0 && ncols>=0 && 0<=imagf<=1 && namlen>0 - swap_bytes = true - return (true, swap_bytes) - end - end - return (false, false) -end - -# Read data type and number of bytes at the start of a data element -function read_header(f::IO, swap_bytes::Bool) - dtype = read_bswap(f, swap_bytes, Int32) - - M = div(rem(dtype, 10000), 1000) - O = div(rem(dtype, 1000), 100) - P = div(rem(dtype, 100), 10) - T = div(rem(dtype, 10), 1) - - mrows = read_bswap(f, swap_bytes, Int32) - ncols = read_bswap(f, swap_bytes, Int32) - imagf = read_bswap(f, swap_bytes, Int32) - namlen = read_bswap(f, swap_bytes, Int32) - - M, O, P, T, mrows, ncols, imagf, namlen -end - -# Read matrix data -function read_matrix(f::IO, swap_bytes::Bool) - M, O, P, T, mrows, ncols, imagf, namlen = read_header(f, swap_bytes) - if ncols == 0 || mrows == 0 - # If one creates a cell array using - # y = cell(m, n) - # then MATLAB will save the empty cells as zero-byte matrices. If one creates a - # empty cells using - # a = {[], [], []} - # then MATLAB does not save the empty cells as zero-byte matrices. To avoid - # surprises, we produce an empty array in both cases. - return ("", Matrix{Union{}}(undef, 0, 0)) - end - name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) - if T == tNUMERIC || T == tSPARSE - real_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) - if T == tNUMERIC && imagf == imagfCOMPLEX - imag_data = read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows)) - data = complex.(real_data, imag_data) - else - data = real_data - end - datamat = reshape(data, Int(mrows), Int(ncols)) - if T == tNUMERIC - return (name, datamat) - elseif T == tSPARSE - if size(datamat,2) == 3 - return (name, sparse(datamat[:,1], datamat[:,2], datamat[:,3])) - else - return (name, sparse(datamat[:,1], datamat[:,2], complex.(datamat[:,3], datamat[:,4]))) - end - end - elseif T == tTEXT - if mrows > 1 - charvec = UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols*mrows))) - charmat = reshape(charvec, Int(mrows), Int(ncols)) - data = [String(charmat[i,:]) for i in 1:mrows] - else - data = String(UInt8.(read_bswap(f, M==mBIG_ENDIAN, Vector{pTYPE[P]}(undef, ncols)))) - end - return (name, data) - end -end - -# Open MAT file for reading -matopen(ios::IOStream, endian_indicator::Bool) = Matlabv4File(ios, endian_indicator) - -# Read whole MAT file -function read(matfile::Matlabv4File) - seek(matfile.ios, 0) - vars = Dict{String, Any}() - while !eof(matfile.ios) - (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) - vars[name] = data - end - vars -end - -# Read only variable names -function getvarnames(matfile::Matlabv4File) - if !isdefined(matfile, :varnames) - seek(matfile.ios, 0) - matfile.varnames = varnames = Dict{String, Int64}() - while !eof(matfile.ios) - offset = position(matfile.ios) - M, O, P, T, mrows, ncols, imagf, namlen = read_header(matfile.ios, matfile.swap_bytes) - f = matfile.ios - - name = String(read_bswap(f, M==mBIG_ENDIAN, Vector{UInt8}(undef, namlen))[1:end-1]) - varnames[name] = offset - imag_offset = 0 - skip(f, mrows*ncols*sizeof(pTYPE[P])) - if imagf == imagfCOMPLEX - skip(f, mrows*ncols*sizeof(pTYPE[P])) - end - end - end - matfile.varnames -end - -names(matfile::Matlabv4File) = keys(getvarnames(matfile)) - -# Read a variable from a MAT file -function read(matfile::Matlabv4File, varname::String) - varnames = getvarnames(matfile) - if !haskey(varnames, varname) - error("no variable $varname in file") - end - seek(matfile.ios, varnames[varname]) - (name, data) = read_matrix(matfile.ios, matfile.swap_bytes) - data -end - -function colvals(A::AbstractSparseMatrix) - rows = rowvals(A) - cols = similar(rows) - m,n = size(A) - for i=1:n - for j in nzrange(A,i) - cols[j] = i - end - end - cols -end - -function write(parent::Matlabv4File, name::String, s) - M = Int(parent.swap_bytes) - O = 0 - P = 0 - for p=keys(pTYPE) - if eltype(s) == pTYPE[p] || eltype(s) == Complex{pTYPE[p]} - P = p - end - end - if pTYPE[P] != eltype(s) && Complex{pTYPE[P]} != eltype(s) && - !(s isa AbstractString && pTYPE[P] == Float64) && - !(s isa Vector{String} && pTYPE[P] == Float64) - error("invalid value type when writing v4 file") - end - if s isa AbstractSparseMatrix - T = tSPARSE - elseif s isa AbstractString || s isa Vector{String} - T = tTEXT - else - T = tNUMERIC - end - write(parent.ios, Int32(1000*M + 100*O + 10*P + T)) - - mrows = 1 - ncols = 1 - if s isa AbstractVector && !(s isa Vector{String}) - ncols = length(s) - elseif s isa AbstractMatrix - if s isa AbstractSparseMatrix - mrows = nnz(s) - ncols = 3 - if eltype(s) <: Complex - ncols = 4 - end - else - mrows, ncols = size(s) - end - elseif s isa Vector{String} - ncols = length(s[1]) - mrows = length(s) - elseif s isa AbstractString - ncols = length(s) - end - write(parent.ios, Int32(mrows)) - write(parent.ios, Int32(ncols)) - - imagf = 0 - if eltype(s) <: Complex && T == tNUMERIC - imagf = 1 - end - write(parent.ios, Int32(imagf)) - - namlen = length(name) + 1 - write(parent.ios, Int32(namlen)) - - write(parent.ios, Vector{UInt8}(name)) - write(parent.ios, UInt8(0)) - - if s isa AbstractArray && T == tNUMERIC - write(parent.ios, reshape(real(s), length(s))) - if imagf == 1 - write(parent.ios, reshape(imag(s), length(s))) - end - elseif s isa Number - write(parent.ios, real(s)) - if imagf == 1 - write(parent.ios, imag(s)) - end - elseif s isa AbstractString - floatarray = Float64.(Vector{UInt8}(s)) - write(parent.ios, floatarray) - elseif s isa Vector{String} - floatarray = Matrix{Float64}(undef, mrows, ncols) - for (i,strel) = enumerate(s) - floatarray[i,:] = Float64.(Vector{UInt8}(strel)) - end - write(parent.ios, floatarray) - elseif T == tSPARSE - rows = rowvals(s) - cols = colvals(s) - vals = nonzeros(s) - write(parent.ios, pTYPE[P].(rows)) - write(parent.ios, pTYPE[P].(cols)) - write(parent.ios, pTYPE[P].(real(vals))) - if eltype(s) <: Complex - write(parent.ios, pTYPE[P].(imag(vals))) - end - end -end - -# Close MAT file -close(matfile::Matlabv4File) = close(matfile.ios) - -end diff --git a/src/MAT_v4_pr164.jl b/src/MAT_v4_pr164.jl deleted file mode 100644 index 20f3479..0000000 --- a/src/MAT_v4_pr164.jl +++ /dev/null @@ -1,59 +0,0 @@ -module MAT_v4_pr164 - -import Base: read, write, close - -mutable struct Matlabv4File - ios::IOStream - varnames::Dict{String, Int64} - - Matlabv4File(ios) = new(ios) -end - -const V4_ELTYPE = [Float64, Float32, Int32, Int16, UInt16, UInt8] - -matopen(ios::IOStream) = Matlabv4File(ios) - -function unpack_header_type(type::Int32) - T, P, O, M = digits(type; pad=4) - iszero(O) || error("file is not a v4 MAT file (magic digit not 0)") - iszero(M) || error("only little endian v4 MAT currently supported") - iszero(T) || error("only full numeric matrices supported for v4 MAT files") - - P in 0:5 || error("invalid eltype digit in v4 MAT file: $P") - eltype = V4_ELTYPE[P+1] - - return eltype -end - -function read_one_mat!(mat::Matlabv4File) - type, mrows, ncols, imagf, namlen = read!(mat.ios, Vector{Int32}(undef, 5)) - eltype = unpack_header_type(type) - - iszero(imagf) || error("no imaginary matrix support") - - name = String(read!(mat.ios, Vector{UInt8}(undef, namlen-1))) - - # one null byte before start of matrix data - Base.read(mat.ios, UInt8) - - @show mrows, ncols - value = read!(mat.ios, Matrix{eltype}(undef, mrows, ncols)) - - return name => value -end - -function read(mat::Matlabv4File) - seekstart(mat.ios) - - results = Dict{String,Any}() - while !eof(mat.ios) - name, value = read_one_mat!(mat) - results[name] = value - end - - return results -end - -close(mat::Matlabv4File) = close(mat.ios) - -end # module diff --git a/src/Matlab_MATFileFormatV4.pdf b/src/Matlab_MATFileFormatV4.pdf deleted file mode 100644 index 8cbe7e72d5a33ded36139939783ed74ba7646eae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23557 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 M97s}9O-~*4Kg~M4yZ`_I diff --git a/src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf b/src/OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf deleted file mode 100644 index 9f0db31aadd2215647a07e05568a3cf7135473da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 85780 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 z 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 diff --git a/test/runtests.jl b/test/runtests.jl index de33258..451b468 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,6 +9,6 @@ using SparseArrays, LinearAlgebra include("read.jl") include("readwrite4.jl") include("write.jl") - +include("runtests_modelica.jl") diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index dd37e2f..3d0f8c8 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -1,52 +1,54 @@ -cd(joinpath(@__DIR__,"..")) + +# Test origins: +# test/v4_Modelica/BouncingBall/BouncingBall_res.mat - simulated with OpenModelica v1.19.0 +# test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat - simulated with Dymola v2021 +# test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with OpenModelica v1.19.0 +# These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents + import Pkg Pkg.activate(joinpath(@__DIR__, "..")) -# test only MAT_v4 using Test using JSON -# include("../src/MAT.jl") -include("../src/MAT_v4_Modelica.jl") - - +cd(joinpath(@__DIR__,"..")) +include("../src/MAT.jl") +# include("../src/MAT_v4_Modelica.jl") +#OpenModelica v1.19.0 +matBBO = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_res.mat") +matFBB = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_res.mat") +#Dymola v2021 -- not implemented +# matBBD = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") -# The Modelica MATv4 file takes the basic v4 Matrix format and adds some requirments to the contents and ordering of the matrices -# The first matrix, Aclass is narrowly defined @testset "isLittleEndian" begin - @test MAT_v4_Modelica.isLittleEndian(0) == true - @test MAT_v4_Modelica.isLittleEndian(1000) == false - @test MAT_v4_Modelica.isLittleEndian(2000) == false - @test MAT_v4_Modelica.isLittleEndian(3000) == false + @test MAT.MAT_v4_Modelica.isLittleEndian(0) == true + @test MAT.MAT_v4_Modelica.isLittleEndian(1000) == false + @test MAT.MAT_v4_Modelica.isLittleEndian(2000) == false + @test MAT.MAT_v4_Modelica.isLittleEndian(3000) == false end @testset "dataFormat" begin - @test MAT_v4_Modelica.dataFormat(0000) <: Float64 - @test MAT_v4_Modelica.dataFormat(0010) <: Float32 - @test MAT_v4_Modelica.dataFormat(0020) <: Int32 - @test MAT_v4_Modelica.dataFormat(0030) <: Int16 - @test MAT_v4_Modelica.dataFormat(0040) <: UInt16 - @test MAT_v4_Modelica.dataFormat(0050) <: UInt8 + @test MAT.MAT_v4_Modelica.dataFormat(0000) <: Float64 + @test MAT.MAT_v4_Modelica.dataFormat(0020) <: Int32 + @test MAT.MAT_v4_Modelica.dataFormat(0030) <: Int16 + @test MAT.MAT_v4_Modelica.dataFormat(0040) <: UInt16 + @test MAT.MAT_v4_Modelica.dataFormat(0050) <: UInt8 end @testset "typeBytes" begin - @test MAT_v4_Modelica.typeBytes(Int32) == 4 + @test MAT.MAT_v4_Modelica.typeBytes(Int32) == 4 end - @testset "Aclass" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) @test ac.positionStart == 0 @test ac.positionEnd == 71 end - @testset "readVariableNames" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) # @show vn @test length(vn.names) == 11 @test vn.names[1] == "time" @@ -56,21 +58,17 @@ end @test vn.positionEnd == 228 end - @testset "getVariableIndex" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 - @test MAT_v4_Modelica.getVariableIndex(vn, vn.names[10]) == 10 + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 + @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[10]) == 10 end - @testset "readVariableDescriptions" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) @test length(vd.descriptions) == 11 @test vd.descriptions[1] == "Simulation time [s]" @test vd.descriptions[3] == "velocity of ball" @@ -78,11 +76,10 @@ end end @testset "readDataInfo" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) - di = MAT_v4_Modelica.readDataInfo(ac,vd) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) # @show di.info[3] @test di.info[1]["isWithinTimeRange"] == -1 @test di.info[3]["locatedInData"] == 2 @@ -91,45 +88,43 @@ end end @testset "readVariable: BouncingBall" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/BouncingBall/BouncingBall_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) - di = MAT_v4_Modelica.readDataInfo(ac,vd) + ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) # println(JSON.json(di.info, 2)) #get the data 1/2 info - eff = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 + eff = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 @test length(eff) == 2 @test eff[1] ≈ 0.77 @test eff[2] ≈ 0.77 - grav = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 + grav = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 @test length(grav) == 2 @test grav[1] ≈ 9.81 @test grav[2] ≈ 9.81 - time = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # data0 + time = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # data0 @test all(isapprox.(time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) - height = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 + height = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 @test isapprox(height[1], 111, rtol=1e-3) @test isapprox(height[2], 110.9509, rtol=1e-3) - vel = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 + vel = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 @test isapprox(vel[2], -0.981, rtol=1e-3) end @testset "readVariable: FallingBodyBox" begin - mat = "W:/sync/mechgits/library/julia/MAT.jl/test/Modelica/FallingBodyBox/FallingBodyBox_res.mat" - ac = MAT_v4_Modelica.readAclass(mat) - vn = MAT_v4_Modelica.readVariableNames(ac) - vd = MAT_v4_Modelica.readVariableDescriptions(ac,vn) - di = MAT_v4_Modelica.readDataInfo(ac,vd) + ac = MAT.MAT_v4_Modelica.readAclass(matFBB) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) # println(JSON.json(di.info, 2)) - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # display(var) ret = true for i = 2:length(var)-1 #last time is duplicated @@ -138,18 +133,18 @@ end @test ret == true #point-check values read from FallingBodyBox_res.csv - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") @test isapprox(var[16], 0.002923239, rtol=1e-3) - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1,1]") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1,1]") @test isapprox(var[26], 0.983794001, rtol=1e-3) - @test_throws ErrorException MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + @test_throws ErrorException MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") @test isapprox(var[33], -0.58818129, rtol=1e-3) - var = MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") @test isapprox(var[72], 0.935886479, rtol=1e-3) end diff --git a/test/runtests_pr132.jl b/test/runtests_pr132.jl deleted file mode 100644 index ee4eea2..0000000 --- a/test/runtests_pr132.jl +++ /dev/null @@ -1,37 +0,0 @@ -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -# test only MAT_v4 -using Test -include("../src/MAT.jl") -include("../src/MAT_v4_pr132.jl") - -mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - -@testset "checkv4" begin - # function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Bool, ff::Bool, compress::Bool) - # function matopen(fname::AbstractString, mode::AbstractString; compress::Bool = false) - # mat = MAT.matopen(mat1s, "r" ) - - rawfid = open(mat1s, "r") - (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) - close(rawfid) - - @test isv4 == true -end - -rawfid = open(mat1s, "r") -(isv4, swap_bytes) = MAT_v4.checkv4(rawfid) -mat = MAT_v4_pr132.matopen(rawfid, swap_bytes ) - -@testset "getvarnames" begin - # function getvarnames(matfile::Matlabv4File) - @show gvn = MAT_v4_pr132.getvarnames(mat) - # gvn = MAT_v4.getvarnames(mat) = Dict("name" => 71, "Aclass" => 0, "dataInfo" => 448328, "data_2" => 501296, "data_1" => 488197, "description" => 117126) -# ...and this is not correct - @test false -end - - -close(rawfid) \ No newline at end of file diff --git a/test/runtests_pr164.jl b/test/runtests_pr164.jl deleted file mode 100644 index dea51b4..0000000 --- a/test/runtests_pr164.jl +++ /dev/null @@ -1,48 +0,0 @@ -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -# test only MAT_v4 -using Test -include("../src/MAT.jl") -include("../src/MAT_v4_pr164.jl") - -mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - - - -@testset "unpack_header" begin - rawfid = open(mat1s, "r") - # (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) - - # From 'Version-4-MAT-File-Format.pdf': - # Each matrix starts with a fixed-length 20-byte header that contains information describing certain attributes of the Matrix. - # The 20-byte header consists of five long (4-byte) integers: - @show type, mrows, ncols, imagf, namlen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(type; pad=4) - # P indicates which format the data is stored: 5 = 8bit uint - # T indicates matrix type: 1 = text matrix - - @show eltype = MAT_v4_pr164.unpack_header_type(type) - - - close(rawfid) - - # @test isv4 == true -end - -# rawfid = open(mat1s, "r") -# (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) -# mat = MAT_v4.matopen(rawfid, swap_bytes ) - -# @testset "getvarnames" begin -# # function getvarnames(matfile::Matlabv4File) -# @show gvn = MAT_v4.getvarnames(mat) -# # gvn = MAT_v4.getvarnames(mat) = Dict("name" => 71, "Aclass" => 0, "dataInfo" => 448328, "data_2" => 501296, "data_1" => 488197, "description" => 117126) -# # ...and this is not correct -# @test false -# end - - -# close(rawfid) \ No newline at end of file diff --git a/test/v4/testcomplex_4.2c_SOL2.mat b/test/v4/testcomplex_4.2c_SOL2.mat deleted file mode 100644 index 36621b25c08f18e4545100c6eaec015123c3bf9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmZQzV1B{Cz`zK^oKTvlB(=CCIX|}`C$)mX{sT}H2<)FNn3q;>eb#2;GBsn7820@T p{+azyc_{zfo>i5WKJ&V`pz2S<^g~R6n{x&x4mWop0dqG(`$IPYCV6bhD=3Sn-kr dCzQ{hRiznf5$7NT6&L-x`{nNCr4Eu1c>u#Y9lihn diff --git a/test/v4/testmatrix_4.2c_SOL2.mat b/test/v4/testmatrix_4.2c_SOL2.mat deleted file mode 100644 index 3698c8853b46d4a42194002523b57fddfb225908..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151 zcmZQzV1B{Cz`zW|tUwF`+$E{SCAo0_8%Z(4iJjLfdiEf6^2tVdAI64fzvu`z diff --git a/test/v4/testminus_4.2c_SOL2.mat b/test/v4/testminus_4.2c_SOL2.mat deleted file mode 100644 index cc207ed9f32095f39b7690e2dc1e2dc0d55ee8e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38 kcmZQzV1B{Cz`zK_K#GB@B(=CCH#4uam|_11kN^V%0A!m6lK=n! diff --git a/test/v4/testmulti_4.2c_SOL2.mat b/test/v4/testmulti_4.2c_SOL2.mat deleted file mode 100644 index 9c6ba793cf41bf36447ab7a1890447fe5e939614..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmZQzV1B{Cz`zW|tUwF`Oo>TXrytD# diff --git a/test/v4/testonechar_4.2c_SOL2.mat b/test/v4/testonechar_4.2c_SOL2.mat deleted file mode 100644 index cdb4191c7d2eb0ac66d4f6add250e1f6a604d892..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40 mcmZQzV1CKKz`zK_K#GBoBe96VA*KN&#sC0t%m%jr diff --git a/test/v4/testsparse_4.2c_SOL2.mat b/test/v4/testsparse_4.2c_SOL2.mat deleted file mode 100644 index 55cbd3c1b3d65630beae47832ffbcc7a6fd43354..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmZQzV1C8Gz`y~-%s>nR+$E{SCB+4aMa8KM_8%Z(4iJjL0i+NJVB)xFLh2mArZB+G Wa}aKwqPFu=`o5P%3ch@jFi^D)#D&<~Y{yA#GIl?DK-2^cH@ diff --git a/test/v4/teststring_4.2c_SOL2.mat b/test/v4/teststring_4.2c_SOL2.mat deleted file mode 100644 index 137561e1f636d7b08959e43e969a6984eb7a3b37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 375 zcmZ{gO%6an423I}j^YxvVJTQKEJbWAm;*SP>$ruVzPB0Cq-nqQwbP79e2PePdwTn0 zi61w=`E_0<(adUEA-dyDRLQ$>X9acO7HmP(fmx@H{cwJe*OdBxH||jjyl4?yTB eFvZ{y=>Xxw^u;tl_z+ Date: Tue, 28 Feb 2023 08:28:28 -0600 Subject: [PATCH 41/91] uncomment --- src/MAT.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/MAT.jl b/src/MAT.jl index 84294d7..933dbfb 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -189,12 +189,12 @@ end ### v0.10.0 deprecations ### -# import HDF5: exists -# export exists -# @noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) -# Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) -# return haskey(matfile, varname) -# end +import HDF5: exists +export exists +@noinline function exists(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}, varname::String) + Base.depwarn("`exists(matfile, varname)` is deprecated, use `haskey(matfile, varname)` instead.", :exists) + return haskey(matfile, varname) +end @noinline function Base.names(matfile::Union{MAT_v5.Matlabv5File,MAT_HDF5.MatlabHDF5File}) Base.depwarn("`names(matfile)` is deprecated, use `keys(matfile)` instead.", :names) return keys(matfile) From 746eff1845f2eb2c7877691022dde423800b7829 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 08:31:11 -0600 Subject: [PATCH 42/91] remove more dev files --- test/runtests_modelica_manual.jl | 126 ---------------- test/runtests_modelica_readMatrix.jl | 209 --------------------------- 2 files changed, 335 deletions(-) delete mode 100644 test/runtests_modelica_manual.jl delete mode 100644 test/runtests_modelica_readMatrix.jl diff --git a/test/runtests_modelica_manual.jl b/test/runtests_modelica_manual.jl deleted file mode 100644 index d3ee86c..0000000 --- a/test/runtests_modelica_manual.jl +++ /dev/null @@ -1,126 +0,0 @@ -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -# test only MAT_v4 -using Test -# include("../src/MAT.jl") -# include("../src/MAT_v4_Modelica.jl") - -mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - - -@testset "manual" begin - rawfid = open(mat1s, "r") - - # This reader is collected from two references: - # (isv4, swap_bytes) = MAT_v4.checkv4(rawfid) - # Matlab_MATFileFormatV4.pdf from https://www.eiscat.se/wp-content/uploads/2016/03/Version-4-MAT-File-Format.pdf - # OpenModelica_TheMATv4ResultFileFormat_OpenModelicaUserGuide_v1.21.0-dev-211-gcd8969a225.pdf from https://www.openmodelica.org/doc/OpenModelicaUsersGuide/latest/technical_details.html - - # Start by reading the header, following Matlab: - # Each matrix starts with a fixed-length 20-byte header that contains information describing certain attributes of the Matrix. - # The 20-byte header consists of five long (4-byte) integers: - @show type, nrows, ncols, imagf, namelen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(type; pad=4) - - # M indicates the numeric format of binary numbers on the machine that wrote the file. - # Use this table to determine the number to use for your machine: - # 0 IEEE Little Endian (PC, 386, 486, DEC Risc) - # 1 IEEE Big Endian (Macintosh, SPARC, Apollo,SGI, HP 9000/300, other Motorola) - # 2 VAX D-float - # 3 VAX G-float - # 4 Cray - @enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 - @show numberFormat(M) - - # O is always 0 (zero) and is reserved for future use. - # @show O - - # P indicates which format the data is stored: - # 0 double-precision (64-bit) floating point numbers - # 1 single-precision (32-bit) floating point numbers - # 2 32-bit signed integers - # 3 16-bit signed integers - # 4 16-bit unsigned integers - # 5 8-bit unsigned integers - @enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 - @show dataFormat(P) - @test dataFormat(P) == uint8 - - # T indicates matrix type: 1 = text matrix - @enum matrixFormat numericFull=0 text=1 spars=2 - @show matrixFormat(T) - - # nrows The row dimension contains an integer with the number of rows in the matrix. - @show nrows - # ncols The column dimension contains an integer with the number of columns in the matrix. - @show ncols - # imagf The imaginary flag is an integer whose value is either 0 or 1. If 1, then the matrix has an imaginary part. If 0, there is only real data. - @show imagf - # namelen The name length contains an integer with 1 plus the length of the matrix name. - @show namelen - - # Immediately following the fixed length header is the data whose length is dependent on the variables in the fixed length header: - # name The matrix name consists of namlen ASCII bytes, the last one of which must be a null character (’\0’ ). - @show nameuint = read!(rawfid, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - # pop!(nameuint) #trim \0 - # @show name = String(nameuint) - @show name = replace(String(nameuint), '\0'=>"") - - # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. - # @show realint = read!(rawfid, Vector{UInt8}(undef, 1*nrows*ncols)) # UInt8 from P is 8 bytes long - # @show realint = read!(rawfid, Vector{UInt16}(undef, 8*nrows*ncols)) # UInt8 from P is 8 bytes long - # @show realint = read(rawfid, 32) # UInt8 from P is 8 bytes long - @show realint = read!(rawfid, Matrix{UInt8}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - - # imag Imaginary part of the matrix, if any. If the imaginary flag imagf is nonzero, the imaginary part of a matrix is placed here. It is stored in the same manner as the real data. - if imagf==1 - @show imagint = read!(rawfid, Vector{UInt8}(undef, nrows*ncols)) # read the full namelen to make the pointer ready to read the data - end - - # The variables stored in the MAT-file are (in the order required by OpenModelica): - # Aclass - # • Aclass(1,:) is always Atrajectory - # • Aclass(2,:) is 1.1 in OpenModelica - # • Aclass(3,:) is empty - # • Aclass(4,:) is either binTrans or binNormal - - Aclass1 = replace(String(realint[1,:]), '\0'=>"") - Aclass2 = replace(String(realint[2,:]), '\0'=>"") - Aclass3 = replace(String(realint[3,:]), '\0'=>"") - Aclass4 = replace(String(realint[4,:]), '\0'=>"") - @test Aclass1 == "Atrajectory" - @test Aclass2 == "1.1" - @test isempty(Aclass3) - @test Aclass4 == "binNormal" || Aclass4 == "binTrans" - - #now to read the next matrix header... - # name: - # Is an n x m character (int8) matrix, where n is the number of variables stored in the result-file (including time). m is the length of the longest variable. OpenModelica stores the trailing part of the name as NIL bytes (0) whereas other tools use spaces for the trailing part. - @show type, nrows, ncols, imagf, namelen = read!(rawfid, Vector{Int32}(undef, 5)) # int32=4byte * 5 = 20byte - T, P, O, M = digits(type; pad=4) - @show numberFormat(M) - @show matrixFormat(T) - @show dataFormat(P) - - @show nrows - @show ncols - @show imagf - @show namelen - nameuint = read!(rawfid, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - @show name = replace(String(nameuint), '\0'=>"") - - realint = read!(rawfid, Matrix{UInt8}(undef, nrows,ncols)) # UInt8 from P is 8 bytes long - @show varname1 = replace(String(realint[:,1]), '\0'=>"") # note :,1 implicit transpose - @show varname2 = replace(String(realint[:,2]), '\0'=>"") # note :,1 implicit transpose - @show varname3 = replace(String(realint[:,3]), '\0'=>"") # note :,1 implicit transpose - @show varname4 = replace(String(realint[:,4]), '\0'=>"") # note :,1 implicit transpose - @show varname5 = replace(String(realint[:,5]), '\0'=>"") # note :,1 implicit transpose - - close(rawfid) -end - - diff --git a/test/runtests_modelica_readMatrix.jl b/test/runtests_modelica_readMatrix.jl deleted file mode 100644 index 742e9e5..0000000 --- a/test/runtests_modelica_readMatrix.jl +++ /dev/null @@ -1,209 +0,0 @@ -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - -# test only MAT_v4 -using Test -# include("../src/MAT.jl") -# include("../src/MAT_v4_Modelica.jl") - -mat1s = "W:/sync/mechgits/library/julia/ConvenientModelica/test/ViseHammer_result_1s/ViseHammer_res.mat" - -struct MATrix - name::String - # data::T where T<:AbstractMatrix - data::Any - # type -end - -@enum numberFormat little=0 big=1 vaxD=2 vaxG=3 cray=4 -@enum dataFormat double=0 single=1 int32=2 int16=3 uint16=4 uint8=5 -@enum matrixFormat numericFull=0 text=1 spars=2 - -""" -Read a single data matrix from the opened MAT file -""" -function readMATMatrix(ios::IOStream) - @show startP = position(ios) - # The 20-byte header consists of five long (4-byte) integers: - type, nrows, ncols, imagf, namelen = read!(ios, Vector{Int32}(undef, 5)) # 32bits=4byte, * 5 qty - - #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... - T, P, O, M = digits(type; pad=4) - - nameuint = read!(ios, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") - - # real Real part of the matrix consists of nrows ∗ ncols numbers in the format specified by the P element of the type flag. The data is stored column-wise such that the second column follows the first column, etc. - realint = [] - @show dataFormat(P) - if dataFormat(P) == uint8 - realint = read!(ios, Matrix{UInt8}(undef, nrows,ncols)) - elseif dataFormat(P) == uint16 - realint = read!(ios, Matrix{UInt16}(undef, nrows,ncols)) - elseif dataFormat(P) == int32 - realint = read!(ios, Matrix{Int32}(undef, nrows,ncols)) - elseif dataFormat(P) == single - realint = read!(ios, Matrix{Float32}(undef, nrows,ncols)) - elseif dataFormat(P) == double - realint = read!(ios, Matrix{Float64}(undef, nrows,ncols)) - else - error("Unknown data format for P=$P") - end - - # imag Imaginary part of the matrix, if any. If the imaginary flag imagf is nonzero, the imaginary part of a matrix is placed here. It is stored in the same manner as the real data. - if imagf==1 - error("imaginary not implemented") - # if dataFormat(P) - # @show imagint = read!(ios, Vector{UInt8}(undef, nrows*ncols)) # read the full namelen to make the pointer ready to read the data - end - - @show matrixName - @show endP = position(ios) - - return MATrix(matrixName, realint) -end - -""" - # The variables stored in the MAT-file are (in the order required by OpenModelica): - # Aclass - # • Aclass(1,:) is always Atrajectory - # • Aclass(2,:) is 1.1 in OpenModelica - # • Aclass(3,:) is empty - # • Aclass(4,:) is either binTrans or binNormal - - -""" -function parseAclass(mat::MATrix) - # @show mat - # Aclass1 = replace(String(mat.data[1,:]), '\0'=>"") - # Aclass2 = replace(String(data[2,:]), '\0'=>"") - # Aclass3 = replace(String(data[3,:]), '\0'=>"") - # Aclass4 = replace(String(data[4,:]), '\0'=>"") - Aclass = [ replace(String(mat.data[1,:]), '\0'=>""), - replace(String(mat.data[2,:]), '\0'=>""), - replace(String(mat.data[3,:]), '\0'=>""), - replace(String(mat.data[4,:]), '\0'=>"") ] - return Aclass -end - - -""" -name -Is an n x m character (int8) matrix, where n is the number of variables stored in the result-file (including time). -m is the length of the longest variable. -OpenModelica stores the trailing part of the name as NIL bytes (0) whereas other tools use spaces for the trailing part. -""" -function parseVariableNames(mat::MATrix) - varNames = [] - m,n = size(mat.data) - for i in 1:n - push!(varNames, replace(String(mat.data[:,i]), '\0'=>"")) - end - return varNames -end - -""" -Find the index of the given variable, returning -1 if not found. -""" -function getVariableIndex(variableNames, name) - vecAll = findall( x->x==name, variableNames) - n = length(vecAll) - - if isempty(vecAll) == true - return -1 - else - if n>1 - error("Found $n instances of variable [$name], but variables should be unique.") - end - return vecAll[1] - end -end - - -""" -description -Is an n x m character (int8) matrix containing the comment-string corresponding to the variable in the name matrix -""" -function parseVariableDescriptions(mat::MATrix) - varDesc = [] - m,n = size(mat.data) - for i in 1:n - push!(varDesc, replace(String(mat.data[:,i]), '\0'=>"")) - end - return varDesc -end - -""" -dataInfo provides indicies to access variable data - -dataInfo -Is an n x 4 integer matrix containing information for each variable (in the same order as the name and description matrices). -• dataInfo(i,1) is 1 or 2, saying if variable i is stored in the data_1 or data_2 matrix. If it is 0, it is the abscissa (time variable). -• dataInfo(i,2) contains the index in the data_1 or data_2 matrix. The index is 1-based and may contain several variables pointing to the same row (alias variables). A negative value means that the variable is a negated alias variable. -• dataInfo(i,3) is 0 to signify linear interpolation. In other tools the value is the number of times differentiable this variable is, which may improve plotting. -• dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. -""" -function parseDataInfo(mat::MATrix, varIndex::Int) - # @show mat.data[:,varIndex] - return Dict("locatedInData"=>mat.data[1,varIndex], "indexInData"=>mat.data[2,varIndex], "isInterpolated"=>mat.data[3,varIndex], "isWithinTimeRange"=>mat.data[4,varIndex] ) -end - -""" -data_1 -If it is an n x 1 matrix it contains the values of parameters. If it is an n x 2 matrix, the first and second column -signify start and stop-values. -""" -function parseData1(mat::MATrix, varIndex::Int) - @show size(mat.data) - @show mat.data[varIndex,:] -end - - -@testset "matrix reader" begin - matio = open(mat1s, "r") - seekstart(matio) - ret = readMATMatrix(matio) - Aclass = parseAclass(ret) - @test Aclass[1] == "Atrajectory" - @test Aclass[2] == "1.1" - @test isempty(Aclass[3]) == true - @test Aclass[4] == "binTrans" || Aclass[4] == "binNormal" - - ret = readMATMatrix(matio) # name - variableNames = parseVariableNames(ret) - @test variableNames[1] == "time" - @test variableNames[2490] == "world.z_label.color[3]" - @test getVariableIndex(variableNames, "time") == 1 - - - @show position(matio) - ret = readMATMatrix(matio) # description - variableDescriptions = parseVariableDescriptions(ret) - @test variableDescriptions[2490] == "Color of cylinders" - - # @allocated readMATMatrix(matio) # dataInfo - ret = readMATMatrix(matio) # dataInfo - @show dataInfo = parseDataInfo(ret, 1) - - ret = readMATMatrix(matio) # data_1 - - - # itime = getVariableIndex(variableNames, "time") - # # ditime = parse - # parseData1(ret, itime) - - # parseData1(ret, iphi) - # # parseData1(ret, ihfaf1) - - # iphi = getVariableIndex(variableNames, "revolute.phi") - # ihfaf1 = getVariableIndex(variableNames, "head.frame_a.f[1]") - - ret = readMATMatrix(matio) # data_2 - close(matio) - - @test true - -end - - From 7f581d5507f397e60680f8cf02a08b42ad248374 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 08:34:01 -0600 Subject: [PATCH 43/91] one more --- test/readwrite4.jl | 52 ---------------------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 test/readwrite4.jl diff --git a/test/readwrite4.jl b/test/readwrite4.jl deleted file mode 100644 index c751125..0000000 --- a/test/readwrite4.jl +++ /dev/null @@ -1,52 +0,0 @@ -using MAT, Test - -function check(filename, result) - matfile = matopen(filename) - for (k, v) in result - @test exists(matfile, k) - got = read(matfile, k) - if !isequal(got, v) || (typeof(got) != typeof(v) && (!isa(got, String) || !(isa(v, String)))) - close(matfile) - error(""" - Data mismatch reading $k from $filename ($format) - - Got $(typeof(got)): - - $(repr(got)) - - Expected $(typeof(v)): - - $(repr(v)) - """) - end - end - @test union!(Set(), names(matfile)) == union!(Set(), keys(result)) - close(matfile) - - mat = matread(filename) - if !isequal(mat, result) - error(""" - Data mismatch reading $filename ($format) - - Got: - - $(repr(mat)) - - Expected: - - $(repr(result)) - """) - close(matfile) - return false - end - - return true -end -cd(dirname(@__FILE__)) -for filename in readdir("v4") - #println("testing $filename") - d = matread("v4/$filename") - matwrite4("v4/tmp.mat", d) - check("v4/tmp.mat", d) - rm("v4/tmp.mat") -end From 15fa873e2dfd70adf69382a71f295b70485d798c Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 08:38:29 -0600 Subject: [PATCH 44/91] cleaning up --- src/MAT.jl | 1 - test/runtests.jl | 8 -------- 2 files changed, 9 deletions(-) diff --git a/src/MAT.jl b/src/MAT.jl index 933dbfb..73d174f 100644 --- a/src/MAT.jl +++ b/src/MAT.jl @@ -59,7 +59,6 @@ function matopen(filename::AbstractString, rd::Bool, wr::Bool, cr::Bool, tr::Boo error("File \"$filename\" is too small to be a supported MAT file") end - # Check for MAT v5 file seek(rawfid, 124) version = read(rawfid, UInt16) diff --git a/test/runtests.jl b/test/runtests.jl index 451b468..a3fdd53 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,14 +1,6 @@ - -cd(joinpath(@__DIR__,"..")) -import Pkg -Pkg.activate(joinpath(@__DIR__, "..")) - - using SparseArrays, LinearAlgebra include("read.jl") include("readwrite4.jl") include("write.jl") include("runtests_modelica.jl") - - From 9ddbecfc44f09b0dedbec1c3305b5ba9d4e6b081 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 09:42:01 -0600 Subject: [PATCH 45/91] add Pkg dependency --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 9535a1d..01bdac1 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.10.5" BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] From 7500f9f318120b031cf6227673bac91216f6793f Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 09:48:36 -0600 Subject: [PATCH 46/91] remove JSON --- test/runtests_modelica.jl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 3d0f8c8..8c154dc 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -9,7 +9,6 @@ import Pkg Pkg.activate(joinpath(@__DIR__, "..")) using Test -using JSON cd(joinpath(@__DIR__,"..")) include("../src/MAT.jl") # include("../src/MAT_v4_Modelica.jl") @@ -93,8 +92,6 @@ end vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) - # println(JSON.json(di.info, 2)) #get the data 1/2 info - eff = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 @test length(eff) == 2 @test eff[1] ≈ 0.77 @@ -122,8 +119,6 @@ end vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) - # println(JSON.json(di.info, 2)) - var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "time") # display(var) ret = true From 94232f7dbf1e558acfe5c405350cd8641fd8d3aa Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 10:02:59 -0600 Subject: [PATCH 47/91] pull out open kwarg lock=false to make Julia 1.3 happy --- src/MAT_v4_Modelica.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index f62a1f5..cbfde26 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -112,7 +112,7 @@ end Reads the Aclass matrix, returing if the data is stored binNormal or binTranspose """ function readAclass( filepath::String ) - open(filepath, "r", lock=false) do matio + open(filepath, "r") do matio seekstart(matio) # always start from the start, don't assume the position startP = position(matio) @@ -160,7 +160,7 @@ struct VariableNames end function readVariableNames(ac::Aclass) - open(ac.filepath, "r", lock=false) do matio + open(ac.filepath, "r") do matio seek(matio, ac.positionEnd) #skip over Aclass startP = position(matio) @@ -228,7 +228,7 @@ struct VariableDescriptions end function readVariableDescriptions(ac::Aclass, vn::VariableNames) - open(ac.filepath, "r", lock=false) do matio + open(ac.filepath, "r") do matio seek(matio, vn.positionEnd) #this follows the VariableNames matrix startP = position(matio) @@ -287,7 +287,7 @@ Is an n x 4 integer matrix containing information for each variable (in the same dataInfo(i,4) is -1 in OpenModelica to signify that the value is not defined outside the time range. 0 keeps the first/last value when going outside the time range and 1 performs linear interpolation on the first/last two points. """ function readDataInfo(ac::Aclass, vd::VariableDescriptions) - open(ac.filepath, "r", lock=false) do matio + open(ac.filepath, "r") do matio seek(matio, vd.positionEnd) #this follows the VariableNames matrix startP = position(matio) @@ -365,7 +365,7 @@ end read one variable from the file """ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, di::DataInfo, name::String) - open(ac.filepath, "r", lock=false) do matio + open(ac.filepath, "r") do matio seek(matio, di.positionEnd) #this follows the VariableNames matrix # read data1 header: From 714314c8f4781742c6676a416d323150ceeed3dc Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 15:44:49 -0600 Subject: [PATCH 48/91] add all-in-one readVariable() --- src/MAT_v4_Modelica.jl | 54 ++++++++++++++++++++++++++++++--------- test/runtests_modelica.jl | 20 +++++++++++++-- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index cbfde26..55eac3b 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -47,6 +47,7 @@ module MAT_v4_Modelica +using DataFrames function isLittleEndian(dtype) :: Bool #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... @@ -370,31 +371,26 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d # read data1 header: mh1 = readMatrixHeader!(matio) - if mh1.name != "data_1" - error("trying to read matrix [data_1] but read $matrixName") - end + @assert mh1.name == "data_1" "trying to read matrix [data_1] but read $matrixName" data1MatrixStart = mark(matio) try toskip = mh1.nRows*mh1.nCols*typeBytes(mh1.format) #817*2*8 = 13072, 488197+20+7+13072 = 501296 skip(matio, toskip ) catch e - error("caught error $e while reading $ac.filepath") + throw( ErrorException("Caught error $e while reading $ac.filepath") ) end # read data2 header: mh2 = readMatrixHeader!(matio) - if mh2.name != "data_2" - error("trying to read matrix [data_2] but read $(mh2.name)") - end + @assert mh2.name == "data_2" "trying to read matrix [data_2] but read $(mh2.name)" data2MatrixStart = mark(matio) #with the positions marked, read the desired variable - # println("\nlocate variable [$name]:") varInd = getVariableIndex(vn, name) if varInd < 1 - throw( ErrorException("Variable [$name] not found in file [$(ac.filepath)]") ) + throw( ArgumentError("Variable [$name] not found in file [$(ac.filepath)]") ) end if di.info[varInd]["locatedInData"] == 1 #data_1 @@ -421,20 +417,54 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d end return readns else - error("reading binTranspose not implemented, lack test data") + throw(ErrorException("reading binTranspose not implemented, lack test data") ) end else - error("variable [$name] is located in an unknown location") + throw(ErrorException("variable [$name] is located in an unknown location") ) end end #open end + """ -all-in-one +All-in-one reading of variable `name` from `filepath`, returning a DataFrame with columns "time" and `name` """ function readVariable(filepath::String, name::String) :: DataFrame + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + time = readVariable(ac, vn, vd, di, "time") + varn = readVariable(ac, vn, vd, di, name) + df = DataFrame("time"=>time, name=>varn) + return df end +""" +Reads the vector of variable `names` from mat file `filepath`, returning a DataFrame with columns "time" and `names` +""" +function readVariables(filepath::String, names::AbstractVector{T}) :: DataFrame where T<:AbstractString + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + df = DataFrame("time"=> readVariable(ac, vn, vd, di, "time") ) + for name in names + var = readVariable(ac,vn,vd,di, name) + if length(var) == 1 # a constant value + df[!, name] .= var[1] + elseif length(var) == 2 && var[1] == var[2] + df[!, name] .= var[1] + elseif length(var) == length(df.time) + df[!, name] = var + else + throw(DimensionMismatch("Length of $name [$(length(var))] differs from the dataframe [$(length(df.time))], cannot add it")) + end + end + return df +end end #MAT_v4_Modelica \ No newline at end of file diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 8c154dc..2e29e4e 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -11,7 +11,6 @@ Pkg.activate(joinpath(@__DIR__, "..")) using Test cd(joinpath(@__DIR__,"..")) include("../src/MAT.jl") -# include("../src/MAT_v4_Modelica.jl") #OpenModelica v1.19.0 matBBO = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_res.mat") @@ -134,14 +133,31 @@ end var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1,1]") @test isapprox(var[26], 0.983794001, rtol=1e-3) - @test_throws ErrorException MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") @test isapprox(var[33], -0.58818129, rtol=1e-3) var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") @test isapprox(var[72], 0.935886479, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "world.animateGravity") + @test isapprox(var[1], 1.0, rtol=1e-3) +end + +@testset "all-in-one readVariable" begin + data = MAT.MAT_v4_Modelica.readVariable(matFBB, "bodyBox.frame_a.r_0[1]") + @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(matFBB, "nullVariable") end +@testset "all-in-one readVariables" begin + data = MAT.MAT_v4_Modelica.readVariables(matFBB, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) + @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) + @test isapprox(data[!,"bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) + @test isapprox(data[!,"world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time +end + + ; From 937cefa0a2722f2659ff6e9d68ce017d1f043208 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 15:46:37 -0600 Subject: [PATCH 49/91] add testsets for more readable test results --- test/read.jl | 343 ++++++++++++++++++++++++++------------------------ test/write.jl | 8 +- 2 files changed, 183 insertions(+), 168 deletions(-) diff --git a/test/read.jl b/test/read.jl index 67e62ab..c742cd6 100644 --- a/test/read.jl +++ b/test/read.jl @@ -45,188 +45,201 @@ end global format for _format in ["v6", "v7", "v7.3"] - global format = _format - cd(joinpath(dirname(@__FILE__), format)) - - result = Dict( - "int8" => Int8(1), - "uint8" => UInt8(1), - "int16" => Int16(1), - "uint16" => UInt16(1), - "int32" => Int32(1), - "uint32" => UInt32(1), - "int64" => Int64(1), - "uint64" => UInt64(1), - "single" => Float32(1), - "double" => Float64(1), - "logical" => true - ) - check("simple.mat", result) - matfile = matopen("simple.mat") - mat = read(matfile) - close(matfile) - for (k, v) in result - if(typeof(mat[k]) != typeof(v)) - error("Types for $k didn't match (expected $(typeof(v)), got $(typeof(mat[k])))") + @testset "testing $_format" begin + global format = _format + cd(joinpath(dirname(@__FILE__), format)) + + result = Dict( + "int8" => Int8(1), + "uint8" => UInt8(1), + "int16" => Int16(1), + "uint16" => UInt16(1), + "int32" => Int32(1), + "uint32" => UInt32(1), + "int64" => Int64(1), + "uint64" => UInt64(1), + "single" => Float32(1), + "double" => Float64(1), + "logical" => true + ) + check("simple.mat", result) + matfile = matopen("simple.mat") + mat = read(matfile) + close(matfile) + for (k, v) in result + if(typeof(mat[k]) != typeof(v)) + error("Types for $k didn't match (expected $(typeof(v)), got $(typeof(mat[k])))") + end end - end - - result = Dict( - "imaginary" => ComplexF64[1 -1 1+im 1-im -1+im -1-im im] - ) - check("complex.mat", result) - result = Dict( - "simple_string" => "the quick brown fox", - "accented_string" => "thé qüîck browñ fòx", - "concatenated_strings" => String["this is a string", "this is another string"], - "cell_strings" => Any["this is a string" "this is another string"], - "empty_string" => "" - ) - check("string.mat", result) - - result = Dict( - "a1x2" => [1.0 2.0], - "a2x1" => zeros(2, 1)+[1.0, 2.0], - "a2x2" => [1.0 3.0; 4.0 2.0], - "a2x2x2" => cat([1.0 3.0; 4.0 2.0], [1.0 2.0; 3.0 4.0]; dims=3), - "empty" => zeros(0, 0), - "string" => "string" - ) - check("array.mat", result) - - result = Dict( - "cell" => Any[v for _ in 1:1, - v in (1.0, 2.01, "string", Any["string1" "string2"])] - ) - check("cell.mat", result) - - result = Dict( - "s" => Dict{String,Any}( - "a" => 1.0, - "b" => [1.0 2.0], - "c" => [1.0 2.0 3.0] - ), - "s2" => Dict{String,Any}("a" => Any[1.0 2.0]) - ) - check("struct.mat", result) - - result = Dict( - "logical" => false, - "logical_mat" => [ - true false false - false true false - true false false - ] - ) - check("logical.mat", result) + result = Dict( + "imaginary" => ComplexF64[1 -1 1+im 1-im -1+im -1-im im] + ) + check("complex.mat", result) + + result = Dict( + "simple_string" => "the quick brown fox", + "accented_string" => "thé qüîck browñ fòx", + "concatenated_strings" => String["this is a string", "this is another string"], + "cell_strings" => Any["this is a string" "this is another string"], + "empty_string" => "" + ) + check("string.mat", result) + + result = Dict( + "a1x2" => [1.0 2.0], + "a2x1" => zeros(2, 1)+[1.0, 2.0], + "a2x2" => [1.0 3.0; 4.0 2.0], + "a2x2x2" => cat([1.0 3.0; 4.0 2.0], [1.0 2.0; 3.0 4.0]; dims=3), + "empty" => zeros(0, 0), + "string" => "string" + ) + check("array.mat", result) + + result = Dict( + "cell" => Any[v for _ in 1:1, + v in (1.0, 2.01, "string", Any["string1" "string2"])] + ) + check("cell.mat", result) + + result = Dict( + "s" => Dict{String,Any}( + "a" => 1.0, + "b" => [1.0 2.0], + "c" => [1.0 2.0 3.0] + ), + "s2" => Dict{String,Any}("a" => Any[1.0 2.0]) + ) + check("struct.mat", result) + + result = Dict( + "logical" => false, + "logical_mat" => [ + true false false + false true false + true false false + ] + ) + check("logical.mat", result) + + result = Dict( + "empty_cells" => Any[v for _ in 1:1, v in (zeros(0, 0), "test", zeros(0, 0))] + ) + check("empty_cells.mat", result) + + result = Dict( + "sparse_empty" => sparse(Matrix{Float64}(undef, 0, 0)), + "sparse_eye" => sparse(1.0I, 20, 20), + "sparse_logical" => SparseMatrixCSC{Bool,Int}(5, 5, [1:6;], [1:5;], fill(true, 5)), + "sparse_random" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]), + "sparse_complex" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]*(1. + 1.0im)), + "sparse_zeros" => SparseMatrixCSC(20, 20, ones(Int, 21), Int[], Float64[]) + ) + check("sparse.mat", result) + + matfile = matopen("partial.mat") + var1 = read(matfile, "var1") + @assert var1[28, 33] == 5 + var2 = read(matfile, "var2") + @assert var2[27, 90] == 10 + close(matfile) - result = Dict( - "empty_cells" => Any[v for _ in 1:1, v in (zeros(0, 0), "test", zeros(0, 0))] - ) - check("empty_cells.mat", result) + end +end +@testset "testing bigEndian" begin result = Dict( - "sparse_empty" => sparse(Matrix{Float64}(undef, 0, 0)), - "sparse_eye" => sparse(1.0I, 20, 20), - "sparse_logical" => SparseMatrixCSC{Bool,Int}(5, 5, [1:6;], [1:5;], fill(true, 5)), - "sparse_random" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]), - "sparse_complex" => sparse([0 6. 0; 8. 0 1.; 0 0 9.]*(1. + 1.0im)), - "sparse_zeros" => SparseMatrixCSC(20, 20, ones(Int, 21), Int[], Float64[]) + "index" => [8.8604784000000000e+04 9.8707212000000000e+04 1.0394035200000000e+05 1.1429712000000000e+05 1.5474923999999999e+05 1.5475435200000001e+05 1.5501074400000001e+05 1.5505315200000000e+05 1.5505718400000001e+05 1.5506168400000001e+05 1.5506524799999999e+05 5.4945741599999997e+05 5.6345896799999999e+05 5.9956981200000003e+05 7.0691623199999996e+05 7.9063657200000004e+05 8.4311938800000004e+05 9.2225131200000003e+05 1.1248994160000000e+06 1.2508148520000000e+06 1.4164141320000000e+06 1.4275988280000000e+06 1.4744331000000001e+06 1.4982212879999999e+06 1.5549058440000000e+06 1.5870300840000000e+06 1.6192005120000001e+06 1.6766071560000000e+06 1.9386816839999999e+06 1.9969427879999999e+06 2.0021861880000001e+06 2.3272494120000000e+06 2.5309351080000000e+06 2.6743788720000000e+06], + "spikes" => [ + -3.9146236245031032e+00 -6.7657651330021364e+00 -1.0780027188484372e+01 -1.4345619557780790e+01 -1.5488781013877338e+01 -1.3241531877846004e+01 -8.6339302778751907e+00 -4.1571900578409995e+00 -1.4845719040296610e+00 2.3147400250828232e-01 2.8917910181412778e+00 6.4067867244186800e+00 8.3368575385567603e+00 7.0732985406218223e+00 4.4095174940268036e+00 3.8495932342509342e+00 7.0605464919276546e+00 1.2892731012948772e+01 1.8593404980539656e+01 2.1332908128411184e+01 2.0142332517120792e+01 1.6740473413471157e+01 1.3650330377340575e+01 1.1913871749214691e+01 1.0804794411826084e+01 8.8366401987297127e+00 5.1092331749990514e+00 5.1218216653980408e-01 -2.9327647633922682e+00 -4.4870896208146753e+00 -5.0598199463728655e+00 -4.8330524336350118e+00 -2.8556000012645000e+00 2.9794817723619027e-01 1.8265416505730325e+00 -8.6155940979615875e-02 -3.9623352473810947e+00 -6.9070013227561047e+00 -7.3941131196997647e+00 -5.7411207637544166e+00 -3.2366812420300106e+00 -1.1460492068000723e+00 1.2381260731009580e-01 1.0930145325605314e+00 2.1927876983540933e+00 2.6570284430776856e+00 1.3381366125210661e+00 -1.2539624260623763e+00 -3.3642620416729994e+00 -4.1849749207505456e+00 -3.8760400918509301e+00 -2.6869552030388291e+00 -1.6718246062697015e+00 -2.3709942853677934e+00 -4.6623835517993664e+00 -6.6575320887201714e+00 -6.9891263747717174e+00 -5.7017039068420186e+00 -3.4759011423153079e+00 -1.7092931352045238e+00 -2.3854494206243695e+00 -5.8068462168496913e+00 -9.1001745572212531e+00 -8.8479323560036516e+00 + -5.0745139212223540e+00 -9.6338046242625506e+00 -1.3472006614220170e+01 -1.5245910823426385e+01 -1.5914718584705716e+01 -1.6397777086548157e+01 -1.5688652912166024e+01 -1.2863641020969972e+01 -9.5044110719151487e+00 -8.3463890698794305e+00 -1.0177024989874276e+01 -1.3445743563344164e+01 -1.6692449999817043e+01 -1.9680052373752346e+01 -2.0121942799899024e+01 -8.3700572911264981e+00 3.0032951243048100e+01 9.4582543108269547e+01 1.5377989075611117e+02 1.7130866838277802e+02 1.4748486763605970e+02 1.0947277486760775e+02 7.2684596239613015e+01 3.8193102601464858e+01 1.0540246012312117e+01 -3.8091287155762972e+00 -5.9643997244651556e+00 -3.0829122045048947e+00 -9.3969579584981044e-01 -4.1011214330806744e-01 1.1485354724913588e-01 8.3209939369519947e-01 1.7132389429717065e-03 -3.6057351605809611e+00 -8.8203434292989460e+00 -1.2692989286632969e+01 -1.3074091545751195e+01 -1.0580269175201330e+01 -7.7262110976707312e+00 -6.1406117115548504e+00 -5.0818589866084025e+00 -3.3150901769832091e+00 -1.7731765969545847e+00 -2.5627661695922046e+00 -5.6404917366725886e+00 -8.3842617579066747e+00 -8.5769172149145199e+00 -6.8248629640086911e+00 -5.7747794329330571e+00 -7.1063437611930578e+00 -9.7162880960954556e+00 -1.1170428842658552e+01 -1.0378119546974839e+01 -8.3419883744685741e+00 -6.9209209230224635e+00 -7.4145555946251820e+00 -9.6713314325255784e+00 -1.2062946758371979e+01 -1.2965017154593347e+01 -1.2408773058757985e+01 -1.1624355346583785e+01 -1.1127849526328186e+01 -1.0048811168388408e+01 -7.6327226475166228e+00 + -3.6172800577681850e+00 -3.0496107525626650e+00 -4.0450961689646379e+00 -4.9841853952779340e+00 -4.3137243411079638e+00 -2.4211699563520748e+00 -9.3469136675988018e-01 -9.3834475312754062e-01 -2.3043613016166664e+00 -3.9905808684096256e+00 -4.6842071022425413e+00 -3.8310453992051836e+00 -2.8818230979207713e+00 -5.2038837297130556e+00 -1.1725055393804819e+01 -1.3208091586845086e+01 9.2482123494256854e+00 6.3181823866266974e+01 1.2177486062412244e+02 1.4070171740815530e+02 1.0609880427064739e+02 4.8491974791356185e+01 5.8467389218168400e+00 -9.6802170255950291e+00 -8.6605173391900614e+00 -5.1555616199626577e+00 -5.4237382663488169e+00 -8.2575373220146044e+00 -1.0156241701964554e+01 -9.1574620977201118e+00 -5.6665700132115431e+00 -1.5504659563007614e+00 1.1334383183944798e+00 1.2640431009400714e+00 -6.6794751018025034e-01 -2.9543901520038176e+00 -4.1963329340429310e+00 -4.3642482086638417e+00 -4.0042244694146101e+00 -3.1394295409389494e+00 -1.8179756080601912e+00 -9.3024527470805374e-01 -1.4736656790371958e+00 -3.4645773489091209e+00 -5.8588062538620047e+00 -7.0734948204842896e+00 -6.0481893429370261e+00 -3.3344016972431088e+00 -4.8650896484328832e-01 1.6052150099271567e+00 2.9838901666777322e+00 3.7836445807114574e+00 3.9345735120816623e+00 3.1644840267225094e+00 1.0070686684300534e+00 -2.4713922778659478e+00 -5.9984216012494951e+00 -7.8885829262850233e+00 -7.5154376078825695e+00 -5.8326333185237758e+00 -4.5003931071444478e+00 -4.2328547992043450e+00 -4.1140676583157889e+00 -3.1460951038002301e+00 + -3.6781701228839154e+01 -3.7303084834807457e+01 -3.8884510028175633e+01 -4.1148035399962872e+01 -4.3316242853459642e+01 -4.4384384580854572e+01 -4.4790855343692897e+01 -4.7038755365884079e+01 -5.2501365276546458e+01 -5.6812084874163638e+01 -4.9570438054352273e+01 -2.3981805280442412e+01 1.1015352613446286e+01 3.7025927055247365e+01 4.6196840812364755e+01 4.5435239856395953e+01 4.4108776047185387e+01 4.5957015883875258e+01 4.9338962205718175e+01 5.0907715343642117e+01 4.9487314118838199e+01 4.6376644839165742e+01 4.2907993314061748e+01 3.9175485818078528e+01 3.4646364450142663e+01 2.9438295855526434e+01 2.5290001942034166e+01 2.4585557992414284e+01 2.7869881512937130e+01 3.2856199330415350e+01 3.6123557386279117e+01 3.5905020129260436e+01 3.3291843240497656e+01 3.0657151735220140e+01 2.9399978478943620e+01 2.9238216866099457e+01 2.8701738303043562e+01 2.6077460100630137e+01 2.0689205683518288e+01 1.3779281580459998e+01 8.1712538443203133e+00 6.6925061080176569e+00 1.0147005424138726e+01 1.6509827576474752e+01 2.2276297357800129e+01 2.4765444980789738e+01 2.3565808601878054e+01 2.0365852060539318e+01 1.7482427576467405e+01 1.5922922611993604e+01 1.4534460949429915e+01 1.1655908219543699e+01 7.5216074902940893e+00 3.9334852970087364e+00 1.7985868168036778e+00 3.9751100517608062e-01 -9.6689135481901367e-01 -1.8291316756437888e+00 -1.2108732556384698e+00 1.1422814255806824e+00 4.0827052431935931e+00 5.6696392298883893e+00 4.7889899662105178e+00 1.9874261259261794e+00 + -1.1686614851500112e+00 9.8124871505894706e-01 2.4682927864975457e+00 2.9222372863110442e+00 1.6794130795786746e+00 -6.8820557107116076e-01 -2.4491510090825823e+00 -2.5573541785944220e+00 -1.5657335653505560e+00 -6.2326635393824792e-01 7.5542615186640072e-03 1.2160520506156904e+00 3.7959025732449647e+00 7.1695274203178112e+00 9.8085862745466752e+00 1.1024667458560756e+01 1.1849030121520164e+01 1.3645183550697372e+01 1.6153045543872672e+01 1.7653460112285362e+01 1.6917192047304358e+01 1.4287778990065465e+01 1.1181854556421472e+01 9.2813981144484536e+00 9.6980182244576554e+00 1.1975656236851366e+01 1.4218362205978007e+01 1.4823189770395057e+01 1.3782281414778801e+01 1.2224714159620135e+01 1.1308817315730568e+01 1.1413207645545857e+01 1.1802812942398777e+01 1.1304006120772028e+01 9.4673823725009267e+00 6.7674104407290443e+00 3.9034391463718467e+00 1.0696510964445158e+00 -2.2265622627917740e+00 -6.1619053880194183e+00 -9.4895708918063448e+00 -1.0511848180773416e+01 -9.2016379963305752e+00 -7.3655072801953869e+00 -6.6664600174160604e+00 -7.2291846775841488e+00 -8.2107481715781105e+00 -9.1021811818757321e+00 -1.0311278259980540e+01 -1.2741511638678721e+01 -1.6437776102727412e+01 -1.9514979330779195e+01 -1.9610190285680936e+01 -1.6644297020966704e+01 -1.2800100439249650e+01 -9.9046719354109900e+00 -8.2923627062655303e+00 -7.8966764067275435e+00 -8.8257663696547084e+00 -1.0735616318501446e+01 -1.2599202496900540e+01 -1.3220431400502429e+01 -1.1920594647006027e+01 -9.3084512934042376e+00 + 1.1099942135772691e+01 1.0097197127580243e+01 1.1065906114342178e+01 1.3374123078938322e+01 1.6449533911253752e+01 2.0017355902687680e+01 2.3638247141687746e+01 2.6292880854006093e+01 2.6783333166190332e+01 2.5183008648660202e+01 2.3296988595385344e+01 2.2810464758877668e+01 2.3624918756652875e+01 2.4607374587222779e+01 2.5393398749862719e+01 2.6769469521678889e+01 2.9281995275221178e+01 3.2213436799025317e+01 3.4285343581139983e+01 3.4785296465751173e+01 3.3883822298572078e+01 3.2386332385252516e+01 3.1231943637716320e+01 3.0932840342758819e+01 3.1246878043151604e+01 3.1160302181567783e+01 2.9550070933834700e+01 2.6540966708675843e+01 2.3785330200047358e+01 2.2611886801417548e+01 2.2531515536471172e+01 2.2340044415310590e+01 2.1822535590221452e+01 2.1399364443416083e+01 2.0755992097134378e+01 1.8948315967557182e+01 1.5769917402888659e+01 1.2205733819672014e+01 9.4099541981525228e+00 7.7046512181258402e+00 6.5719664417242711e+00 5.2343013227680553e+00 3.3389338216778657e+00 1.2573655827507051e+00 -5.0199990120897509e-01 -1.8987019795142805e+00 -2.9172592054133535e+00 -3.2152176994632509e+00 -2.9689210401896537e+00 -3.2720957493420673e+00 -4.8582690799070640e+00 -7.1594797906170751e+00 -9.1803890906391956e+00 -1.0495134027231670e+01 -1.1056911353417380e+01 -1.0726534970818260e+01 -9.3753223680626547e+00 -7.2907274381158134e+00 -5.1694262729300959e+00 -3.7312476583791785e+00 -3.6141234953544066e+00 -5.1662008322483448e+00 -7.7776079749577836e+00 -9.8010587078412321e+00 + -1.4685530727984641e+01 -1.0292328330297664e+01 -6.2040173116488546e+00 -2.3375015307800595e+00 1.3923917658511158e+00 5.0564769748901313e+00 8.6976966359851104e+00 1.2323245348083425e+01 1.5903913125353689e+01 1.9380777962659089e+01 2.2677898949762564e+01 2.5718454097742246e+01 2.8441073995486732e+01 3.0813082951470030e+01 3.2837981366611231e+01 3.4555661605993883e+01 3.6035311542317494e+01 3.7362431137561401e+01 3.8622571934083787e+01 3.9885068827368393e+01 4.1190035284914742e+01 4.2541236867236606e+01 4.3906275503545629e+01 4.5224046329587310e+01 4.6417965861852949e+01 4.7412309197387181e+01 4.8148368395523121e+01 4.8597180088665191e+01 4.8766260494684808e+01 4.8698991553936125e+01 4.8466780378761598e+01 4.8155568154983840e+01 4.7849401563031734e+01 4.7614370946271123e+01 4.7486145888939269e+01 4.7463615344086584e+01 4.7509911501785595e+01 4.7560611227134473e+01 4.7537464942025593e+01 4.7364890774312840e+01 4.6985915393397903e+01 4.6374354157541411e+01 4.5540779674504876e+01 4.4531077464674446e+01 4.3417878743397615e+01 4.2286593389902350e+01 4.1218852636871070e+01 4.0276692303289153e+01 3.9490658602725233e+01 3.8854229591844380e+01 3.8325674875439489e+01 3.7836979823323738e+01 3.7308039311521902e+01 3.6663265807716996e+01 3.5847270787923392e+01 3.4836464766174224e+01 3.3644242194420237e+01 3.2318708010392434e+01 3.0933403069436942e+01 2.9572894236760884e+01 2.8316128161485977e+01 2.7220897815058663e+01 2.6312547181929837e+01 2.5579187011419549e+01 + 1.5763956152315169e+01 1.6523243082892250e+01 1.7337427631570872e+01 1.8120936638484590e+01 1.8792445157811912e+01 1.9291006994381139e+01 1.9588077541817789e+01 1.9693009891213325e+01 1.9650918197573489e+01 1.9533337877188348e+01 1.9423561429962543e+01 1.9399599967723205e+01 1.9518198338118580e+01 1.9803119911795868e+01 2.0240058237539227e+01 2.0779199633821964e+01 2.1344921568275133e+01 2.1850676042659646e+01 2.2216063986224665e+01 2.2382665762479274e+01 2.2325443040524132e+01 2.2057418172501748e+01 2.1626689639781716e+01 2.1106384004760407e+01 2.0579565930487874e+01 2.0122142297473431e+01 1.9787200170220586e+01 1.9593930129327656e+01 1.9523364223698717e+01 1.9521786868823959e+01 1.9511132956806740e+01 1.9404280892123200e+01 1.9122163091585080e+01 1.8609252789360340e+01 1.7844318015586811e+01 1.6844290518050197e+01 1.5660474424296631e+01 1.4367827117720619e+01 1.3049394428891683e+01 1.1778975433290963e+01 1.0605636123380648e+01 9.5436591203415890e+00 8.5705174399266060e+00 7.6329046830511684e+00 6.6569045386579333e+00 5.5557424579780292e+00 4.2330232952205966e+00 2.5948387638090815e+00 6.0010139846456756e-01 -1.6403240489988979e+00 -3.7983903715983196e+00 -5.5143675763583140e+00 -7.0197817000212250e+00 -9.2991809829904906e+00 -1.2710108289977208e+01 -1.5773321560596763e+01 -1.6492611312664831e+01 -1.5291065260668367e+01 -1.5553348127486492e+01 -1.9596399167644933e+01 -2.5711586491024381e+01 -3.1294712703031326e+01 -3.6140274982880157e+01 -4.0736526948594026e+01 + -7.5455089032603496e+01 -8.8091850484650791e+01 -7.6894847494814442e+01 -5.1931618057453484e+01 -2.8833831644552824e+01 -6.8895988088626297e+00 2.1168348264522379e+01 4.6693994244353583e+01 5.3777387223823958e+01 4.4603130335345831e+01 3.3530654869558120e+01 2.3587826739136968e+01 8.4238787409792160e+00 -9.2318222698507917e+00 -1.4713136414898660e+01 1.9717165915666754e+00 3.1968641163889899e+01 5.7269930354848498e+01 7.0152688867705734e+01 7.3409072049238773e+01 7.0166235593951257e+01 6.1829204326045371e+01 4.6696134605544373e+01 1.8622054473547621e+01 -2.2829591964997910e+01 -6.2715671170700787e+01 -8.2653742502672856e+01 -8.1088328703707077e+01 -7.2961806812740548e+01 -7.3632394853923415e+01 -8.9841480609702060e+01 -1.1962228131535032e+02 -1.5066572610100792e+02 -1.6038121312642346e+02 -1.2981471355911390e+02 -6.3653163261418783e+01 9.3811209125033130e+00 5.8695741367623484e+01 7.0437047869761585e+01 4.9630793312944938e+01 1.6599415269056415e+01 -2.5402904227425278e+00 4.0559101821793178e+00 2.4429205550104435e+01 3.9021358713588448e+01 3.8520783430674051e+01 1.9952697815867261e+01 -2.1877257859243720e+01 -8.3907527060881065e+01 -1.4250639755111564e+02 -1.6619398657243511e+02 -1.3819218483975862e+02 -6.6785267226843359e+01 7.8497452778842174e+00 2.7663034892382331e+01 -1.9252281399357905e+01 -5.9646303432603780e+01 -1.8852103558667594e+01 6.7639555235119275e+01 9.6932460076734856e+01 4.1262889176984515e+01 -3.3610639804651925e+01 -7.8423083296187542e+01 -1.0839727633754131e+02 + 4.7736577112711117e+01 4.5700337234538978e+01 4.5322631494156710e+01 4.9171444949072850e+01 5.3757473474671947e+01 5.4141589236927501e+01 4.8790940853843097e+01 4.1857477273482019e+01 4.0754533608850998e+01 4.8608027316167508e+01 6.0100216178867996e+01 6.8180296544356494e+01 7.1421072557515430e+01 7.0807306032014466e+01 6.6514973934144194e+01 6.2945074970931898e+01 6.8196514720480167e+01 8.5012727237677268e+01 1.0720726826115141e+02 1.1957256746876999e+02 9.9138254538272335e+01 3.8069276174166163e+01 -2.7233524821672358e+01 -3.9334632563706222e+01 1.3886634524961096e+01 7.3584276961424322e+01 6.3628518198466388e+01 -2.9054572516934122e+01 -1.4209888641029568e+02 -2.0910522987863314e+02 -2.1415046392049794e+02 -1.7586701353513934e+02 -1.1844102599759782e+02 -6.1431963885049129e+01 -1.4806030025628601e+01 1.4027944301627443e+01 1.0652586257860358e+01 -3.0054086554043813e+01 -8.5072361204847368e+01 -1.2046643138190960e+02 -1.2106054250546094e+02 -9.5841527604366775e+01 -6.6055322458778861e+01 -4.4458770387691487e+01 -2.4336370991880798e+01 4.5840797349366902e-01 2.1040288319943066e+01 2.3611095638912182e+01 1.3051021255932143e+00 -2.8851154140515209e+01 -3.7619486391456242e+01 -2.9915658771811749e+01 -3.0708973922309450e+01 -3.1924567737368655e+01 -1.0202022943830535e+01 2.9706495601613049e+01 5.9067239546813376e+01 5.4702088800913174e+01 2.8343487643024787e+01 1.5951389347675942e+01 2.2837902575396686e+01 1.2908948645547047e+01 -3.3255358730535995e+01 -8.7746125812653432e+01 + -8.2377418542911354e+00 -3.4972931824407372e+00 -7.7569626244497876e+00 -1.5166206734460895e+01 -1.5445771078633909e+01 -5.2413228186791176e+00 8.4387175092257500e+00 1.5431757026108208e+01 1.2399978335869392e+01 7.4095099729504046e+00 1.0679579816928053e+01 1.8701188652942292e+01 1.7701213689196198e+01 4.8431711642004913e+00 -4.7779662604481938e+00 3.1723005655630221e+00 2.4531952072999687e+01 4.5698045473740862e+01 5.9257083049327122e+01 6.2627426268528140e+01 5.7411981420461260e+01 5.2570470576359099e+01 5.2813113343401156e+01 5.0453507704104304e+01 3.9691119012785791e+01 2.6528767918502940e+01 1.8741299694240368e+01 1.7724441294399568e+01 2.0821175647900695e+01 2.4417659444952996e+01 2.5870167533339281e+01 2.4983092349575369e+01 2.2998436116876093e+01 2.0558711472598521e+01 1.7502822279045098e+01 1.3739438787271743e+01 9.9370928813924273e+00 7.9696531198819107e+00 9.3237074864808100e+00 1.2496048137913172e+01 1.4202138161523894e+01 1.3776871923186704e+01 1.4057294551750237e+01 1.6919973545359703e+01 2.0190481439159552e+01 2.0371692873037446e+01 1.6567959095411027e+01 1.0520230640011906e+01 4.5794135735479564e+00 8.7710329733688541e-01 8.9182399282027713e-01 4.3463285075996909e+00 8.7315858121978973e+00 1.0903936631346470e+01 1.0288756426506817e+01 1.0049725699443105e+01 1.3054576528907717e+01 1.6854021523448839e+01 1.6041035800270304e+01 1.0367866836597134e+01 6.4265096090680434e+00 9.4949675891987368e+00 1.6836520945082036e+01 2.1325105933919993e+01 + -4.1679158148256432e+00 -5.2810404688221357e+00 -5.9038977834292563e+00 -6.2634044553555412e+00 -5.9749855839469195e+00 -4.3218694766614334e+00 -1.1788772851158797e+00 2.4374543533437842e+00 4.8050829080472326e+00 4.8908091608309068e+00 3.3699448624872437e+00 1.8764385857350361e+00 1.0658937699825455e+00 -2.7882253437288551e-01 -3.1356733669314134e+00 -4.1284337659893531e+00 4.9241012162347957e+00 2.8218865342091348e+01 5.5056325524296241e+01 6.5099843979883218e+01 4.9609447627592324e+01 2.1423693795580775e+01 2.5132863903596458e-01 -5.8604600478907667e+00 -2.3380828579082129e+00 1.9600991106694594e+00 2.5915747007003080e+00 2.0190047306038350e-01 -2.8577160575959448e+00 -5.2869354732703258e+00 -6.9099491413979388e+00 -7.7559496509908179e+00 -8.0339926236897785e+00 -8.1760789963289717e+00 -7.9859360445805283e+00 -6.3703713820186918e+00 -3.0321001946270005e+00 -2.8201117047924162e-02 -7.8351721876626979e-02 -3.2229219827610613e+00 -6.6022607253341592e+00 -7.6428337012652356e+00 -6.3520480710327867e+00 -4.5468067925800151e+00 -3.6792489626697655e+00 -3.7265752054412307e+00 -3.8104694479464527e+00 -3.1502606288908388e+00 -1.5302095841423975e+00 2.6517264914271876e-01 7.6568764912213050e-01 -5.6131323023144386e-01 -2.6412705089170849e+00 -4.2923899829303380e+00 -5.1769894068210993e+00 -4.9614565332821146e+00 -3.2859163281387294e+00 -1.1703534382204590e+00 -7.6910847698287388e-01 -2.6122681986842187e+00 -4.2742848318737003e+00 -3.0739009488926641e+00 6.8120601334287323e-01 3.9336266256502612e+00 + -4.6484775115089541e+00 -8.3581561181698660e+00 -1.0902142569692860e+01 -1.1511797760460409e+01 -9.8276834132977005e+00 -6.2404161549022161e+00 -1.8432175810231679e+00 1.9347492334298952e+00 4.2938226787082971e+00 5.6566425031547753e+00 6.9370739014200060e+00 8.7323889596425897e+00 1.1151007876295226e+01 1.3469254774199312e+01 1.4378076971481226e+01 1.3628707243729355e+01 1.2810890536991595e+01 1.3603571552449427e+01 1.5792969414511386e+01 1.7425203016009618e+01 1.6493276434801135e+01 1.2586445678344095e+01 7.3401463249477024e+00 3.2087842017129540e+00 1.5030100447830739e+00 1.3898465906994972e+00 8.8198090743093560e-01 -1.0493063115005812e+00 -3.7608545130075726e+00 -6.3949540211998350e+00 -9.0066585358720026e+00 -1.1745588278397154e+01 -1.3842632605375675e+01 -1.4123365855042982e+01 -1.2335802612795424e+01 -9.4739532170207532e+00 -6.3782197090918142e+00 -2.8832164731098446e+00 4.8447063694123349e-01 1.6128314119847449e+00 -4.1774621162461167e-01 -3.3629973208103769e+00 -4.5025431650456200e+00 -3.9290643942165806e+00 -3.7022642550448306e+00 -4.6991587651781321e+00 -5.6909794907882132e+00 -5.2906700399814426e+00 -3.7661436808309654e+00 -2.5302504318642383e+00 -2.0969878128246422e+00 -1.3265870686252779e+00 7.0961290345922357e-01 3.0385814819877943e+00 3.9548508946305954e+00 2.9954312845408939e+00 9.9988805713822959e-01 -9.2502312206375614e-01 -2.0384940259267319e+00 -2.2297243620823197e+00 -2.0769146009998161e+00 -2.3170471037592764e+00 -2.8506427004880215e+00 -2.6777747842245225e+00 + -7.7992155184377432e-01 8.4208602530413512e-01 2.4887283142722940e+00 2.4338293029048339e+00 8.3831805409641036e-01 -5.6099977991794492e-01 -7.1266715703072170e-01 -2.3092906084045711e-01 1.6955881483329160e-01 8.8504105274391720e-01 2.2423969436547071e+00 3.3863034262368776e+00 3.6616215908416012e+00 4.2752627336864073e+00 7.0581057720790223e+00 1.1758833658720096e+01 1.6047422019307188e+01 1.8405075233788665e+01 1.9427396781993370e+01 1.9879444229356857e+01 1.9348574654086402e+01 1.7629540788742673e+01 1.5792875449818970e+01 1.4872230427632566e+01 1.4471792816861928e+01 1.3426898347317799e+01 1.1319456367524939e+01 8.8575138135993203e+00 7.0846315948436089e+00 6.5127944804059057e+00 6.6781793515550252e+00 6.2758575263602134e+00 4.2213211609500574e+00 1.0187928511802555e+00 -1.3365602410322484e+00 -1.2247622425200726e+00 1.1481723220280773e+00 4.3321910406078903e+00 6.9648334152330520e+00 8.0154771868425438e+00 6.5719188712588483e+00 2.4474218319276826e+00 -3.0491523253799624e+00 -7.4154828913997903e+00 -8.5953677824666350e+00 -6.7813390775209372e+00 -4.2068257455106703e+00 -2.7309337921106196e+00 -2.0348235578019351e+00 -6.2767747718278888e-01 1.7379756336568186e+00 3.5393101620046572e+00 3.5444884355037840e+00 2.3746522750550279e+00 1.3500592448824880e+00 6.9976097822180139e-01 -1.5618921435797500e-01 -1.3162226599662721e+00 -2.4845798158016903e+00 -3.7797798352858396e+00 -5.2368125011457316e+00 -5.9486138818233414e+00 -5.1354371684134463e+00 -3.7528582973371494e+00 + -3.0447537175403006e+00 -1.7667805638958940e+00 -1.9778229750617011e+00 -2.2515604403315699e+00 -1.3449261643785633e+00 -1.0662626484421048e-01 -5.7657486030100791e-02 -7.4315041221388778e-01 -3.2337810363445102e-01 1.8577438758015754e+00 4.8425765941254983e+00 7.8123978114524020e+00 1.1105854493054094e+01 1.4841903991300153e+01 1.7468871344145363e+01 1.7574512951317725e+01 1.6451820522767886e+01 1.6628323973953727e+01 1.8301434258000356e+01 1.8931382166939795e+01 1.6217394592510296e+01 1.0559530299242045e+01 4.4576293834236687e+00 -1.1822470014800246e-02 -2.5727563770885062e+00 -3.7547505719161225e+00 -3.5081536657220189e+00 -1.4835065248806825e+00 1.8247576781703743e+00 4.8275703093820033e+00 5.7228574725963268e+00 3.8558888609578723e+00 4.0990146070366057e-01 -2.4574645653496310e+00 -3.1102243476353593e+00 -1.4474225309731716e+00 7.4127875680854416e-01 1.0383069562131020e+00 -1.0150232301722759e+00 -3.4198210424706135e+00 -4.3088303898183238e+00 -3.9165170232756070e+00 -3.6605958183355347e+00 -4.2469618414480417e+00 -5.2237494007048566e+00 -5.9420620236257076e+00 -6.1698135259889568e+00 -5.7089733374935268e+00 -4.0492707065696969e+00 -8.0849384208890562e-01 3.3216805308527921e+00 6.2990262694136261e+00 6.1812329879916819e+00 3.1266169114028388e+00 -4.8505368345761951e-01 -2.2625885070785667e+00 -2.0371523965480165e+00 -1.4033783831712108e+00 -1.2651726669536290e+00 -8.7131680693218405e-01 5.0106877171388586e-01 1.8406747295741790e+00 1.3085085083594870e+00 -1.5293730324086736e+00 + 2.3513445326289961e+00 1.7384880477187443e+00 2.2184897775735659e+00 5.2051223974837342e+00 1.0452816121087638e+01 1.5377084182445486e+01 1.7198975360259148e+01 1.6104713320124159e+01 1.4833420837246866e+01 1.5100670860303905e+01 1.6073099350257220e+01 1.6269109488250638e+01 1.5637757524959511e+01 1.5476499170789715e+01 1.6604024256326596e+01 1.8317676582762779e+01 1.9829075755286041e+01 2.1816151151864332e+01 2.5055347925633036e+01 2.7333907737059032e+01 2.2427805378565086e+01 3.7141556286936721e+00 -2.8648138173116212e+01 -6.3843957225821207e+01 -8.7089860614432553e+01 -9.1923311472937954e+01 -8.3938126428996938e+01 -7.2885847777427841e+01 -6.4297677965088411e+01 -5.7914366258401401e+01 -5.0470379597131561e+01 -3.9499120538780375e+01 -2.5751695307781368e+01 -1.1778187088014317e+01 1.0189820693630494e+00 1.1960524761686607e+01 1.8733303083800365e+01 1.8356414318508428e+01 1.0668647686854321e+01 -1.0075011878329772e+00 -1.2633619248121462e+01 -2.1404683668367543e+01 -2.5602377289037388e+01 -2.4560926814716566e+01 -1.8847733387633440e+01 -9.8404336731293878e+00 3.5966958558048301e-01 9.0662935627188972e+00 1.4335829915263089e+01 1.6036694595613060e+01 1.5184337823870287e+01 1.2469246394512929e+01 7.6677145828817306e+00 8.5165822752424969e-01 -5.8806346340252293e+00 -9.1084583818696743e+00 -7.2999534121027425e+00 -2.4813921667292815e+00 1.7215656085634032e+00 3.0197896060174911e+00 1.4579843578258131e+00 -1.6420102258369735e+00 -4.8732983588159042e+00 -6.9044980772313806e+00 + -7.1748224092297468e+00 -3.8936102456845245e+00 1.0743831294909865e+00 4.8816301487736782e+00 5.4189196440465226e+00 2.8421806913201926e+00 -7.8193972997551731e-01 -3.3953855854612938e+00 -3.9985877326731809e+00 -2.4284915455410818e+00 3.3095855413398589e-01 2.1860065858780535e+00 1.8280988855495470e+00 3.4048799490454618e-01 3.8790118108287441e-01 3.8556647578240639e+00 9.9718322097927121e+00 1.5912297294502212e+01 1.9511397698602284e+01 2.0651366980982488e+01 2.0134282655984631e+01 1.8560445836154724e+01 1.6287447146155721e+01 1.3561600277652611e+01 1.0795264078502715e+01 8.6030785401577266e+00 7.0022103875078896e+00 5.1888911547538754e+00 2.7145704050730384e+00 1.4143845469113936e-01 -1.9107456843838713e+00 -3.4559243419646326e+00 -4.2223452047916581e+00 -3.1130434517080205e+00 1.7183260785086762e-01 3.7869557639525033e+00 5.3380153379310666e+00 4.2352058165010487e+00 1.6573488592368961e+00 -8.5887188564406203e-01 -2.1681503131724709e+00 -2.1279908375294667e+00 -2.0792749208808359e+00 -3.3997834567720187e+00 -5.3580052019440032e+00 -5.9792374734377622e+00 -4.8403202446726707e+00 -3.3487890303481347e+00 -2.6600981462061033e+00 -2.7228934357253367e+00 -3.1308053901450172e+00 -3.8963449071942557e+00 -5.2729284759196471e+00 -7.2764195946828067e+00 -9.3283947520693822e+00 -1.0227824562298673e+01 -9.0739885142293417e+00 -6.5681575524140818e+00 -4.8038541028704778e+00 -5.3349275900632449e+00 -7.6025577687023391e+00 -9.3130362450711317e+00 -8.3152888687476345e+00 -4.2484255376825795e+00 + -1.2094820634316039e+00 -1.2706817164948352e+00 -1.2775018026388452e+00 -2.3357176552894954e-01 1.8928499829725349e+00 4.4677863928736414e+00 6.9513585826945681e+00 9.0204123867514259e+00 1.0482452392061710e+01 1.1455309752506835e+01 1.2237810752650413e+01 1.2688637793112969e+01 1.2235313891126804e+01 1.1100297213735251e+01 1.0773414547739115e+01 1.2217993905654721e+01 1.4701466325393794e+01 1.7319490559451381e+01 1.9808972202344144e+01 2.1257689253929254e+01 2.0368628657184544e+01 1.7524563363309646e+01 1.4843567969750833e+01 1.3863574192388871e+01 1.4031045614008303e+01 1.3693669004482535e+01 1.2122436338523961e+01 9.9467781708780620e+00 7.8498090003437868e+00 5.7967683127528629e+00 3.7206536395890204e+00 2.3267777177674382e+00 2.6303789001713365e+00 4.7445695138569235e+00 7.6683738268993942e+00 9.9717476628589878e+00 1.0271279571889663e+01 8.1958760665984958e+00 5.3998213275104048e+00 4.2899793853628481e+00 5.5462911375305124e+00 7.6595253723593695e+00 8.4981448223169700e+00 7.2294579833209385e+00 5.1143564806110922e+00 3.8260968045635870e+00 3.3065755059745818e+00 2.5815464286120089e+00 1.7488042411202225e+00 1.5184709880337026e+00 1.6370045591241176e+00 1.1531897091681369e+00 -1.5863929796862442e-01 -1.5759013113892080e+00 -2.3911490323759002e+00 -2.4147368159491429e+00 -1.8749121657426793e+00 -1.4086065112641286e+00 -1.6573036686621481e+00 -2.4019595691049194e+00 -2.7197582423545761e+00 -2.3228923158464436e+00 -1.8987417992229161e+00 -1.9762436025825927e+00 + -2.1788608089091870e+00 -2.3721017929273520e+00 -2.2248744539174794e+00 -1.3562719882732486e+00 1.9303974836178317e-01 1.3110471548593794e+00 1.2797866409946126e+00 1.1446512790093115e+00 2.4218364442454297e+00 5.3568199699566073e+00 9.1928444896243846e+00 1.2911855868338753e+01 1.5131303558516208e+01 1.4796675429288374e+01 1.2747818159896775e+01 1.1384776546745211e+01 1.2304499348204516e+01 1.5012766622841385e+01 1.7706586949808607e+01 1.8615946254339946e+01 1.7088985921306737e+01 1.4118105664819083e+01 1.1547667913836323e+01 1.0268505344072013e+01 9.5426651513383796e+00 8.3936752159092531e+00 6.7898835952875585e+00 4.9190137058105190e+00 2.3035160443938620e+00 -1.4454604882529436e+00 -5.5578141560543060e+00 -8.6285337833508056e+00 -9.8768639485094845e+00 -9.3497459396540972e+00 -7.4531759065479113e+00 -4.8343191161214989e+00 -2.2707557414183874e+00 -2.1273932165940168e-01 1.4763518504563440e+00 2.9929246806770484e+00 3.6484933755646152e+00 1.9940019916984792e+00 -2.3622776610960083e+00 -7.2558689942915287e+00 -9.3484617741468909e+00 -7.4954744905729234e+00 -3.9544592514927057e+00 -1.7075406868672076e+00 -1.4165546781975049e+00 -1.6221832630448796e+00 -1.0260840386708110e+00 1.6403282116014173e-01 6.0805828587662303e-01 -7.8558049118458273e-01 -3.6478240721938118e+00 -6.4046749238136291e+00 -7.7290809125845756e+00 -7.3840184127203816e+00 -5.7754694634749093e+00 -3.6798105136804447e+00 -2.5013142267663810e+00 -3.3639333122560284e+00 -5.7371424805126106e+00 -8.0051890648261459e+00 + -1.5740146351038696e+01 -1.5127188568935047e+01 -1.6157259030112712e+01 -1.7330034352768784e+01 -1.7263465597740716e+01 -1.6336957355980775e+01 -1.5946556228923413e+01 -1.6451747846316870e+01 -1.6476134055902989e+01 -1.4472580313892262e+01 -1.0935457247727644e+01 -7.9876368021814939e+00 -6.0265186692348891e+00 -2.0146808151690361e+00 8.0163390797194936e+00 2.5505537996776390e+01 4.7787314568418353e+01 6.8639540131510500e+01 8.1758985020608336e+01 8.5294009078541549e+01 8.2283381938562812e+01 7.6550409062053944e+01 6.9344678720567757e+01 6.0023044834365038e+01 4.8849934461625729e+01 3.8040291720134526e+01 2.9945383165919644e+01 2.5344624124046817e+01 2.3679402477458130e+01 2.3454441703713346e+01 2.2281261219059516e+01 1.7989994660541914e+01 1.0436193819072196e+01 2.1102305275848425e+00 -3.6577185659374600e+00 -5.5145847757485971e+00 -4.5324408328651318e+00 -1.8554008253732013e+00 2.7089378387782199e+00 9.2187555916040793e+00 1.5945566856263504e+01 2.0472163602212458e+01 2.2294516323769198e+01 2.2881411820375533e+01 2.2911237311157720e+01 2.1364859320014958e+01 1.7602082925628398e+01 1.2398805245993104e+01 6.8726800470759422e+00 1.6538594259187884e+00 -2.9437120155973195e+00 -6.5367981004048668e+00 -8.6437256178931250e+00 -9.1231101796868952e+00 -8.6648085339554566e+00 -8.4735847323575335e+00 -8.9963274994615468e+00 -9.3198443907122499e+00 -8.2504681130406006e+00 -5.5321797455350028e+00 -1.9690195798275383e+00 9.0573194795780632e-01 1.6386927935969133e+00 3.9409445444748448e-01 + -1.2097853394633344e+00 -2.3372661136012782e+00 -4.5868126352335468e+00 -7.7696106024369840e+00 -9.8847492594673341e+00 -8.4351485269365991e+00 -3.4815504396923176e+00 2.0318425367749775e+00 5.4688409099903446e+00 6.8155210844128202e+00 7.5289034993747936e+00 8.2470533843717533e+00 8.5005207895890198e+00 8.1730893089208134e+00 8.1217939375154362e+00 9.2472099093868128e+00 1.1682275429968531e+01 1.4843432519914167e+01 1.7625325319637877e+01 1.8827596749646286e+01 1.8262944365019820e+01 1.7257770010849043e+01 1.7139858491940043e+01 1.7319082568288064e+01 1.5832240015066093e+01 1.1889468934637401e+01 7.0260166885118247e+00 3.4084227701886709e+00 1.5903858359584313e+00 6.4628466394257855e-01 2.9208281149091886e-01 1.3680522046018677e+00 3.7148191371537109e+00 5.3471095100773640e+00 4.5999975157196396e+00 1.8618036691603201e+00 -1.1675467460557312e+00 -3.2014095470489683e+00 -4.1751621005792883e+00 -4.4441528031142692e+00 -3.7587484122597536e+00 -2.0282422842477690e+00 -4.8339177039881087e-01 -2.6711860399748077e-01 -3.8790549777032224e-01 8.5572500658487782e-01 3.0975713952792994e+00 4.3374149891908393e+00 3.2920941855788466e+00 3.9295487513328070e-01 -2.4783937608256150e+00 -3.2055601186298843e+00 -1.7160184560799154e+00 -6.3702741131976026e-01 -2.2383884758776662e+00 -5.5570116369254112e+00 -8.2084983887639105e+00 -9.5917739048230395e+00 -1.0331039070410487e+01 -1.0417944274770905e+01 -9.6648501662164410e+00 -8.3293893029282344e+00 -6.5549338022834966e+00 -4.3174230553074713e+00 + -1.9521686822118689e+01 -2.0828890952194818e+01 -2.1026487257169343e+01 -2.0644194340000958e+01 -2.0356661177644074e+01 -2.0486033910028034e+01 -2.0592315338305198e+01 -1.9336484142295909e+01 -1.5938473018577051e+01 -1.1921626681007268e+01 -9.9587671774048481e+00 -1.0450212320468967e+01 -1.0318268307692627e+01 -5.6239283444522234e+00 5.5321151308175374e+00 2.2417493712423962e+01 4.2078909484239603e+01 5.9973160754273493e+01 7.1702694488382562e+01 7.5072713982579657e+01 7.0924560394362075e+01 6.2663339872630232e+01 5.4356911122207862e+01 4.7989283986803173e+01 4.2765855626087045e+01 3.7486699590135927e+01 3.2734101027576685e+01 2.9993606241210717e+01 2.9069222310304095e+01 2.7645584279325117e+01 2.3767330134444784e+01 1.7501891196670105e+01 1.0097030989509490e+01 3.3105933448286935e+00 -4.4358692407392453e-01 4.2309373559202790e-01 4.6272362980750925e+00 9.2452802627883770e+00 1.2881829396441868e+01 1.6139200491488932e+01 1.9696338650752981e+01 2.2945254570047432e+01 2.4052713164028159e+01 2.1836262493236529e+01 1.7755371912217502e+01 1.4544324719156007e+01 1.3076166058655325e+01 1.2023365019266210e+01 9.9330468323536198e+00 6.5642773658552329e+00 2.6211689892425389e+00 -1.2463040312411424e+00 -4.6931152521918520e+00 -7.0610915480926959e+00 -7.8896098442295468e+00 -8.1377876309837323e+00 -9.2879431013150882e+00 -1.1118321847684978e+01 -1.1512230767538767e+01 -8.6570795004612968e+00 -3.0565564637606264e+00 2.6614202613936517e+00 5.7492933678780842e+00 5.1870636768943843e+00 + -1.2768528401242900e+01 -1.7338863074172309e+01 -2.0237569092156448e+01 -2.0576492513490894e+01 -1.9431123286614632e+01 -1.8641772316619317e+01 -1.8943170420368556e+01 -1.9167043655793719e+01 -1.7528873877656416e+01 -1.3999909814005159e+01 -1.0805366953545310e+01 -9.6142597372311247e+00 -8.9149462386606366e+00 -5.4471587606889269e+00 2.2906924892770721e+00 1.2948039993449356e+01 2.3895472387004183e+01 3.2785154492570712e+01 3.8224124421928316e+01 3.9996932598102731e+01 3.8917325110890907e+01 3.6404969952831500e+01 3.3603549356134771e+01 3.0482131529017348e+01 2.6418551514199979e+01 2.1753968035179085e+01 1.7865996396778279e+01 1.5605745867809237e+01 1.4445507979298107e+01 1.3294236667868901e+01 1.1519440091973062e+01 9.2364945825762419e+00 7.2532314388618229e+00 6.2602214003637382e+00 5.4987881290564387e+00 3.2320466512581985e+00 -9.0506248164303926e-01 -4.9456160363826012e+00 -6.3608335770462485e+00 -4.2734547891318062e+00 4.6309862256935785e-01 6.7021945441538548e+00 1.3111777285636606e+01 1.7514480785911530e+01 1.8248810290360364e+01 1.5766174142459235e+01 1.1757377659322158e+01 7.9733313460406592e+00 6.0274343310982506e+00 6.1770944745629279e+00 6.2936748428494491e+00 4.0368463533344734e+00 -4.2685170771143977e-02 -3.1865409383399417e+00 -3.8901205566515773e+00 -2.9576974863775090e+00 -1.4738094422502874e+00 4.0118819835881747e-01 2.6525695143418675e+00 4.6014650691595245e+00 5.1565376735843209e+00 4.0636905672697523e+00 2.6453975037372648e+00 2.3284476158012066e+00 + -1.3007839852110759e+01 -1.1025538159980380e+01 -1.0686371074924564e+01 -1.2744092667984233e+01 -1.5642265829843563e+01 -1.7140214084438682e+01 -1.6617034625505930e+01 -1.5300909110451405e+01 -1.4577809836556332e+01 -1.4955079002970214e+01 -1.5654258710135920e+01 -1.4407648124599390e+01 -9.0954740513659118e+00 -6.2982400317265430e-02 1.0332127909943164e+01 2.0193720207165487e+01 2.8972473785793127e+01 3.6317935037933722e+01 4.1210257687869564e+01 4.2631382536124164e+01 4.0972150948544574e+01 3.8156297398596216e+01 3.5717588235143509e+01 3.3207917004761363e+01 2.9304640994990272e+01 2.4112801189469973e+01 1.9353557646954098e+01 1.6308754098888908e+01 1.4516954476871140e+01 1.2392183920492100e+01 8.3965380716467521e+00 2.2326430594848903e+00 -4.3996278411324354e+00 -8.8062298459598374e+00 -9.2226405800100828e+00 -5.6733723455263254e+00 3.7975785593939992e-02 5.0496912837583618e+00 7.5061362215685907e+00 8.1297047935707027e+00 8.9880765155299258e+00 1.0960262021995606e+01 1.3066019970002715e+01 1.3738034632375317e+01 1.1896051602413694e+01 7.5099861905659733e+00 2.1853151873444245e+00 -1.4107524690116966e+00 -1.7374936024760426e+00 4.7474258642688838e-02 1.2777176662851293e+00 5.9151627918367655e-01 -9.4200465373474518e-01 -1.3835697125582160e+00 -1.1439767142326973e-02 2.0431818226269547e+00 2.8931387751496276e+00 1.7217225031925740e+00 -3.6057387583206468e-01 -1.4188595163897433e+00 -5.3401986141825453e-01 1.7331384545428845e+00 4.1028021413802733e+00 5.1530807234519207e+00 + -5.9423907462970762e+00 -6.6393370296983978e+00 -7.7903948031970467e+00 -9.0887616470108981e+00 -9.0730958856624682e+00 -7.3051960638397269e+00 -5.1092125692791504e+00 -4.1499019811620803e+00 -5.1825095304735012e+00 -7.4650822660744725e+00 -8.7858341576439987e+00 -6.9165862119378128e+00 -1.1152780425762092e+00 7.4456360751231196e+00 1.5504355240213883e+01 1.9891995742575336e+01 2.1880531164099967e+01 2.5705009499507383e+01 3.1274011045582068e+01 3.4010306181075244e+01 3.2770325333513270e+01 3.0948804502035770e+01 2.9539194246932787e+01 2.6197352223405726e+01 2.0544443634266784e+01 1.4590436095771173e+01 9.4132762585289544e+00 5.0726802461989733e+00 1.8845604581530462e+00 -2.0830751870538844e-01 -2.0248221543626945e+00 -3.9513021238798549e+00 -5.3937857883630116e+00 -5.7622214722998990e+00 -5.0964393510051575e+00 -3.6685468807689410e+00 -1.9925824037801600e+00 -1.3116773229001528e+00 -2.6369224209795075e+00 -4.8818299037998507e+00 -5.7575190263162872e+00 -4.9151597170028811e+00 -4.2580797535905948e+00 -5.3162000432783687e+00 -8.0260447395650854e+00 -1.1124603514599908e+01 -1.2372738496954803e+01 -9.9327640511974646e+00 -4.8837906274471230e+00 -8.7396451836104794e-01 -1.7270469647056230e-01 -1.5776571699924549e+00 -2.7982572746004415e+00 -3.1062409337303580e+00 -3.0845221509902201e+00 -3.0432684148630136e+00 -2.5011943998532136e+00 -1.1481969001182208e+00 3.2261647132979526e-01 7.8134574976169557e-01 -1.2641763242828769e-01 -1.4193228153058111e+00 -1.7047633076684265e+00 -8.4627935364228413e-01 + -3.3511583497758619e+00 -5.1854891048629845e+00 -8.2045548190539002e+00 -1.1321927344458468e+01 -1.3232156516018623e+01 -1.2891455905593750e+01 -1.0308983110741357e+01 -7.2770589395120400e+00 -6.3230641518260899e+00 -7.7668924711145673e+00 -8.5533116261484974e+00 -5.1754472720840532e+00 2.5464912688278725e+00 1.1150146477409665e+01 1.6749765390457195e+01 1.8459480819125350e+01 1.8853443625394785e+01 2.0968383227964456e+01 2.4745356436846293e+01 2.6900729147509413e+01 2.4586028405507879e+01 1.8423893823872788e+01 1.1687464461034605e+01 7.4694246602111161e+00 6.5063740610776568e+00 6.6399254866014452e+00 4.8727847407542608e+00 6.2948434274342469e-01 -3.8180925201354374e+00 -5.9745650071051903e+00 -5.0248037653094384e+00 -1.7240345099049890e+00 2.4707226972860665e+00 6.1789893211512830e+00 8.4606300098802265e+00 9.1837056122684562e+00 9.4175247215558571e+00 1.0361111194478536e+01 1.1355825197106681e+01 1.0443694556801294e+01 7.3275805488596522e+00 4.0729400267880624e+00 2.7849489919865249e+00 4.0449011920910980e+00 6.8903062423979726e+00 8.8579270913084418e+00 7.2409153007186635e+00 1.9815538314749910e+00 -3.4963983686766915e+00 -5.7025552070066450e+00 -4.6316183670653945e+00 -2.8570944089319230e+00 -2.1945685959883381e+00 -2.7295179820568904e+00 -4.1440094951781976e+00 -6.1739578627827907e+00 -7.9441831061071788e+00 -8.3008782660488869e+00 -7.1697557563545278e+00 -5.6642787357607363e+00 -4.5049090776747240e+00 -3.1919541675350729e+00 -9.9748944534117390e-01 1.8822644573222371e+00 + 1.6022978144703672e+00 -1.4311786252489094e+00 -4.9990745496272506e+00 -7.1229734861916816e+00 -6.0298488474363783e+00 -1.8843154305695582e+00 2.8057312397445893e+00 5.3045500670547181e+00 5.2217607939007324e+00 4.6015143428315151e+00 5.3985890315998715e+00 7.6627280386075034e+00 1.0581969356003244e+01 1.3680704869130523e+01 1.6133038629367640e+01 1.6836134617835810e+01 1.5985757935422852e+01 1.5071121283266066e+01 1.5015214850097710e+01 1.5311877473734373e+01 1.4952636894507775e+01 1.3509895518873545e+01 1.1287529167684998e+01 8.8944719667341001e+00 6.8056307961237392e+00 5.1599082109694479e+00 3.9096368310365088e+00 3.0884796567205659e+00 2.8027781380901557e+00 2.7427378963657629e+00 1.8080112317158328e+00 -7.2093739358622477e-01 -3.2256888875692162e+00 -2.5485135905025946e+00 2.1938957914977939e+00 7.8096722519713868e+00 1.0108587362109473e+01 8.0119639048454889e+00 3.7875674256494771e+00 -1.7909623605085379e-01 -3.6408942410859053e+00 -7.2043954289116119e+00 -1.0036996806105844e+01 -1.0480191286127010e+01 -8.6104715538045582e+00 -6.5737607267403781e+00 -6.4789888225111065e+00 -8.9832745466307422e+00 -1.3098938079216277e+01 -1.6488189562785699e+01 -1.7072074337181476e+01 -1.4675862164708811e+01 -1.0393673234405423e+01 -5.7706460525680994e+00 -3.2783034075657795e+00 -4.8699351422351356e+00 -9.0537498769047904e+00 -1.1681066334269010e+01 -1.0244235531230570e+01 -5.9719523689580347e+00 -1.9644630163354910e+00 -6.8837039873644690e-01 -2.7600482002062674e+00 -6.6765535392302047e+00 + -2.6793902875688205e+00 4.3700049391893547e-01 5.2207839165959946e-01 -2.5289383815408999e+00 -6.4357928708025778e+00 -8.6801895727348093e+00 -8.6519420551714941e+00 -7.5157946794804973e+00 -6.9229481363763350e+00 -7.7994153444953156e+00 -9.3675453247907754e+00 -1.0006058503692298e+01 -9.5066143246051311e+00 -9.1193248923592503e+00 -9.0487355120456403e+00 -6.9972012375818347e+00 -1.6948530860574240e-01 1.0752688787226244e+01 2.0728963668504598e+01 2.4824092328193505e+01 2.2702194691381209e+01 1.8103111981612606e+01 1.5186220180215653e+01 1.5435411462559298e+01 1.6851176785915300e+01 1.6256911281706046e+01 1.2853830842714240e+01 8.7992906000849906e+00 6.3848237032232804e+00 6.1798137367968202e+00 7.8154545356284144e+00 1.0467840599577846e+01 1.2480989360982822e+01 1.2582337684046703e+01 1.1590145096205708e+01 1.1337803770903093e+01 1.2054473739609797e+01 1.2250402949861966e+01 1.0983877637654345e+01 8.6551288071765491e+00 5.8174031977868585e+00 2.9188587271482644e+00 8.3930412496566609e-01 2.3622649323786860e-01 7.1444423031664739e-01 1.2655853456754595e+00 1.3229336834759118e+00 1.3578720434847311e+00 2.1076821170226498e+00 3.0728270343869779e+00 2.7843524321077324e+00 9.0852714062149698e-01 -1.0017936404451253e+00 -1.3974751167516584e+00 -6.4988794902308711e-01 -5.0807738982896855e-01 -2.0685431131427148e+00 -4.6510643108399936e+00 -6.4865750445095642e+00 -6.4858500824649585e+00 -5.1351447719989922e+00 -3.4051840546128562e+00 -1.7184951490663052e+00 -5.0407691071619776e-01 + -3.6981497476642828e+00 -3.0390710048817726e+00 -1.4989922553639727e+00 1.0284003810800462e+00 3.6622719317496890e+00 4.7174258831055269e+00 3.5121017080473034e+00 2.1663525068616294e+00 3.8933497703894613e+00 8.9756839635313099e+00 1.4369540972251285e+01 1.7517537513748934e+01 1.8367043951422612e+01 1.7487527428352760e+01 1.4910602011410518e+01 1.1788546361447585e+01 1.0695666486764603e+01 1.2768873027185609e+01 1.6000928788239836e+01 1.7360605935279679e+01 1.5930200170467559e+01 1.3260280784655849e+01 1.0849461771852765e+01 8.4797732256321510e+00 5.3784098602866708e+00 1.7165101724011995e+00 -1.4089618767998704e+00 -2.7952639837436752e+00 -2.2276522503149407e+00 -9.5316276326730420e-01 -5.3299313777074875e-01 -1.5019739358173170e+00 -3.5515201972803432e+00 -6.3187612986855708e+00 -9.3137337609793054e+00 -1.1318424950944941e+01 -1.0816559293228659e+01 -7.7508782461822854e+00 -4.2600195254335116e+00 -2.8712335676010809e+00 -4.2724290569942225e+00 -6.8899881431600578e+00 -8.1742447353742449e+00 -6.8082430068650392e+00 -3.9299615464428408e+00 -1.4896688083453651e+00 -4.1155002553450060e-02 6.2487647569787685e-01 -2.9020287195242966e-01 -3.8853316012394776e+00 -9.2125322820605025e+00 -1.3050926297558480e+01 -1.2737108773295276e+01 -8.7818443279864002e+00 -4.3127261410662650e+00 -2.3302722768023965e+00 -3.5325752167188584e+00 -6.2805892811261916e+00 -8.5016614679139195e+00 -9.4759820546177966e+00 -9.7204496390574704e+00 -9.7546714698047463e+00 -9.5752380341344967e+00 -9.3036539221877597e+00 + -1.7414985024478632e+01 -1.7710392269365567e+01 -1.8830345987045778e+01 -2.0905808395741552e+01 -2.3089781413981267e+01 -2.3899680760146289e+01 -2.2409641713666709e+01 -1.8930485800816182e+01 -1.4601034805586801e+01 -1.0520204067883871e+01 -7.1082264501151942e+00 -4.1771499408845667e+00 -1.2985932549142798e+00 2.5888198678848857e+00 9.5645053119594277e+00 2.1215332248500019e+01 3.6152478620496041e+01 5.0021402817940384e+01 5.8774208038896376e+01 6.1292360127806575e+01 5.8550762166546200e+01 5.1882091757242364e+01 4.2968986888033839e+01 3.3918424522864342e+01 2.6125731850668569e+01 1.9638134139516286e+01 1.3840450777647449e+01 8.3996525004490294e+00 3.8021795817073860e+00 1.1190628236490561e+00 7.5962516407305214e-01 1.4058807055741134e+00 8.2163118862213358e-01 -2.0188943881722592e+00 -6.0499550558624495e+00 -9.4755634193659208e+00 -1.1248638187248371e+01 -1.0876954767354995e+01 -7.8849814096440030e+00 -2.2277330312359691e+00 5.4138464272246747e+00 1.3796881454212489e+01 2.0984396320719696e+01 2.4893133374375935e+01 2.4942654855570076e+01 2.2326104353812248e+01 1.8202892776374178e+01 1.2706268587571685e+01 5.8859262076676249e+00 -1.1947559660524161e+00 -6.5689254319385437e+00 -8.7772879515580264e+00 -8.1899879887551386e+00 -6.5832072140625550e+00 -5.4688753261733805e+00 -4.9175403512819100e+00 -3.8699212831075047e+00 -1.6574438279084263e+00 8.5455765389835547e-01 2.0911729100665450e+00 1.8202474430112003e+00 1.6089416985684442e+00 2.9743808071641795e+00 5.8948563383564005e+00 + -3.3338514474187440e+00 -4.1130389374748058e+00 -5.1547704706642552e+00 -5.4193898987702704e+00 -4.0201652371671948e+00 -1.8076212696539353e+00 -8.9324938220319039e-01 -1.9868709786288514e+00 -3.3105995821020633e+00 -2.6515298234057667e+00 5.1903435078819071e-01 4.9231535819829615e+00 8.5469168923530585e+00 1.0071331008104004e+01 9.9283440319809362e+00 9.9281856288240444e+00 1.1689028944723646e+01 1.5180332000597723e+01 1.8495089144282854e+01 1.9468749633612084e+01 1.7774035163293483e+01 1.4795935112020807e+01 1.1632060118366278e+01 8.3107388002809923e+00 4.7969367455220766e+00 1.5228719476515731e+00 -1.2245839851149136e+00 -3.6724142837818849e+00 -6.1258400070447641e+00 -8.3043113945111795e+00 -9.2382296924864278e+00 -8.2818369343941640e+00 -6.3024540208842872e+00 -5.1153884354297716e+00 -5.4012598664097871e+00 -5.9086039260782988e+00 -5.2469498739198066e+00 -3.8701397078727497e+00 -3.4699836914403539e+00 -4.6999263838482115e+00 -6.4704904874754252e+00 -7.1253409419073517e+00 -5.1911313857217234e+00 -5.8248621615308416e-02 6.3735411041921815e+00 9.9842526040059774e+00 8.0901302020184040e+00 2.0765046689514737e+00 -4.0814624682782448e+00 -6.9335386550902367e+00 -5.3396830806898752e+00 -6.1569207664081205e-01 4.2371402928715165e+00 6.1453027828107594e+00 4.0729416305299182e+00 -5.6892224917549727e-01 -5.7266782214867291e+00 -9.4817569691134302e+00 -9.4466837493039364e+00 -4.4716815460577326e+00 2.5480231258059796e+00 6.3862725120871939e+00 4.7626827206150235e+00 2.7250354238558483e-01 + -6.6519531318265823e+00 -5.1307688760840637e+00 -1.5990450796308475e+00 3.2882196698155921e+00 7.5434887899979923e+00 9.8601744151731392e+00 1.0431338476778441e+01 1.0132207096708374e+01 9.8633193310657052e+00 1.0117849589142009e+01 1.0556320935364319e+01 1.0529237565608828e+01 1.0064399849178292e+01 9.8902096970315743e+00 1.0802538876610429e+01 1.3065613777540369e+01 1.5904751019353327e+01 1.7996878960824926e+01 1.8887262226773334e+01 1.9137392198836288e+01 1.8914272666242024e+01 1.7510508054314936e+01 1.4350155185280446e+01 9.5073777371400769e+00 3.7350791719348706e+00 -9.6044902572999469e-01 -2.1523757668680674e+00 1.2726446256922297e-01 2.7975425794223376e+00 3.1310049165812357e+00 1.4400885075259127e+00 -9.4699453877260531e-03 5.2252907539724913e-01 2.9730000242572210e+00 5.8714424054021102e+00 7.9092364851498598e+00 8.7344188791480644e+00 7.9030314193993458e+00 4.5624392185691374e+00 -5.8583551307959691e-01 -4.3527827486539321e+00 -3.8542689679266942e+00 -3.3407572539104535e-02 3.0190256438646816e+00 2.5671043830395353e+00 -2.7379787916712028e-01 -2.8159393124422332e+00 -3.9496120934673513e+00 -4.3869096765280995e+00 -4.8326892938827388e+00 -4.8327167767960031e+00 -3.6048592531173460e+00 -1.4049856452797012e+00 6.1244691414035868e-01 1.4045344432842721e+00 4.0378165896259843e-01 -2.2594911404427971e+00 -5.2758916592334257e+00 -6.6711436023807975e+00 -5.4988956630405870e+00 -2.7595297745975564e+00 -4.3083978002099643e-01 1.2310669289941012e-02 -1.8074724020788719e+00 + -2.5417468582312877e+00 -1.9298735695592950e-01 1.4524591906329190e+00 1.4111623962585536e+00 -3.3194621049731687e-01 -2.5702905387021753e+00 -3.3145376224302021e+00 -1.6531002526600589e+00 1.5085470936791965e+00 4.9384825610490894e+00 8.1158180601966112e+00 1.0592317046651424e+01 1.1720352844683582e+01 1.1731547138555802e+01 1.1880590058094510e+01 1.2779413370052000e+01 1.4075646883094780e+01 1.5918248275430194e+01 1.8326489365386571e+01 1.9316206123129387e+01 1.6690629155310674e+01 1.1661681239753394e+01 8.0053968635282153e+00 7.5533291045585287e+00 8.4805002524471416e+00 7.9431009917907147e+00 4.7893096294793169e+00 -2.9205326061448478e-01 -5.9299905729305458e+00 -1.0889025474342041e+01 -1.4253802911511571e+01 -1.5424425634403272e+01 -1.4219613607718578e+01 -1.0988129270893715e+01 -6.7271231240914489e+00 -2.8803629271713325e+00 -5.3221218228894118e-01 -1.4819897860306691e-01 -2.2309622192950278e+00 -6.7555441266626337e+00 -1.1288796495269558e+01 -1.1879369455774969e+01 -7.6695223775198915e+00 -2.7345250210875287e+00 -1.2464522929563033e+00 -2.5982520939275595e+00 -3.2907753012277650e+00 -2.0946248103194671e+00 -1.2670396502483379e+00 -2.9734875330694628e+00 -6.1944576306095183e+00 -8.0259110770747917e+00 -7.0406747672630017e+00 -4.3818830819913117e+00 -2.2337026899490038e+00 -1.7757039249321303e+00 -2.1690903031406541e+00 -1.7112483834128271e+00 -2.2443726961269805e-01 7.8312271984712389e-01 7.2340304513709708e-01 9.8143716696324113e-01 2.5456848227724063e+00 4.1446278797649949e+00 + 5.9509298374097952e+00 5.0678810528340978e+00 4.6985100256137695e+00 5.6806474798139019e+00 7.1270807068985986e+00 7.3896907513926973e+00 6.1956015245970972e+00 5.5852197604153861e+00 7.9143806050507850e+00 1.2903336221013145e+01 1.7478615210727796e+01 1.8733526717119613e+01 1.6557524998442712e+01 1.3237833414525580e+01 1.1194587213855545e+01 1.1742137197718762e+01 1.5292487123411739e+01 2.1089872768798958e+01 2.6615090233197662e+01 2.9112067948790987e+01 2.8381112807191901e+01 2.6745309556602031e+01 2.6230622139582557e+01 2.6873626067912664e+01 2.7379954661291766e+01 2.6724918542001461e+01 2.5352283105591539e+01 2.4558243711441179e+01 2.4278527981980282e+01 2.2429473534878777e+01 1.7501264178786681e+01 1.0987541340332779e+01 5.6451288588220212e+00 1.5426875751835269e+00 -4.2209755312065607e+00 -1.3196792287091672e+01 -2.2788705236686901e+01 -2.9082273423343587e+01 -3.1121693514905303e+01 -3.0849786691440926e+01 -2.9901975701231901e+01 -2.8187902353798506e+01 -2.4897002861955549e+01 -1.9629404309588349e+01 -1.3381778061564034e+01 -8.8077830839680740e+00 -8.0332756560395637e+00 -1.0120926859300432e+01 -1.2004163217689651e+01 -1.1842485990658060e+01 -1.0575962516702482e+01 -1.0149295488895902e+01 -1.0971618377841839e+01 -1.1645676947992518e+01 -1.0824020132311130e+01 -8.7473035146007145e+00 -6.7816366706045397e+00 -5.8695256679524599e+00 -6.1984801392743734e+00 -7.8537905857434129e+00 -1.0416960930281348e+01 -1.2429989647191839e+01 -1.2650740393840184e+01 -1.1521198276477271e+01 + ] ) - check("sparse.mat", result) - - matfile = matopen("partial.mat") - var1 = read(matfile, "var1") - @assert var1[28, 33] == 5 - var2 = read(matfile, "var2") - @assert var2[27, 90] == 10 - close(matfile) - + check(joinpath(dirname(@__FILE__), "big_endian.mat"), result) end -result = Dict( - "index" => [8.8604784000000000e+04 9.8707212000000000e+04 1.0394035200000000e+05 1.1429712000000000e+05 1.5474923999999999e+05 1.5475435200000001e+05 1.5501074400000001e+05 1.5505315200000000e+05 1.5505718400000001e+05 1.5506168400000001e+05 1.5506524799999999e+05 5.4945741599999997e+05 5.6345896799999999e+05 5.9956981200000003e+05 7.0691623199999996e+05 7.9063657200000004e+05 8.4311938800000004e+05 9.2225131200000003e+05 1.1248994160000000e+06 1.2508148520000000e+06 1.4164141320000000e+06 1.4275988280000000e+06 1.4744331000000001e+06 1.4982212879999999e+06 1.5549058440000000e+06 1.5870300840000000e+06 1.6192005120000001e+06 1.6766071560000000e+06 1.9386816839999999e+06 1.9969427879999999e+06 2.0021861880000001e+06 2.3272494120000000e+06 2.5309351080000000e+06 2.6743788720000000e+06], - "spikes" => [ - -3.9146236245031032e+00 -6.7657651330021364e+00 -1.0780027188484372e+01 -1.4345619557780790e+01 -1.5488781013877338e+01 -1.3241531877846004e+01 -8.6339302778751907e+00 -4.1571900578409995e+00 -1.4845719040296610e+00 2.3147400250828232e-01 2.8917910181412778e+00 6.4067867244186800e+00 8.3368575385567603e+00 7.0732985406218223e+00 4.4095174940268036e+00 3.8495932342509342e+00 7.0605464919276546e+00 1.2892731012948772e+01 1.8593404980539656e+01 2.1332908128411184e+01 2.0142332517120792e+01 1.6740473413471157e+01 1.3650330377340575e+01 1.1913871749214691e+01 1.0804794411826084e+01 8.8366401987297127e+00 5.1092331749990514e+00 5.1218216653980408e-01 -2.9327647633922682e+00 -4.4870896208146753e+00 -5.0598199463728655e+00 -4.8330524336350118e+00 -2.8556000012645000e+00 2.9794817723619027e-01 1.8265416505730325e+00 -8.6155940979615875e-02 -3.9623352473810947e+00 -6.9070013227561047e+00 -7.3941131196997647e+00 -5.7411207637544166e+00 -3.2366812420300106e+00 -1.1460492068000723e+00 1.2381260731009580e-01 1.0930145325605314e+00 2.1927876983540933e+00 2.6570284430776856e+00 1.3381366125210661e+00 -1.2539624260623763e+00 -3.3642620416729994e+00 -4.1849749207505456e+00 -3.8760400918509301e+00 -2.6869552030388291e+00 -1.6718246062697015e+00 -2.3709942853677934e+00 -4.6623835517993664e+00 -6.6575320887201714e+00 -6.9891263747717174e+00 -5.7017039068420186e+00 -3.4759011423153079e+00 -1.7092931352045238e+00 -2.3854494206243695e+00 -5.8068462168496913e+00 -9.1001745572212531e+00 -8.8479323560036516e+00 - -5.0745139212223540e+00 -9.6338046242625506e+00 -1.3472006614220170e+01 -1.5245910823426385e+01 -1.5914718584705716e+01 -1.6397777086548157e+01 -1.5688652912166024e+01 -1.2863641020969972e+01 -9.5044110719151487e+00 -8.3463890698794305e+00 -1.0177024989874276e+01 -1.3445743563344164e+01 -1.6692449999817043e+01 -1.9680052373752346e+01 -2.0121942799899024e+01 -8.3700572911264981e+00 3.0032951243048100e+01 9.4582543108269547e+01 1.5377989075611117e+02 1.7130866838277802e+02 1.4748486763605970e+02 1.0947277486760775e+02 7.2684596239613015e+01 3.8193102601464858e+01 1.0540246012312117e+01 -3.8091287155762972e+00 -5.9643997244651556e+00 -3.0829122045048947e+00 -9.3969579584981044e-01 -4.1011214330806744e-01 1.1485354724913588e-01 8.3209939369519947e-01 1.7132389429717065e-03 -3.6057351605809611e+00 -8.8203434292989460e+00 -1.2692989286632969e+01 -1.3074091545751195e+01 -1.0580269175201330e+01 -7.7262110976707312e+00 -6.1406117115548504e+00 -5.0818589866084025e+00 -3.3150901769832091e+00 -1.7731765969545847e+00 -2.5627661695922046e+00 -5.6404917366725886e+00 -8.3842617579066747e+00 -8.5769172149145199e+00 -6.8248629640086911e+00 -5.7747794329330571e+00 -7.1063437611930578e+00 -9.7162880960954556e+00 -1.1170428842658552e+01 -1.0378119546974839e+01 -8.3419883744685741e+00 -6.9209209230224635e+00 -7.4145555946251820e+00 -9.6713314325255784e+00 -1.2062946758371979e+01 -1.2965017154593347e+01 -1.2408773058757985e+01 -1.1624355346583785e+01 -1.1127849526328186e+01 -1.0048811168388408e+01 -7.6327226475166228e+00 - -3.6172800577681850e+00 -3.0496107525626650e+00 -4.0450961689646379e+00 -4.9841853952779340e+00 -4.3137243411079638e+00 -2.4211699563520748e+00 -9.3469136675988018e-01 -9.3834475312754062e-01 -2.3043613016166664e+00 -3.9905808684096256e+00 -4.6842071022425413e+00 -3.8310453992051836e+00 -2.8818230979207713e+00 -5.2038837297130556e+00 -1.1725055393804819e+01 -1.3208091586845086e+01 9.2482123494256854e+00 6.3181823866266974e+01 1.2177486062412244e+02 1.4070171740815530e+02 1.0609880427064739e+02 4.8491974791356185e+01 5.8467389218168400e+00 -9.6802170255950291e+00 -8.6605173391900614e+00 -5.1555616199626577e+00 -5.4237382663488169e+00 -8.2575373220146044e+00 -1.0156241701964554e+01 -9.1574620977201118e+00 -5.6665700132115431e+00 -1.5504659563007614e+00 1.1334383183944798e+00 1.2640431009400714e+00 -6.6794751018025034e-01 -2.9543901520038176e+00 -4.1963329340429310e+00 -4.3642482086638417e+00 -4.0042244694146101e+00 -3.1394295409389494e+00 -1.8179756080601912e+00 -9.3024527470805374e-01 -1.4736656790371958e+00 -3.4645773489091209e+00 -5.8588062538620047e+00 -7.0734948204842896e+00 -6.0481893429370261e+00 -3.3344016972431088e+00 -4.8650896484328832e-01 1.6052150099271567e+00 2.9838901666777322e+00 3.7836445807114574e+00 3.9345735120816623e+00 3.1644840267225094e+00 1.0070686684300534e+00 -2.4713922778659478e+00 -5.9984216012494951e+00 -7.8885829262850233e+00 -7.5154376078825695e+00 -5.8326333185237758e+00 -4.5003931071444478e+00 -4.2328547992043450e+00 -4.1140676583157889e+00 -3.1460951038002301e+00 - -3.6781701228839154e+01 -3.7303084834807457e+01 -3.8884510028175633e+01 -4.1148035399962872e+01 -4.3316242853459642e+01 -4.4384384580854572e+01 -4.4790855343692897e+01 -4.7038755365884079e+01 -5.2501365276546458e+01 -5.6812084874163638e+01 -4.9570438054352273e+01 -2.3981805280442412e+01 1.1015352613446286e+01 3.7025927055247365e+01 4.6196840812364755e+01 4.5435239856395953e+01 4.4108776047185387e+01 4.5957015883875258e+01 4.9338962205718175e+01 5.0907715343642117e+01 4.9487314118838199e+01 4.6376644839165742e+01 4.2907993314061748e+01 3.9175485818078528e+01 3.4646364450142663e+01 2.9438295855526434e+01 2.5290001942034166e+01 2.4585557992414284e+01 2.7869881512937130e+01 3.2856199330415350e+01 3.6123557386279117e+01 3.5905020129260436e+01 3.3291843240497656e+01 3.0657151735220140e+01 2.9399978478943620e+01 2.9238216866099457e+01 2.8701738303043562e+01 2.6077460100630137e+01 2.0689205683518288e+01 1.3779281580459998e+01 8.1712538443203133e+00 6.6925061080176569e+00 1.0147005424138726e+01 1.6509827576474752e+01 2.2276297357800129e+01 2.4765444980789738e+01 2.3565808601878054e+01 2.0365852060539318e+01 1.7482427576467405e+01 1.5922922611993604e+01 1.4534460949429915e+01 1.1655908219543699e+01 7.5216074902940893e+00 3.9334852970087364e+00 1.7985868168036778e+00 3.9751100517608062e-01 -9.6689135481901367e-01 -1.8291316756437888e+00 -1.2108732556384698e+00 1.1422814255806824e+00 4.0827052431935931e+00 5.6696392298883893e+00 4.7889899662105178e+00 1.9874261259261794e+00 - -1.1686614851500112e+00 9.8124871505894706e-01 2.4682927864975457e+00 2.9222372863110442e+00 1.6794130795786746e+00 -6.8820557107116076e-01 -2.4491510090825823e+00 -2.5573541785944220e+00 -1.5657335653505560e+00 -6.2326635393824792e-01 7.5542615186640072e-03 1.2160520506156904e+00 3.7959025732449647e+00 7.1695274203178112e+00 9.8085862745466752e+00 1.1024667458560756e+01 1.1849030121520164e+01 1.3645183550697372e+01 1.6153045543872672e+01 1.7653460112285362e+01 1.6917192047304358e+01 1.4287778990065465e+01 1.1181854556421472e+01 9.2813981144484536e+00 9.6980182244576554e+00 1.1975656236851366e+01 1.4218362205978007e+01 1.4823189770395057e+01 1.3782281414778801e+01 1.2224714159620135e+01 1.1308817315730568e+01 1.1413207645545857e+01 1.1802812942398777e+01 1.1304006120772028e+01 9.4673823725009267e+00 6.7674104407290443e+00 3.9034391463718467e+00 1.0696510964445158e+00 -2.2265622627917740e+00 -6.1619053880194183e+00 -9.4895708918063448e+00 -1.0511848180773416e+01 -9.2016379963305752e+00 -7.3655072801953869e+00 -6.6664600174160604e+00 -7.2291846775841488e+00 -8.2107481715781105e+00 -9.1021811818757321e+00 -1.0311278259980540e+01 -1.2741511638678721e+01 -1.6437776102727412e+01 -1.9514979330779195e+01 -1.9610190285680936e+01 -1.6644297020966704e+01 -1.2800100439249650e+01 -9.9046719354109900e+00 -8.2923627062655303e+00 -7.8966764067275435e+00 -8.8257663696547084e+00 -1.0735616318501446e+01 -1.2599202496900540e+01 -1.3220431400502429e+01 -1.1920594647006027e+01 -9.3084512934042376e+00 - 1.1099942135772691e+01 1.0097197127580243e+01 1.1065906114342178e+01 1.3374123078938322e+01 1.6449533911253752e+01 2.0017355902687680e+01 2.3638247141687746e+01 2.6292880854006093e+01 2.6783333166190332e+01 2.5183008648660202e+01 2.3296988595385344e+01 2.2810464758877668e+01 2.3624918756652875e+01 2.4607374587222779e+01 2.5393398749862719e+01 2.6769469521678889e+01 2.9281995275221178e+01 3.2213436799025317e+01 3.4285343581139983e+01 3.4785296465751173e+01 3.3883822298572078e+01 3.2386332385252516e+01 3.1231943637716320e+01 3.0932840342758819e+01 3.1246878043151604e+01 3.1160302181567783e+01 2.9550070933834700e+01 2.6540966708675843e+01 2.3785330200047358e+01 2.2611886801417548e+01 2.2531515536471172e+01 2.2340044415310590e+01 2.1822535590221452e+01 2.1399364443416083e+01 2.0755992097134378e+01 1.8948315967557182e+01 1.5769917402888659e+01 1.2205733819672014e+01 9.4099541981525228e+00 7.7046512181258402e+00 6.5719664417242711e+00 5.2343013227680553e+00 3.3389338216778657e+00 1.2573655827507051e+00 -5.0199990120897509e-01 -1.8987019795142805e+00 -2.9172592054133535e+00 -3.2152176994632509e+00 -2.9689210401896537e+00 -3.2720957493420673e+00 -4.8582690799070640e+00 -7.1594797906170751e+00 -9.1803890906391956e+00 -1.0495134027231670e+01 -1.1056911353417380e+01 -1.0726534970818260e+01 -9.3753223680626547e+00 -7.2907274381158134e+00 -5.1694262729300959e+00 -3.7312476583791785e+00 -3.6141234953544066e+00 -5.1662008322483448e+00 -7.7776079749577836e+00 -9.8010587078412321e+00 - -1.4685530727984641e+01 -1.0292328330297664e+01 -6.2040173116488546e+00 -2.3375015307800595e+00 1.3923917658511158e+00 5.0564769748901313e+00 8.6976966359851104e+00 1.2323245348083425e+01 1.5903913125353689e+01 1.9380777962659089e+01 2.2677898949762564e+01 2.5718454097742246e+01 2.8441073995486732e+01 3.0813082951470030e+01 3.2837981366611231e+01 3.4555661605993883e+01 3.6035311542317494e+01 3.7362431137561401e+01 3.8622571934083787e+01 3.9885068827368393e+01 4.1190035284914742e+01 4.2541236867236606e+01 4.3906275503545629e+01 4.5224046329587310e+01 4.6417965861852949e+01 4.7412309197387181e+01 4.8148368395523121e+01 4.8597180088665191e+01 4.8766260494684808e+01 4.8698991553936125e+01 4.8466780378761598e+01 4.8155568154983840e+01 4.7849401563031734e+01 4.7614370946271123e+01 4.7486145888939269e+01 4.7463615344086584e+01 4.7509911501785595e+01 4.7560611227134473e+01 4.7537464942025593e+01 4.7364890774312840e+01 4.6985915393397903e+01 4.6374354157541411e+01 4.5540779674504876e+01 4.4531077464674446e+01 4.3417878743397615e+01 4.2286593389902350e+01 4.1218852636871070e+01 4.0276692303289153e+01 3.9490658602725233e+01 3.8854229591844380e+01 3.8325674875439489e+01 3.7836979823323738e+01 3.7308039311521902e+01 3.6663265807716996e+01 3.5847270787923392e+01 3.4836464766174224e+01 3.3644242194420237e+01 3.2318708010392434e+01 3.0933403069436942e+01 2.9572894236760884e+01 2.8316128161485977e+01 2.7220897815058663e+01 2.6312547181929837e+01 2.5579187011419549e+01 - 1.5763956152315169e+01 1.6523243082892250e+01 1.7337427631570872e+01 1.8120936638484590e+01 1.8792445157811912e+01 1.9291006994381139e+01 1.9588077541817789e+01 1.9693009891213325e+01 1.9650918197573489e+01 1.9533337877188348e+01 1.9423561429962543e+01 1.9399599967723205e+01 1.9518198338118580e+01 1.9803119911795868e+01 2.0240058237539227e+01 2.0779199633821964e+01 2.1344921568275133e+01 2.1850676042659646e+01 2.2216063986224665e+01 2.2382665762479274e+01 2.2325443040524132e+01 2.2057418172501748e+01 2.1626689639781716e+01 2.1106384004760407e+01 2.0579565930487874e+01 2.0122142297473431e+01 1.9787200170220586e+01 1.9593930129327656e+01 1.9523364223698717e+01 1.9521786868823959e+01 1.9511132956806740e+01 1.9404280892123200e+01 1.9122163091585080e+01 1.8609252789360340e+01 1.7844318015586811e+01 1.6844290518050197e+01 1.5660474424296631e+01 1.4367827117720619e+01 1.3049394428891683e+01 1.1778975433290963e+01 1.0605636123380648e+01 9.5436591203415890e+00 8.5705174399266060e+00 7.6329046830511684e+00 6.6569045386579333e+00 5.5557424579780292e+00 4.2330232952205966e+00 2.5948387638090815e+00 6.0010139846456756e-01 -1.6403240489988979e+00 -3.7983903715983196e+00 -5.5143675763583140e+00 -7.0197817000212250e+00 -9.2991809829904906e+00 -1.2710108289977208e+01 -1.5773321560596763e+01 -1.6492611312664831e+01 -1.5291065260668367e+01 -1.5553348127486492e+01 -1.9596399167644933e+01 -2.5711586491024381e+01 -3.1294712703031326e+01 -3.6140274982880157e+01 -4.0736526948594026e+01 - -7.5455089032603496e+01 -8.8091850484650791e+01 -7.6894847494814442e+01 -5.1931618057453484e+01 -2.8833831644552824e+01 -6.8895988088626297e+00 2.1168348264522379e+01 4.6693994244353583e+01 5.3777387223823958e+01 4.4603130335345831e+01 3.3530654869558120e+01 2.3587826739136968e+01 8.4238787409792160e+00 -9.2318222698507917e+00 -1.4713136414898660e+01 1.9717165915666754e+00 3.1968641163889899e+01 5.7269930354848498e+01 7.0152688867705734e+01 7.3409072049238773e+01 7.0166235593951257e+01 6.1829204326045371e+01 4.6696134605544373e+01 1.8622054473547621e+01 -2.2829591964997910e+01 -6.2715671170700787e+01 -8.2653742502672856e+01 -8.1088328703707077e+01 -7.2961806812740548e+01 -7.3632394853923415e+01 -8.9841480609702060e+01 -1.1962228131535032e+02 -1.5066572610100792e+02 -1.6038121312642346e+02 -1.2981471355911390e+02 -6.3653163261418783e+01 9.3811209125033130e+00 5.8695741367623484e+01 7.0437047869761585e+01 4.9630793312944938e+01 1.6599415269056415e+01 -2.5402904227425278e+00 4.0559101821793178e+00 2.4429205550104435e+01 3.9021358713588448e+01 3.8520783430674051e+01 1.9952697815867261e+01 -2.1877257859243720e+01 -8.3907527060881065e+01 -1.4250639755111564e+02 -1.6619398657243511e+02 -1.3819218483975862e+02 -6.6785267226843359e+01 7.8497452778842174e+00 2.7663034892382331e+01 -1.9252281399357905e+01 -5.9646303432603780e+01 -1.8852103558667594e+01 6.7639555235119275e+01 9.6932460076734856e+01 4.1262889176984515e+01 -3.3610639804651925e+01 -7.8423083296187542e+01 -1.0839727633754131e+02 - 4.7736577112711117e+01 4.5700337234538978e+01 4.5322631494156710e+01 4.9171444949072850e+01 5.3757473474671947e+01 5.4141589236927501e+01 4.8790940853843097e+01 4.1857477273482019e+01 4.0754533608850998e+01 4.8608027316167508e+01 6.0100216178867996e+01 6.8180296544356494e+01 7.1421072557515430e+01 7.0807306032014466e+01 6.6514973934144194e+01 6.2945074970931898e+01 6.8196514720480167e+01 8.5012727237677268e+01 1.0720726826115141e+02 1.1957256746876999e+02 9.9138254538272335e+01 3.8069276174166163e+01 -2.7233524821672358e+01 -3.9334632563706222e+01 1.3886634524961096e+01 7.3584276961424322e+01 6.3628518198466388e+01 -2.9054572516934122e+01 -1.4209888641029568e+02 -2.0910522987863314e+02 -2.1415046392049794e+02 -1.7586701353513934e+02 -1.1844102599759782e+02 -6.1431963885049129e+01 -1.4806030025628601e+01 1.4027944301627443e+01 1.0652586257860358e+01 -3.0054086554043813e+01 -8.5072361204847368e+01 -1.2046643138190960e+02 -1.2106054250546094e+02 -9.5841527604366775e+01 -6.6055322458778861e+01 -4.4458770387691487e+01 -2.4336370991880798e+01 4.5840797349366902e-01 2.1040288319943066e+01 2.3611095638912182e+01 1.3051021255932143e+00 -2.8851154140515209e+01 -3.7619486391456242e+01 -2.9915658771811749e+01 -3.0708973922309450e+01 -3.1924567737368655e+01 -1.0202022943830535e+01 2.9706495601613049e+01 5.9067239546813376e+01 5.4702088800913174e+01 2.8343487643024787e+01 1.5951389347675942e+01 2.2837902575396686e+01 1.2908948645547047e+01 -3.3255358730535995e+01 -8.7746125812653432e+01 - -8.2377418542911354e+00 -3.4972931824407372e+00 -7.7569626244497876e+00 -1.5166206734460895e+01 -1.5445771078633909e+01 -5.2413228186791176e+00 8.4387175092257500e+00 1.5431757026108208e+01 1.2399978335869392e+01 7.4095099729504046e+00 1.0679579816928053e+01 1.8701188652942292e+01 1.7701213689196198e+01 4.8431711642004913e+00 -4.7779662604481938e+00 3.1723005655630221e+00 2.4531952072999687e+01 4.5698045473740862e+01 5.9257083049327122e+01 6.2627426268528140e+01 5.7411981420461260e+01 5.2570470576359099e+01 5.2813113343401156e+01 5.0453507704104304e+01 3.9691119012785791e+01 2.6528767918502940e+01 1.8741299694240368e+01 1.7724441294399568e+01 2.0821175647900695e+01 2.4417659444952996e+01 2.5870167533339281e+01 2.4983092349575369e+01 2.2998436116876093e+01 2.0558711472598521e+01 1.7502822279045098e+01 1.3739438787271743e+01 9.9370928813924273e+00 7.9696531198819107e+00 9.3237074864808100e+00 1.2496048137913172e+01 1.4202138161523894e+01 1.3776871923186704e+01 1.4057294551750237e+01 1.6919973545359703e+01 2.0190481439159552e+01 2.0371692873037446e+01 1.6567959095411027e+01 1.0520230640011906e+01 4.5794135735479564e+00 8.7710329733688541e-01 8.9182399282027713e-01 4.3463285075996909e+00 8.7315858121978973e+00 1.0903936631346470e+01 1.0288756426506817e+01 1.0049725699443105e+01 1.3054576528907717e+01 1.6854021523448839e+01 1.6041035800270304e+01 1.0367866836597134e+01 6.4265096090680434e+00 9.4949675891987368e+00 1.6836520945082036e+01 2.1325105933919993e+01 - -4.1679158148256432e+00 -5.2810404688221357e+00 -5.9038977834292563e+00 -6.2634044553555412e+00 -5.9749855839469195e+00 -4.3218694766614334e+00 -1.1788772851158797e+00 2.4374543533437842e+00 4.8050829080472326e+00 4.8908091608309068e+00 3.3699448624872437e+00 1.8764385857350361e+00 1.0658937699825455e+00 -2.7882253437288551e-01 -3.1356733669314134e+00 -4.1284337659893531e+00 4.9241012162347957e+00 2.8218865342091348e+01 5.5056325524296241e+01 6.5099843979883218e+01 4.9609447627592324e+01 2.1423693795580775e+01 2.5132863903596458e-01 -5.8604600478907667e+00 -2.3380828579082129e+00 1.9600991106694594e+00 2.5915747007003080e+00 2.0190047306038350e-01 -2.8577160575959448e+00 -5.2869354732703258e+00 -6.9099491413979388e+00 -7.7559496509908179e+00 -8.0339926236897785e+00 -8.1760789963289717e+00 -7.9859360445805283e+00 -6.3703713820186918e+00 -3.0321001946270005e+00 -2.8201117047924162e-02 -7.8351721876626979e-02 -3.2229219827610613e+00 -6.6022607253341592e+00 -7.6428337012652356e+00 -6.3520480710327867e+00 -4.5468067925800151e+00 -3.6792489626697655e+00 -3.7265752054412307e+00 -3.8104694479464527e+00 -3.1502606288908388e+00 -1.5302095841423975e+00 2.6517264914271876e-01 7.6568764912213050e-01 -5.6131323023144386e-01 -2.6412705089170849e+00 -4.2923899829303380e+00 -5.1769894068210993e+00 -4.9614565332821146e+00 -3.2859163281387294e+00 -1.1703534382204590e+00 -7.6910847698287388e-01 -2.6122681986842187e+00 -4.2742848318737003e+00 -3.0739009488926641e+00 6.8120601334287323e-01 3.9336266256502612e+00 - -4.6484775115089541e+00 -8.3581561181698660e+00 -1.0902142569692860e+01 -1.1511797760460409e+01 -9.8276834132977005e+00 -6.2404161549022161e+00 -1.8432175810231679e+00 1.9347492334298952e+00 4.2938226787082971e+00 5.6566425031547753e+00 6.9370739014200060e+00 8.7323889596425897e+00 1.1151007876295226e+01 1.3469254774199312e+01 1.4378076971481226e+01 1.3628707243729355e+01 1.2810890536991595e+01 1.3603571552449427e+01 1.5792969414511386e+01 1.7425203016009618e+01 1.6493276434801135e+01 1.2586445678344095e+01 7.3401463249477024e+00 3.2087842017129540e+00 1.5030100447830739e+00 1.3898465906994972e+00 8.8198090743093560e-01 -1.0493063115005812e+00 -3.7608545130075726e+00 -6.3949540211998350e+00 -9.0066585358720026e+00 -1.1745588278397154e+01 -1.3842632605375675e+01 -1.4123365855042982e+01 -1.2335802612795424e+01 -9.4739532170207532e+00 -6.3782197090918142e+00 -2.8832164731098446e+00 4.8447063694123349e-01 1.6128314119847449e+00 -4.1774621162461167e-01 -3.3629973208103769e+00 -4.5025431650456200e+00 -3.9290643942165806e+00 -3.7022642550448306e+00 -4.6991587651781321e+00 -5.6909794907882132e+00 -5.2906700399814426e+00 -3.7661436808309654e+00 -2.5302504318642383e+00 -2.0969878128246422e+00 -1.3265870686252779e+00 7.0961290345922357e-01 3.0385814819877943e+00 3.9548508946305954e+00 2.9954312845408939e+00 9.9988805713822959e-01 -9.2502312206375614e-01 -2.0384940259267319e+00 -2.2297243620823197e+00 -2.0769146009998161e+00 -2.3170471037592764e+00 -2.8506427004880215e+00 -2.6777747842245225e+00 - -7.7992155184377432e-01 8.4208602530413512e-01 2.4887283142722940e+00 2.4338293029048339e+00 8.3831805409641036e-01 -5.6099977991794492e-01 -7.1266715703072170e-01 -2.3092906084045711e-01 1.6955881483329160e-01 8.8504105274391720e-01 2.2423969436547071e+00 3.3863034262368776e+00 3.6616215908416012e+00 4.2752627336864073e+00 7.0581057720790223e+00 1.1758833658720096e+01 1.6047422019307188e+01 1.8405075233788665e+01 1.9427396781993370e+01 1.9879444229356857e+01 1.9348574654086402e+01 1.7629540788742673e+01 1.5792875449818970e+01 1.4872230427632566e+01 1.4471792816861928e+01 1.3426898347317799e+01 1.1319456367524939e+01 8.8575138135993203e+00 7.0846315948436089e+00 6.5127944804059057e+00 6.6781793515550252e+00 6.2758575263602134e+00 4.2213211609500574e+00 1.0187928511802555e+00 -1.3365602410322484e+00 -1.2247622425200726e+00 1.1481723220280773e+00 4.3321910406078903e+00 6.9648334152330520e+00 8.0154771868425438e+00 6.5719188712588483e+00 2.4474218319276826e+00 -3.0491523253799624e+00 -7.4154828913997903e+00 -8.5953677824666350e+00 -6.7813390775209372e+00 -4.2068257455106703e+00 -2.7309337921106196e+00 -2.0348235578019351e+00 -6.2767747718278888e-01 1.7379756336568186e+00 3.5393101620046572e+00 3.5444884355037840e+00 2.3746522750550279e+00 1.3500592448824880e+00 6.9976097822180139e-01 -1.5618921435797500e-01 -1.3162226599662721e+00 -2.4845798158016903e+00 -3.7797798352858396e+00 -5.2368125011457316e+00 -5.9486138818233414e+00 -5.1354371684134463e+00 -3.7528582973371494e+00 - -3.0447537175403006e+00 -1.7667805638958940e+00 -1.9778229750617011e+00 -2.2515604403315699e+00 -1.3449261643785633e+00 -1.0662626484421048e-01 -5.7657486030100791e-02 -7.4315041221388778e-01 -3.2337810363445102e-01 1.8577438758015754e+00 4.8425765941254983e+00 7.8123978114524020e+00 1.1105854493054094e+01 1.4841903991300153e+01 1.7468871344145363e+01 1.7574512951317725e+01 1.6451820522767886e+01 1.6628323973953727e+01 1.8301434258000356e+01 1.8931382166939795e+01 1.6217394592510296e+01 1.0559530299242045e+01 4.4576293834236687e+00 -1.1822470014800246e-02 -2.5727563770885062e+00 -3.7547505719161225e+00 -3.5081536657220189e+00 -1.4835065248806825e+00 1.8247576781703743e+00 4.8275703093820033e+00 5.7228574725963268e+00 3.8558888609578723e+00 4.0990146070366057e-01 -2.4574645653496310e+00 -3.1102243476353593e+00 -1.4474225309731716e+00 7.4127875680854416e-01 1.0383069562131020e+00 -1.0150232301722759e+00 -3.4198210424706135e+00 -4.3088303898183238e+00 -3.9165170232756070e+00 -3.6605958183355347e+00 -4.2469618414480417e+00 -5.2237494007048566e+00 -5.9420620236257076e+00 -6.1698135259889568e+00 -5.7089733374935268e+00 -4.0492707065696969e+00 -8.0849384208890562e-01 3.3216805308527921e+00 6.2990262694136261e+00 6.1812329879916819e+00 3.1266169114028388e+00 -4.8505368345761951e-01 -2.2625885070785667e+00 -2.0371523965480165e+00 -1.4033783831712108e+00 -1.2651726669536290e+00 -8.7131680693218405e-01 5.0106877171388586e-01 1.8406747295741790e+00 1.3085085083594870e+00 -1.5293730324086736e+00 - 2.3513445326289961e+00 1.7384880477187443e+00 2.2184897775735659e+00 5.2051223974837342e+00 1.0452816121087638e+01 1.5377084182445486e+01 1.7198975360259148e+01 1.6104713320124159e+01 1.4833420837246866e+01 1.5100670860303905e+01 1.6073099350257220e+01 1.6269109488250638e+01 1.5637757524959511e+01 1.5476499170789715e+01 1.6604024256326596e+01 1.8317676582762779e+01 1.9829075755286041e+01 2.1816151151864332e+01 2.5055347925633036e+01 2.7333907737059032e+01 2.2427805378565086e+01 3.7141556286936721e+00 -2.8648138173116212e+01 -6.3843957225821207e+01 -8.7089860614432553e+01 -9.1923311472937954e+01 -8.3938126428996938e+01 -7.2885847777427841e+01 -6.4297677965088411e+01 -5.7914366258401401e+01 -5.0470379597131561e+01 -3.9499120538780375e+01 -2.5751695307781368e+01 -1.1778187088014317e+01 1.0189820693630494e+00 1.1960524761686607e+01 1.8733303083800365e+01 1.8356414318508428e+01 1.0668647686854321e+01 -1.0075011878329772e+00 -1.2633619248121462e+01 -2.1404683668367543e+01 -2.5602377289037388e+01 -2.4560926814716566e+01 -1.8847733387633440e+01 -9.8404336731293878e+00 3.5966958558048301e-01 9.0662935627188972e+00 1.4335829915263089e+01 1.6036694595613060e+01 1.5184337823870287e+01 1.2469246394512929e+01 7.6677145828817306e+00 8.5165822752424969e-01 -5.8806346340252293e+00 -9.1084583818696743e+00 -7.2999534121027425e+00 -2.4813921667292815e+00 1.7215656085634032e+00 3.0197896060174911e+00 1.4579843578258131e+00 -1.6420102258369735e+00 -4.8732983588159042e+00 -6.9044980772313806e+00 - -7.1748224092297468e+00 -3.8936102456845245e+00 1.0743831294909865e+00 4.8816301487736782e+00 5.4189196440465226e+00 2.8421806913201926e+00 -7.8193972997551731e-01 -3.3953855854612938e+00 -3.9985877326731809e+00 -2.4284915455410818e+00 3.3095855413398589e-01 2.1860065858780535e+00 1.8280988855495470e+00 3.4048799490454618e-01 3.8790118108287441e-01 3.8556647578240639e+00 9.9718322097927121e+00 1.5912297294502212e+01 1.9511397698602284e+01 2.0651366980982488e+01 2.0134282655984631e+01 1.8560445836154724e+01 1.6287447146155721e+01 1.3561600277652611e+01 1.0795264078502715e+01 8.6030785401577266e+00 7.0022103875078896e+00 5.1888911547538754e+00 2.7145704050730384e+00 1.4143845469113936e-01 -1.9107456843838713e+00 -3.4559243419646326e+00 -4.2223452047916581e+00 -3.1130434517080205e+00 1.7183260785086762e-01 3.7869557639525033e+00 5.3380153379310666e+00 4.2352058165010487e+00 1.6573488592368961e+00 -8.5887188564406203e-01 -2.1681503131724709e+00 -2.1279908375294667e+00 -2.0792749208808359e+00 -3.3997834567720187e+00 -5.3580052019440032e+00 -5.9792374734377622e+00 -4.8403202446726707e+00 -3.3487890303481347e+00 -2.6600981462061033e+00 -2.7228934357253367e+00 -3.1308053901450172e+00 -3.8963449071942557e+00 -5.2729284759196471e+00 -7.2764195946828067e+00 -9.3283947520693822e+00 -1.0227824562298673e+01 -9.0739885142293417e+00 -6.5681575524140818e+00 -4.8038541028704778e+00 -5.3349275900632449e+00 -7.6025577687023391e+00 -9.3130362450711317e+00 -8.3152888687476345e+00 -4.2484255376825795e+00 - -1.2094820634316039e+00 -1.2706817164948352e+00 -1.2775018026388452e+00 -2.3357176552894954e-01 1.8928499829725349e+00 4.4677863928736414e+00 6.9513585826945681e+00 9.0204123867514259e+00 1.0482452392061710e+01 1.1455309752506835e+01 1.2237810752650413e+01 1.2688637793112969e+01 1.2235313891126804e+01 1.1100297213735251e+01 1.0773414547739115e+01 1.2217993905654721e+01 1.4701466325393794e+01 1.7319490559451381e+01 1.9808972202344144e+01 2.1257689253929254e+01 2.0368628657184544e+01 1.7524563363309646e+01 1.4843567969750833e+01 1.3863574192388871e+01 1.4031045614008303e+01 1.3693669004482535e+01 1.2122436338523961e+01 9.9467781708780620e+00 7.8498090003437868e+00 5.7967683127528629e+00 3.7206536395890204e+00 2.3267777177674382e+00 2.6303789001713365e+00 4.7445695138569235e+00 7.6683738268993942e+00 9.9717476628589878e+00 1.0271279571889663e+01 8.1958760665984958e+00 5.3998213275104048e+00 4.2899793853628481e+00 5.5462911375305124e+00 7.6595253723593695e+00 8.4981448223169700e+00 7.2294579833209385e+00 5.1143564806110922e+00 3.8260968045635870e+00 3.3065755059745818e+00 2.5815464286120089e+00 1.7488042411202225e+00 1.5184709880337026e+00 1.6370045591241176e+00 1.1531897091681369e+00 -1.5863929796862442e-01 -1.5759013113892080e+00 -2.3911490323759002e+00 -2.4147368159491429e+00 -1.8749121657426793e+00 -1.4086065112641286e+00 -1.6573036686621481e+00 -2.4019595691049194e+00 -2.7197582423545761e+00 -2.3228923158464436e+00 -1.8987417992229161e+00 -1.9762436025825927e+00 - -2.1788608089091870e+00 -2.3721017929273520e+00 -2.2248744539174794e+00 -1.3562719882732486e+00 1.9303974836178317e-01 1.3110471548593794e+00 1.2797866409946126e+00 1.1446512790093115e+00 2.4218364442454297e+00 5.3568199699566073e+00 9.1928444896243846e+00 1.2911855868338753e+01 1.5131303558516208e+01 1.4796675429288374e+01 1.2747818159896775e+01 1.1384776546745211e+01 1.2304499348204516e+01 1.5012766622841385e+01 1.7706586949808607e+01 1.8615946254339946e+01 1.7088985921306737e+01 1.4118105664819083e+01 1.1547667913836323e+01 1.0268505344072013e+01 9.5426651513383796e+00 8.3936752159092531e+00 6.7898835952875585e+00 4.9190137058105190e+00 2.3035160443938620e+00 -1.4454604882529436e+00 -5.5578141560543060e+00 -8.6285337833508056e+00 -9.8768639485094845e+00 -9.3497459396540972e+00 -7.4531759065479113e+00 -4.8343191161214989e+00 -2.2707557414183874e+00 -2.1273932165940168e-01 1.4763518504563440e+00 2.9929246806770484e+00 3.6484933755646152e+00 1.9940019916984792e+00 -2.3622776610960083e+00 -7.2558689942915287e+00 -9.3484617741468909e+00 -7.4954744905729234e+00 -3.9544592514927057e+00 -1.7075406868672076e+00 -1.4165546781975049e+00 -1.6221832630448796e+00 -1.0260840386708110e+00 1.6403282116014173e-01 6.0805828587662303e-01 -7.8558049118458273e-01 -3.6478240721938118e+00 -6.4046749238136291e+00 -7.7290809125845756e+00 -7.3840184127203816e+00 -5.7754694634749093e+00 -3.6798105136804447e+00 -2.5013142267663810e+00 -3.3639333122560284e+00 -5.7371424805126106e+00 -8.0051890648261459e+00 - -1.5740146351038696e+01 -1.5127188568935047e+01 -1.6157259030112712e+01 -1.7330034352768784e+01 -1.7263465597740716e+01 -1.6336957355980775e+01 -1.5946556228923413e+01 -1.6451747846316870e+01 -1.6476134055902989e+01 -1.4472580313892262e+01 -1.0935457247727644e+01 -7.9876368021814939e+00 -6.0265186692348891e+00 -2.0146808151690361e+00 8.0163390797194936e+00 2.5505537996776390e+01 4.7787314568418353e+01 6.8639540131510500e+01 8.1758985020608336e+01 8.5294009078541549e+01 8.2283381938562812e+01 7.6550409062053944e+01 6.9344678720567757e+01 6.0023044834365038e+01 4.8849934461625729e+01 3.8040291720134526e+01 2.9945383165919644e+01 2.5344624124046817e+01 2.3679402477458130e+01 2.3454441703713346e+01 2.2281261219059516e+01 1.7989994660541914e+01 1.0436193819072196e+01 2.1102305275848425e+00 -3.6577185659374600e+00 -5.5145847757485971e+00 -4.5324408328651318e+00 -1.8554008253732013e+00 2.7089378387782199e+00 9.2187555916040793e+00 1.5945566856263504e+01 2.0472163602212458e+01 2.2294516323769198e+01 2.2881411820375533e+01 2.2911237311157720e+01 2.1364859320014958e+01 1.7602082925628398e+01 1.2398805245993104e+01 6.8726800470759422e+00 1.6538594259187884e+00 -2.9437120155973195e+00 -6.5367981004048668e+00 -8.6437256178931250e+00 -9.1231101796868952e+00 -8.6648085339554566e+00 -8.4735847323575335e+00 -8.9963274994615468e+00 -9.3198443907122499e+00 -8.2504681130406006e+00 -5.5321797455350028e+00 -1.9690195798275383e+00 9.0573194795780632e-01 1.6386927935969133e+00 3.9409445444748448e-01 - -1.2097853394633344e+00 -2.3372661136012782e+00 -4.5868126352335468e+00 -7.7696106024369840e+00 -9.8847492594673341e+00 -8.4351485269365991e+00 -3.4815504396923176e+00 2.0318425367749775e+00 5.4688409099903446e+00 6.8155210844128202e+00 7.5289034993747936e+00 8.2470533843717533e+00 8.5005207895890198e+00 8.1730893089208134e+00 8.1217939375154362e+00 9.2472099093868128e+00 1.1682275429968531e+01 1.4843432519914167e+01 1.7625325319637877e+01 1.8827596749646286e+01 1.8262944365019820e+01 1.7257770010849043e+01 1.7139858491940043e+01 1.7319082568288064e+01 1.5832240015066093e+01 1.1889468934637401e+01 7.0260166885118247e+00 3.4084227701886709e+00 1.5903858359584313e+00 6.4628466394257855e-01 2.9208281149091886e-01 1.3680522046018677e+00 3.7148191371537109e+00 5.3471095100773640e+00 4.5999975157196396e+00 1.8618036691603201e+00 -1.1675467460557312e+00 -3.2014095470489683e+00 -4.1751621005792883e+00 -4.4441528031142692e+00 -3.7587484122597536e+00 -2.0282422842477690e+00 -4.8339177039881087e-01 -2.6711860399748077e-01 -3.8790549777032224e-01 8.5572500658487782e-01 3.0975713952792994e+00 4.3374149891908393e+00 3.2920941855788466e+00 3.9295487513328070e-01 -2.4783937608256150e+00 -3.2055601186298843e+00 -1.7160184560799154e+00 -6.3702741131976026e-01 -2.2383884758776662e+00 -5.5570116369254112e+00 -8.2084983887639105e+00 -9.5917739048230395e+00 -1.0331039070410487e+01 -1.0417944274770905e+01 -9.6648501662164410e+00 -8.3293893029282344e+00 -6.5549338022834966e+00 -4.3174230553074713e+00 - -1.9521686822118689e+01 -2.0828890952194818e+01 -2.1026487257169343e+01 -2.0644194340000958e+01 -2.0356661177644074e+01 -2.0486033910028034e+01 -2.0592315338305198e+01 -1.9336484142295909e+01 -1.5938473018577051e+01 -1.1921626681007268e+01 -9.9587671774048481e+00 -1.0450212320468967e+01 -1.0318268307692627e+01 -5.6239283444522234e+00 5.5321151308175374e+00 2.2417493712423962e+01 4.2078909484239603e+01 5.9973160754273493e+01 7.1702694488382562e+01 7.5072713982579657e+01 7.0924560394362075e+01 6.2663339872630232e+01 5.4356911122207862e+01 4.7989283986803173e+01 4.2765855626087045e+01 3.7486699590135927e+01 3.2734101027576685e+01 2.9993606241210717e+01 2.9069222310304095e+01 2.7645584279325117e+01 2.3767330134444784e+01 1.7501891196670105e+01 1.0097030989509490e+01 3.3105933448286935e+00 -4.4358692407392453e-01 4.2309373559202790e-01 4.6272362980750925e+00 9.2452802627883770e+00 1.2881829396441868e+01 1.6139200491488932e+01 1.9696338650752981e+01 2.2945254570047432e+01 2.4052713164028159e+01 2.1836262493236529e+01 1.7755371912217502e+01 1.4544324719156007e+01 1.3076166058655325e+01 1.2023365019266210e+01 9.9330468323536198e+00 6.5642773658552329e+00 2.6211689892425389e+00 -1.2463040312411424e+00 -4.6931152521918520e+00 -7.0610915480926959e+00 -7.8896098442295468e+00 -8.1377876309837323e+00 -9.2879431013150882e+00 -1.1118321847684978e+01 -1.1512230767538767e+01 -8.6570795004612968e+00 -3.0565564637606264e+00 2.6614202613936517e+00 5.7492933678780842e+00 5.1870636768943843e+00 - -1.2768528401242900e+01 -1.7338863074172309e+01 -2.0237569092156448e+01 -2.0576492513490894e+01 -1.9431123286614632e+01 -1.8641772316619317e+01 -1.8943170420368556e+01 -1.9167043655793719e+01 -1.7528873877656416e+01 -1.3999909814005159e+01 -1.0805366953545310e+01 -9.6142597372311247e+00 -8.9149462386606366e+00 -5.4471587606889269e+00 2.2906924892770721e+00 1.2948039993449356e+01 2.3895472387004183e+01 3.2785154492570712e+01 3.8224124421928316e+01 3.9996932598102731e+01 3.8917325110890907e+01 3.6404969952831500e+01 3.3603549356134771e+01 3.0482131529017348e+01 2.6418551514199979e+01 2.1753968035179085e+01 1.7865996396778279e+01 1.5605745867809237e+01 1.4445507979298107e+01 1.3294236667868901e+01 1.1519440091973062e+01 9.2364945825762419e+00 7.2532314388618229e+00 6.2602214003637382e+00 5.4987881290564387e+00 3.2320466512581985e+00 -9.0506248164303926e-01 -4.9456160363826012e+00 -6.3608335770462485e+00 -4.2734547891318062e+00 4.6309862256935785e-01 6.7021945441538548e+00 1.3111777285636606e+01 1.7514480785911530e+01 1.8248810290360364e+01 1.5766174142459235e+01 1.1757377659322158e+01 7.9733313460406592e+00 6.0274343310982506e+00 6.1770944745629279e+00 6.2936748428494491e+00 4.0368463533344734e+00 -4.2685170771143977e-02 -3.1865409383399417e+00 -3.8901205566515773e+00 -2.9576974863775090e+00 -1.4738094422502874e+00 4.0118819835881747e-01 2.6525695143418675e+00 4.6014650691595245e+00 5.1565376735843209e+00 4.0636905672697523e+00 2.6453975037372648e+00 2.3284476158012066e+00 - -1.3007839852110759e+01 -1.1025538159980380e+01 -1.0686371074924564e+01 -1.2744092667984233e+01 -1.5642265829843563e+01 -1.7140214084438682e+01 -1.6617034625505930e+01 -1.5300909110451405e+01 -1.4577809836556332e+01 -1.4955079002970214e+01 -1.5654258710135920e+01 -1.4407648124599390e+01 -9.0954740513659118e+00 -6.2982400317265430e-02 1.0332127909943164e+01 2.0193720207165487e+01 2.8972473785793127e+01 3.6317935037933722e+01 4.1210257687869564e+01 4.2631382536124164e+01 4.0972150948544574e+01 3.8156297398596216e+01 3.5717588235143509e+01 3.3207917004761363e+01 2.9304640994990272e+01 2.4112801189469973e+01 1.9353557646954098e+01 1.6308754098888908e+01 1.4516954476871140e+01 1.2392183920492100e+01 8.3965380716467521e+00 2.2326430594848903e+00 -4.3996278411324354e+00 -8.8062298459598374e+00 -9.2226405800100828e+00 -5.6733723455263254e+00 3.7975785593939992e-02 5.0496912837583618e+00 7.5061362215685907e+00 8.1297047935707027e+00 8.9880765155299258e+00 1.0960262021995606e+01 1.3066019970002715e+01 1.3738034632375317e+01 1.1896051602413694e+01 7.5099861905659733e+00 2.1853151873444245e+00 -1.4107524690116966e+00 -1.7374936024760426e+00 4.7474258642688838e-02 1.2777176662851293e+00 5.9151627918367655e-01 -9.4200465373474518e-01 -1.3835697125582160e+00 -1.1439767142326973e-02 2.0431818226269547e+00 2.8931387751496276e+00 1.7217225031925740e+00 -3.6057387583206468e-01 -1.4188595163897433e+00 -5.3401986141825453e-01 1.7331384545428845e+00 4.1028021413802733e+00 5.1530807234519207e+00 - -5.9423907462970762e+00 -6.6393370296983978e+00 -7.7903948031970467e+00 -9.0887616470108981e+00 -9.0730958856624682e+00 -7.3051960638397269e+00 -5.1092125692791504e+00 -4.1499019811620803e+00 -5.1825095304735012e+00 -7.4650822660744725e+00 -8.7858341576439987e+00 -6.9165862119378128e+00 -1.1152780425762092e+00 7.4456360751231196e+00 1.5504355240213883e+01 1.9891995742575336e+01 2.1880531164099967e+01 2.5705009499507383e+01 3.1274011045582068e+01 3.4010306181075244e+01 3.2770325333513270e+01 3.0948804502035770e+01 2.9539194246932787e+01 2.6197352223405726e+01 2.0544443634266784e+01 1.4590436095771173e+01 9.4132762585289544e+00 5.0726802461989733e+00 1.8845604581530462e+00 -2.0830751870538844e-01 -2.0248221543626945e+00 -3.9513021238798549e+00 -5.3937857883630116e+00 -5.7622214722998990e+00 -5.0964393510051575e+00 -3.6685468807689410e+00 -1.9925824037801600e+00 -1.3116773229001528e+00 -2.6369224209795075e+00 -4.8818299037998507e+00 -5.7575190263162872e+00 -4.9151597170028811e+00 -4.2580797535905948e+00 -5.3162000432783687e+00 -8.0260447395650854e+00 -1.1124603514599908e+01 -1.2372738496954803e+01 -9.9327640511974646e+00 -4.8837906274471230e+00 -8.7396451836104794e-01 -1.7270469647056230e-01 -1.5776571699924549e+00 -2.7982572746004415e+00 -3.1062409337303580e+00 -3.0845221509902201e+00 -3.0432684148630136e+00 -2.5011943998532136e+00 -1.1481969001182208e+00 3.2261647132979526e-01 7.8134574976169557e-01 -1.2641763242828769e-01 -1.4193228153058111e+00 -1.7047633076684265e+00 -8.4627935364228413e-01 - -3.3511583497758619e+00 -5.1854891048629845e+00 -8.2045548190539002e+00 -1.1321927344458468e+01 -1.3232156516018623e+01 -1.2891455905593750e+01 -1.0308983110741357e+01 -7.2770589395120400e+00 -6.3230641518260899e+00 -7.7668924711145673e+00 -8.5533116261484974e+00 -5.1754472720840532e+00 2.5464912688278725e+00 1.1150146477409665e+01 1.6749765390457195e+01 1.8459480819125350e+01 1.8853443625394785e+01 2.0968383227964456e+01 2.4745356436846293e+01 2.6900729147509413e+01 2.4586028405507879e+01 1.8423893823872788e+01 1.1687464461034605e+01 7.4694246602111161e+00 6.5063740610776568e+00 6.6399254866014452e+00 4.8727847407542608e+00 6.2948434274342469e-01 -3.8180925201354374e+00 -5.9745650071051903e+00 -5.0248037653094384e+00 -1.7240345099049890e+00 2.4707226972860665e+00 6.1789893211512830e+00 8.4606300098802265e+00 9.1837056122684562e+00 9.4175247215558571e+00 1.0361111194478536e+01 1.1355825197106681e+01 1.0443694556801294e+01 7.3275805488596522e+00 4.0729400267880624e+00 2.7849489919865249e+00 4.0449011920910980e+00 6.8903062423979726e+00 8.8579270913084418e+00 7.2409153007186635e+00 1.9815538314749910e+00 -3.4963983686766915e+00 -5.7025552070066450e+00 -4.6316183670653945e+00 -2.8570944089319230e+00 -2.1945685959883381e+00 -2.7295179820568904e+00 -4.1440094951781976e+00 -6.1739578627827907e+00 -7.9441831061071788e+00 -8.3008782660488869e+00 -7.1697557563545278e+00 -5.6642787357607363e+00 -4.5049090776747240e+00 -3.1919541675350729e+00 -9.9748944534117390e-01 1.8822644573222371e+00 - 1.6022978144703672e+00 -1.4311786252489094e+00 -4.9990745496272506e+00 -7.1229734861916816e+00 -6.0298488474363783e+00 -1.8843154305695582e+00 2.8057312397445893e+00 5.3045500670547181e+00 5.2217607939007324e+00 4.6015143428315151e+00 5.3985890315998715e+00 7.6627280386075034e+00 1.0581969356003244e+01 1.3680704869130523e+01 1.6133038629367640e+01 1.6836134617835810e+01 1.5985757935422852e+01 1.5071121283266066e+01 1.5015214850097710e+01 1.5311877473734373e+01 1.4952636894507775e+01 1.3509895518873545e+01 1.1287529167684998e+01 8.8944719667341001e+00 6.8056307961237392e+00 5.1599082109694479e+00 3.9096368310365088e+00 3.0884796567205659e+00 2.8027781380901557e+00 2.7427378963657629e+00 1.8080112317158328e+00 -7.2093739358622477e-01 -3.2256888875692162e+00 -2.5485135905025946e+00 2.1938957914977939e+00 7.8096722519713868e+00 1.0108587362109473e+01 8.0119639048454889e+00 3.7875674256494771e+00 -1.7909623605085379e-01 -3.6408942410859053e+00 -7.2043954289116119e+00 -1.0036996806105844e+01 -1.0480191286127010e+01 -8.6104715538045582e+00 -6.5737607267403781e+00 -6.4789888225111065e+00 -8.9832745466307422e+00 -1.3098938079216277e+01 -1.6488189562785699e+01 -1.7072074337181476e+01 -1.4675862164708811e+01 -1.0393673234405423e+01 -5.7706460525680994e+00 -3.2783034075657795e+00 -4.8699351422351356e+00 -9.0537498769047904e+00 -1.1681066334269010e+01 -1.0244235531230570e+01 -5.9719523689580347e+00 -1.9644630163354910e+00 -6.8837039873644690e-01 -2.7600482002062674e+00 -6.6765535392302047e+00 - -2.6793902875688205e+00 4.3700049391893547e-01 5.2207839165959946e-01 -2.5289383815408999e+00 -6.4357928708025778e+00 -8.6801895727348093e+00 -8.6519420551714941e+00 -7.5157946794804973e+00 -6.9229481363763350e+00 -7.7994153444953156e+00 -9.3675453247907754e+00 -1.0006058503692298e+01 -9.5066143246051311e+00 -9.1193248923592503e+00 -9.0487355120456403e+00 -6.9972012375818347e+00 -1.6948530860574240e-01 1.0752688787226244e+01 2.0728963668504598e+01 2.4824092328193505e+01 2.2702194691381209e+01 1.8103111981612606e+01 1.5186220180215653e+01 1.5435411462559298e+01 1.6851176785915300e+01 1.6256911281706046e+01 1.2853830842714240e+01 8.7992906000849906e+00 6.3848237032232804e+00 6.1798137367968202e+00 7.8154545356284144e+00 1.0467840599577846e+01 1.2480989360982822e+01 1.2582337684046703e+01 1.1590145096205708e+01 1.1337803770903093e+01 1.2054473739609797e+01 1.2250402949861966e+01 1.0983877637654345e+01 8.6551288071765491e+00 5.8174031977868585e+00 2.9188587271482644e+00 8.3930412496566609e-01 2.3622649323786860e-01 7.1444423031664739e-01 1.2655853456754595e+00 1.3229336834759118e+00 1.3578720434847311e+00 2.1076821170226498e+00 3.0728270343869779e+00 2.7843524321077324e+00 9.0852714062149698e-01 -1.0017936404451253e+00 -1.3974751167516584e+00 -6.4988794902308711e-01 -5.0807738982896855e-01 -2.0685431131427148e+00 -4.6510643108399936e+00 -6.4865750445095642e+00 -6.4858500824649585e+00 -5.1351447719989922e+00 -3.4051840546128562e+00 -1.7184951490663052e+00 -5.0407691071619776e-01 - -3.6981497476642828e+00 -3.0390710048817726e+00 -1.4989922553639727e+00 1.0284003810800462e+00 3.6622719317496890e+00 4.7174258831055269e+00 3.5121017080473034e+00 2.1663525068616294e+00 3.8933497703894613e+00 8.9756839635313099e+00 1.4369540972251285e+01 1.7517537513748934e+01 1.8367043951422612e+01 1.7487527428352760e+01 1.4910602011410518e+01 1.1788546361447585e+01 1.0695666486764603e+01 1.2768873027185609e+01 1.6000928788239836e+01 1.7360605935279679e+01 1.5930200170467559e+01 1.3260280784655849e+01 1.0849461771852765e+01 8.4797732256321510e+00 5.3784098602866708e+00 1.7165101724011995e+00 -1.4089618767998704e+00 -2.7952639837436752e+00 -2.2276522503149407e+00 -9.5316276326730420e-01 -5.3299313777074875e-01 -1.5019739358173170e+00 -3.5515201972803432e+00 -6.3187612986855708e+00 -9.3137337609793054e+00 -1.1318424950944941e+01 -1.0816559293228659e+01 -7.7508782461822854e+00 -4.2600195254335116e+00 -2.8712335676010809e+00 -4.2724290569942225e+00 -6.8899881431600578e+00 -8.1742447353742449e+00 -6.8082430068650392e+00 -3.9299615464428408e+00 -1.4896688083453651e+00 -4.1155002553450060e-02 6.2487647569787685e-01 -2.9020287195242966e-01 -3.8853316012394776e+00 -9.2125322820605025e+00 -1.3050926297558480e+01 -1.2737108773295276e+01 -8.7818443279864002e+00 -4.3127261410662650e+00 -2.3302722768023965e+00 -3.5325752167188584e+00 -6.2805892811261916e+00 -8.5016614679139195e+00 -9.4759820546177966e+00 -9.7204496390574704e+00 -9.7546714698047463e+00 -9.5752380341344967e+00 -9.3036539221877597e+00 - -1.7414985024478632e+01 -1.7710392269365567e+01 -1.8830345987045778e+01 -2.0905808395741552e+01 -2.3089781413981267e+01 -2.3899680760146289e+01 -2.2409641713666709e+01 -1.8930485800816182e+01 -1.4601034805586801e+01 -1.0520204067883871e+01 -7.1082264501151942e+00 -4.1771499408845667e+00 -1.2985932549142798e+00 2.5888198678848857e+00 9.5645053119594277e+00 2.1215332248500019e+01 3.6152478620496041e+01 5.0021402817940384e+01 5.8774208038896376e+01 6.1292360127806575e+01 5.8550762166546200e+01 5.1882091757242364e+01 4.2968986888033839e+01 3.3918424522864342e+01 2.6125731850668569e+01 1.9638134139516286e+01 1.3840450777647449e+01 8.3996525004490294e+00 3.8021795817073860e+00 1.1190628236490561e+00 7.5962516407305214e-01 1.4058807055741134e+00 8.2163118862213358e-01 -2.0188943881722592e+00 -6.0499550558624495e+00 -9.4755634193659208e+00 -1.1248638187248371e+01 -1.0876954767354995e+01 -7.8849814096440030e+00 -2.2277330312359691e+00 5.4138464272246747e+00 1.3796881454212489e+01 2.0984396320719696e+01 2.4893133374375935e+01 2.4942654855570076e+01 2.2326104353812248e+01 1.8202892776374178e+01 1.2706268587571685e+01 5.8859262076676249e+00 -1.1947559660524161e+00 -6.5689254319385437e+00 -8.7772879515580264e+00 -8.1899879887551386e+00 -6.5832072140625550e+00 -5.4688753261733805e+00 -4.9175403512819100e+00 -3.8699212831075047e+00 -1.6574438279084263e+00 8.5455765389835547e-01 2.0911729100665450e+00 1.8202474430112003e+00 1.6089416985684442e+00 2.9743808071641795e+00 5.8948563383564005e+00 - -3.3338514474187440e+00 -4.1130389374748058e+00 -5.1547704706642552e+00 -5.4193898987702704e+00 -4.0201652371671948e+00 -1.8076212696539353e+00 -8.9324938220319039e-01 -1.9868709786288514e+00 -3.3105995821020633e+00 -2.6515298234057667e+00 5.1903435078819071e-01 4.9231535819829615e+00 8.5469168923530585e+00 1.0071331008104004e+01 9.9283440319809362e+00 9.9281856288240444e+00 1.1689028944723646e+01 1.5180332000597723e+01 1.8495089144282854e+01 1.9468749633612084e+01 1.7774035163293483e+01 1.4795935112020807e+01 1.1632060118366278e+01 8.3107388002809923e+00 4.7969367455220766e+00 1.5228719476515731e+00 -1.2245839851149136e+00 -3.6724142837818849e+00 -6.1258400070447641e+00 -8.3043113945111795e+00 -9.2382296924864278e+00 -8.2818369343941640e+00 -6.3024540208842872e+00 -5.1153884354297716e+00 -5.4012598664097871e+00 -5.9086039260782988e+00 -5.2469498739198066e+00 -3.8701397078727497e+00 -3.4699836914403539e+00 -4.6999263838482115e+00 -6.4704904874754252e+00 -7.1253409419073517e+00 -5.1911313857217234e+00 -5.8248621615308416e-02 6.3735411041921815e+00 9.9842526040059774e+00 8.0901302020184040e+00 2.0765046689514737e+00 -4.0814624682782448e+00 -6.9335386550902367e+00 -5.3396830806898752e+00 -6.1569207664081205e-01 4.2371402928715165e+00 6.1453027828107594e+00 4.0729416305299182e+00 -5.6892224917549727e-01 -5.7266782214867291e+00 -9.4817569691134302e+00 -9.4466837493039364e+00 -4.4716815460577326e+00 2.5480231258059796e+00 6.3862725120871939e+00 4.7626827206150235e+00 2.7250354238558483e-01 - -6.6519531318265823e+00 -5.1307688760840637e+00 -1.5990450796308475e+00 3.2882196698155921e+00 7.5434887899979923e+00 9.8601744151731392e+00 1.0431338476778441e+01 1.0132207096708374e+01 9.8633193310657052e+00 1.0117849589142009e+01 1.0556320935364319e+01 1.0529237565608828e+01 1.0064399849178292e+01 9.8902096970315743e+00 1.0802538876610429e+01 1.3065613777540369e+01 1.5904751019353327e+01 1.7996878960824926e+01 1.8887262226773334e+01 1.9137392198836288e+01 1.8914272666242024e+01 1.7510508054314936e+01 1.4350155185280446e+01 9.5073777371400769e+00 3.7350791719348706e+00 -9.6044902572999469e-01 -2.1523757668680674e+00 1.2726446256922297e-01 2.7975425794223376e+00 3.1310049165812357e+00 1.4400885075259127e+00 -9.4699453877260531e-03 5.2252907539724913e-01 2.9730000242572210e+00 5.8714424054021102e+00 7.9092364851498598e+00 8.7344188791480644e+00 7.9030314193993458e+00 4.5624392185691374e+00 -5.8583551307959691e-01 -4.3527827486539321e+00 -3.8542689679266942e+00 -3.3407572539104535e-02 3.0190256438646816e+00 2.5671043830395353e+00 -2.7379787916712028e-01 -2.8159393124422332e+00 -3.9496120934673513e+00 -4.3869096765280995e+00 -4.8326892938827388e+00 -4.8327167767960031e+00 -3.6048592531173460e+00 -1.4049856452797012e+00 6.1244691414035868e-01 1.4045344432842721e+00 4.0378165896259843e-01 -2.2594911404427971e+00 -5.2758916592334257e+00 -6.6711436023807975e+00 -5.4988956630405870e+00 -2.7595297745975564e+00 -4.3083978002099643e-01 1.2310669289941012e-02 -1.8074724020788719e+00 - -2.5417468582312877e+00 -1.9298735695592950e-01 1.4524591906329190e+00 1.4111623962585536e+00 -3.3194621049731687e-01 -2.5702905387021753e+00 -3.3145376224302021e+00 -1.6531002526600589e+00 1.5085470936791965e+00 4.9384825610490894e+00 8.1158180601966112e+00 1.0592317046651424e+01 1.1720352844683582e+01 1.1731547138555802e+01 1.1880590058094510e+01 1.2779413370052000e+01 1.4075646883094780e+01 1.5918248275430194e+01 1.8326489365386571e+01 1.9316206123129387e+01 1.6690629155310674e+01 1.1661681239753394e+01 8.0053968635282153e+00 7.5533291045585287e+00 8.4805002524471416e+00 7.9431009917907147e+00 4.7893096294793169e+00 -2.9205326061448478e-01 -5.9299905729305458e+00 -1.0889025474342041e+01 -1.4253802911511571e+01 -1.5424425634403272e+01 -1.4219613607718578e+01 -1.0988129270893715e+01 -6.7271231240914489e+00 -2.8803629271713325e+00 -5.3221218228894118e-01 -1.4819897860306691e-01 -2.2309622192950278e+00 -6.7555441266626337e+00 -1.1288796495269558e+01 -1.1879369455774969e+01 -7.6695223775198915e+00 -2.7345250210875287e+00 -1.2464522929563033e+00 -2.5982520939275595e+00 -3.2907753012277650e+00 -2.0946248103194671e+00 -1.2670396502483379e+00 -2.9734875330694628e+00 -6.1944576306095183e+00 -8.0259110770747917e+00 -7.0406747672630017e+00 -4.3818830819913117e+00 -2.2337026899490038e+00 -1.7757039249321303e+00 -2.1690903031406541e+00 -1.7112483834128271e+00 -2.2443726961269805e-01 7.8312271984712389e-01 7.2340304513709708e-01 9.8143716696324113e-01 2.5456848227724063e+00 4.1446278797649949e+00 - 5.9509298374097952e+00 5.0678810528340978e+00 4.6985100256137695e+00 5.6806474798139019e+00 7.1270807068985986e+00 7.3896907513926973e+00 6.1956015245970972e+00 5.5852197604153861e+00 7.9143806050507850e+00 1.2903336221013145e+01 1.7478615210727796e+01 1.8733526717119613e+01 1.6557524998442712e+01 1.3237833414525580e+01 1.1194587213855545e+01 1.1742137197718762e+01 1.5292487123411739e+01 2.1089872768798958e+01 2.6615090233197662e+01 2.9112067948790987e+01 2.8381112807191901e+01 2.6745309556602031e+01 2.6230622139582557e+01 2.6873626067912664e+01 2.7379954661291766e+01 2.6724918542001461e+01 2.5352283105591539e+01 2.4558243711441179e+01 2.4278527981980282e+01 2.2429473534878777e+01 1.7501264178786681e+01 1.0987541340332779e+01 5.6451288588220212e+00 1.5426875751835269e+00 -4.2209755312065607e+00 -1.3196792287091672e+01 -2.2788705236686901e+01 -2.9082273423343587e+01 -3.1121693514905303e+01 -3.0849786691440926e+01 -2.9901975701231901e+01 -2.8187902353798506e+01 -2.4897002861955549e+01 -1.9629404309588349e+01 -1.3381778061564034e+01 -8.8077830839680740e+00 -8.0332756560395637e+00 -1.0120926859300432e+01 -1.2004163217689651e+01 -1.1842485990658060e+01 -1.0575962516702482e+01 -1.0149295488895902e+01 -1.0971618377841839e+01 -1.1645676947992518e+01 -1.0824020132311130e+01 -8.7473035146007145e+00 -6.7816366706045397e+00 -5.8695256679524599e+00 -6.1984801392743734e+00 -7.8537905857434129e+00 -1.0416960930281348e+01 -1.2429989647191839e+01 -1.2650740393840184e+01 -1.1521198276477271e+01 - ] -) -check(joinpath(dirname(@__FILE__), "big_endian.mat"), result) - -# test reading of mxOBJECT_CLASS (#57) -let objtestfile = "obj.mat" - vars = matread(joinpath(dirname(@__FILE__), objtestfile)) - # check if all variables were read - for key in ("A", "tm", "signal") - @test key in keys(vars) +@testset "test reading of mxOBJECT_CLASS (#57)" begin + let objtestfile = "obj.mat" + vars = matread(joinpath(dirname(@__FILE__), objtestfile)) + # check if all variables were read + for key in ("A", "tm", "signal") + @test key in keys(vars) + end + # check if class name was read correctly + @test vars["A"]["class"] == "Assoc" end - # check if class name was read correctly - @test vars["A"]["class"] == "Assoc" end -# test reading of empty struct -let objtestfile = "empty_struct.mat" - vars = matread(joinpath(dirname(@__FILE__), objtestfile)) - @test "a" in keys(vars) - @test vars["a"]["size"] == [] - @test vars["a"]["params"] == [] +@testset "test reading of empty struct" begin + let objtestfile = "empty_struct.mat" + vars = matread(joinpath(dirname(@__FILE__), objtestfile)) + @test "a" in keys(vars) + @test vars["a"]["size"] == [] + @test vars["a"]["params"] == [] + end end -# test reading of a Matlab figure -let objtestfile = "figure.fig" - vars = matread(joinpath(dirname(@__FILE__), objtestfile)) - @test "hgS_070000" in keys(vars) - @test vars["hgS_070000"]["handle"] == 1.0 - @test vars["hgS_070000"]["type"] == "figure" +@testset "test reading of a Matlab figure" begin + let objtestfile = "figure.fig" + vars = matread(joinpath(dirname(@__FILE__), objtestfile)) + @test "hgS_070000" in keys(vars) + @test vars["hgS_070000"]["handle"] == 1.0 + @test vars["hgS_070000"]["type"] == "figure" + end end + # test reading file containing Matlab function handle, table, and datetime objects # since we don't support these objects, just make sure that there are no errors # reading the file and that the variables are there and replaced with `missing` -let objtestfile = "function_handles.mat" - vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile)) - @test "sin" in keys(vars) - @test ismissing(vars["sin"]) - @test "anonymous" in keys(vars) - @test ismissing(vars["anonymous"]) +@testset "test reading unsupported function_handles" begin + let objtestfile = "function_handles.mat" + vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile)) + @test "sin" in keys(vars) + @test ismissing(vars["sin"]) + @test "anonymous" in keys(vars) + @test ismissing(vars["anonymous"]) + end end -let objtestfile = "struct_table_datetime.mat" - vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile))["s"] - @test "testTable" in keys(vars) - @test ismissing(vars["testTable"]) - @test "testDatetime" in keys(vars) - @test ismissing(vars["testDatetime"]) + +@testset "test reading unsupported struct, table, and datetime objects" begin + let objtestfile = "struct_table_datetime.mat" + vars = matread(joinpath(dirname(@__FILE__), "v7.3", objtestfile))["s"] + @test "testTable" in keys(vars) + @test ismissing(vars["testTable"]) + @test "testDatetime" in keys(vars) + @test ismissing(vars["testDatetime"]) + end end diff --git a/test/write.jl b/test/write.jl index 3a1d820..d9e6c6d 100644 --- a/test/write.jl +++ b/test/write.jl @@ -96,9 +96,11 @@ test_write(Dict( "sparse_zeros" => SparseMatrixCSC(20, 20, ones(Int, 21), Int[], Float64[]) )) -@test_throws ErrorException test_write(Dict("1invalidkey" => "starts with a number")) -@test_throws ErrorException test_write(Dict("another invalid key" => "invalid characters")) -@test_throws ErrorException test_write(Dict("yetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkey" => "too long")) +@testset "write exceptions" begin + @test_throws ErrorException test_write(Dict("1invalidkey" => "starts with a number")) + @test_throws ErrorException test_write(Dict("another invalid key" => "invalid characters")) + @test_throws ErrorException test_write(Dict("yetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkeyyetanotherinvalidkey" => "too long")) +end struct TestCompositeKind field1::AbstractString From 229c0fc5efd2f9b15b188824a13c8298dba0ad70 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Tue, 28 Feb 2023 15:45:52 -0600 Subject: [PATCH 50/91] add DataFrames dependency --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 01bdac1..43394a3 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.10.5" [deps] BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" From 0da7b00098fef091935dba7f94d9399d4fd44d75 Mon Sep 17 00:00:00 2001 From: Brian Quadras Date: Fri, 19 May 2023 22:31:17 +0530 Subject: [PATCH 51/91] Added support for Dymola .mat files --- src/MAT_v4_Modelica.jl | 24 ++++++++++++------------ test/runtests_modelica.jl | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index 55eac3b..ca611c9 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -134,7 +134,7 @@ function readAclass( filepath::String ) end nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - name = replace(String(nameuint), '\0'=>"") + name = strip(replace(String(nameuint), '\0'=>"")) if name != "Aclass" error("First matrix must be named Aclass, is instead [$name]. This likely means that [$filepath] is not a Modelica MAT v4 file.") end @@ -143,10 +143,10 @@ function readAclass( filepath::String ) fmt = dataFormat(dtype) # read the format type before reading realint = read!(matio, Matrix{UInt8}(undef, nrows,ncols)) - Aclass1 = replace(String(realint[1,:]), '\0'=>"") - Aclass2 = replace(String(realint[2,:]), '\0'=>"") - Aclass3 = replace(String(realint[3,:]), '\0'=>"") - Aclass4 = replace(String(realint[4,:]), '\0'=>"") + Aclass1 = strip(replace(String(realint[1,:]), '\0'=>"")) + Aclass2 = strip(replace(String(realint[2,:]), '\0'=>"")) + Aclass3 = strip(replace(String(realint[3,:]), '\0'=>"")) + Aclass4 = strip(replace(String(realint[4,:]), '\0'=>"")) if Aclass1 == "Atrajectory" && Aclass2 == "1.1" && isempty(Aclass3) && Aclass4 == "binNormal" || Aclass4 == "binTrans" return Aclass( filepath, Aclass4 == "binTranspose", startP, position(matio) ) end @@ -180,7 +180,7 @@ function readVariableNames(ac::Aclass) #read the matrix name nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") + matrixName = strip(replace(String(nameuint), '\0'=>"")) if matrixName != "name" error("trying to read matrix [name] but read [$matrixName]") end @@ -197,7 +197,7 @@ function readVariableNames(ac::Aclass) #pull the names out of the matrix vnames = [] for i in 1:ncols - push!(vnames, replace(String(realint[:,i]), '\0'=>"")) # note :,1 = implicit transpose + push!(vnames, strip(replace(String(realint[:,i]), '\0'=>""))) # note :,1 = implicit transpose end return VariableNames(vnames, startP, position(matio)) @@ -247,7 +247,7 @@ function readVariableDescriptions(ac::Aclass, vn::VariableNames) #read the matrix name nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") + matrixName = strip(replace(String(nameuint), '\0'=>"")) if matrixName != "description" error("trying to read matrix [description] but read [$matrixName]") end @@ -263,7 +263,7 @@ function readVariableDescriptions(ac::Aclass, vn::VariableNames) vdesc = [] for i in 1:ncols - push!(vdesc, replace(String(realread[:,i]), '\0'=>"")) # note :,1 = implicit transpose + push!(vdesc, strip(replace(String(realread[:,i]), '\0'=>""))) # note :,1 = implicit transpose end return VariableDescriptions(vn.names, vdesc, startP, position(matio)) @@ -306,7 +306,7 @@ function readDataInfo(ac::Aclass, vd::VariableDescriptions) #read the matrix name nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") + matrixName = strip(replace(String(nameuint), '\0'=>"")) if matrixName != "dataInfo" error("trying to read variable [dataInfo] but read [$matrixName]") end @@ -355,7 +355,7 @@ function readMatrixHeader!(matio::IOStream) :: MatrixHeader # data1MatrixName = mark(matio) nameuint = read!(matio, Vector{UInt8}(undef, namelen)) # read the full namelen to make the pointer ready to read the data - matrixName = replace(String(nameuint), '\0'=>"") + matrixName = strip(replace(String(nameuint), '\0'=>"")) fmt = dataFormat(dtype) # read the format type before reading @@ -406,7 +406,7 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d return readns end - elseif name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 + elseif name == "Time" || name == "time" || di.info[varInd]["locatedInData"] == 2 #data_2 #read the matrix data_2 if ac.isTranspose == false # data is sequential: time(t0), var1(t0), var2(t0),... varN(t0), time(t1), var1(t1),... diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 2e29e4e..ce7e9a8 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -15,8 +15,8 @@ include("../src/MAT.jl") #OpenModelica v1.19.0 matBBO = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_res.mat") matFBB = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_res.mat") -#Dymola v2021 -- not implemented -# matBBD = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") +#Dymola v2021 +matBBD = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") @testset "isLittleEndian" begin @@ -85,7 +85,7 @@ end @test di.info[11]["isWithinTimeRange"] == 0 end -@testset "readVariable: BouncingBall" begin +@testset "readVariable: BouncingBall OpenModelica" begin ac = MAT.MAT_v4_Modelica.readAclass(matBBO) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) @@ -112,6 +112,33 @@ end @test isapprox(vel[2], -0.981, rtol=1e-3) end +@testset "readVariable: BouncingBall Dymola" begin + ac = MAT.MAT_v4_Modelica.readAclass(matBBD) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) + + eff = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "eff") #data1 + @test length(eff) == 2 + @test eff[1] ≈ 0.77 + @test eff[2] ≈ 0.77 + + grav = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "grav") #data1 + @test length(grav) == 2 + @test grav[1] ≈ 9.81 + @test grav[2] ≈ 9.81 + + Time = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "Time") # data0 + @test all(isapprox.(Time, [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1], rtol=1e-3)) + + height = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "height") #data2 + @test isapprox(height[1], 111, rtol=1e-3) + @test isapprox(height[2], 110.9509, rtol=1e-3) + + vel = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "vel") #data2 + @test isapprox(vel[2], -0.981, rtol=1e-3) +end + @testset "readVariable: FallingBodyBox" begin ac = MAT.MAT_v4_Modelica.readAclass(matFBB) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) From 3b719d3f710e98616827333ef47a44ed16011506 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Fri, 9 Jun 2023 15:34:13 -0500 Subject: [PATCH 52/91] remove DataFrames --- Project.toml | 1 - src/MAT_v4_Modelica.jl | 45 +-------------------------------------- test/runtests_modelica.jl | 24 ++++++++++----------- 3 files changed, 13 insertions(+), 57 deletions(-) diff --git a/Project.toml b/Project.toml index 43394a3..01bdac1 100644 --- a/Project.toml +++ b/Project.toml @@ -5,7 +5,6 @@ version = "0.10.5" [deps] BufferedStreams = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" -DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index ca611c9..0922162 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -1,5 +1,5 @@ # A module to read MAT files written by OpenModelica tools - + # Copyright (C) 2023 Ben Conrad # # Permission is hereby granted, free of charge, to any person obtaining @@ -47,7 +47,6 @@ module MAT_v4_Modelica -using DataFrames function isLittleEndian(dtype) :: Bool #The type flag contains an integer whose decimal digits encode storage information. If the integer is represented as MOPT where M is the thousands digit... @@ -425,46 +424,4 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d end #open end - -""" -All-in-one reading of variable `name` from `filepath`, returning a DataFrame with columns "time" and `name` -""" -function readVariable(filepath::String, name::String) :: DataFrame - ac = readAclass(filepath) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) - di = readDataInfo(ac,vd) - - time = readVariable(ac, vn, vd, di, "time") - varn = readVariable(ac, vn, vd, di, name) - - df = DataFrame("time"=>time, name=>varn) - return df -end - -""" -Reads the vector of variable `names` from mat file `filepath`, returning a DataFrame with columns "time" and `names` -""" -function readVariables(filepath::String, names::AbstractVector{T}) :: DataFrame where T<:AbstractString - ac = readAclass(filepath) - vn = readVariableNames(ac) - vd = readVariableDescriptions(ac,vn) - di = readDataInfo(ac,vd) - - df = DataFrame("time"=> readVariable(ac, vn, vd, di, "time") ) - for name in names - var = readVariable(ac,vn,vd,di, name) - if length(var) == 1 # a constant value - df[!, name] .= var[1] - elseif length(var) == 2 && var[1] == var[2] - df[!, name] .= var[1] - elseif length(var) == length(df.time) - df[!, name] = var - else - throw(DimensionMismatch("Length of $name [$(length(var))] differs from the dataframe [$(length(df.time))], cannot add it")) - end - end - return df -end - end #MAT_v4_Modelica \ No newline at end of file diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index ce7e9a8..8ca0b90 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -172,18 +172,18 @@ end @test isapprox(var[1], 1.0, rtol=1e-3) end -@testset "all-in-one readVariable" begin - data = MAT.MAT_v4_Modelica.readVariable(matFBB, "bodyBox.frame_a.r_0[1]") - @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) - @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(matFBB, "nullVariable") -end - -@testset "all-in-one readVariables" begin - data = MAT.MAT_v4_Modelica.readVariables(matFBB, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) - @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) - @test isapprox(data[!,"bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) - @test isapprox(data[!,"world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time -end +# @testset "all-in-one readVariable" begin +# data = MAT.MAT_v4_Modelica.readVariable(matFBB, "bodyBox.frame_a.r_0[1]") +# @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) +# @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(matFBB, "nullVariable") +# end + +# @testset "all-in-one readVariables" begin +# data = MAT.MAT_v4_Modelica.readVariables(matFBB, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) +# @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) +# @test isapprox(data[!,"bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) +# @test isapprox(data[!,"world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time +# end ; From 46faf3b91f940bd035d3aa282d203092709a06e1 Mon Sep 17 00:00:00 2001 From: Ben Conrad Date: Fri, 9 Jun 2023 16:41:47 -0500 Subject: [PATCH 53/91] add isMatV4Modelica() --- src/MAT_v4_Modelica.jl | 20 +++++++ test/runtests_modelica.jl | 49 +++++++++--------- ...Ball_res.mat => BouncingBall_om1.19.0.mat} | Bin ...ox_res.mat => FallingBodyBox_om1.19.0.mat} | Bin 4 files changed, 44 insertions(+), 25 deletions(-) rename test/v4_Modelica/BouncingBall/{BouncingBall_res.mat => BouncingBall_om1.19.0.mat} (100%) rename test/v4_Modelica/FallingBodyBox/{FallingBodyBox_res.mat => FallingBodyBox_om1.19.0.mat} (100%) diff --git a/src/MAT_v4_Modelica.jl b/src/MAT_v4_Modelica.jl index 0922162..1dedddb 100644 --- a/src/MAT_v4_Modelica.jl +++ b/src/MAT_v4_Modelica.jl @@ -424,4 +424,24 @@ function readVariable(ac::Aclass, vn::VariableNames, vd::VariableDescriptions, d end #open end +""" +Determine whether the `filepath` is a mat file in the Modelica format. +""" +function isMatV4Modelica(filepath::String) + #first check the extension + ret = splitext(filepath)[2] == ".mat" || splitext(filepath)[2] == ".MAT" + + #now we need to interrogate the contents, by ensuring that all of the internal functions return correctly + ac = readAclass(filepath) + vn = readVariableNames(ac) + vd = readVariableDescriptions(ac,vn) + di = readDataInfo(ac,vd) + + ret &= typeof(ac) == Aclass + ret &= typeof(vn) == VariableNames + ret &= typeof(vd) == VariableDescriptions + ret &= typeof(di) == DataInfo + return ret; +end + end #MAT_v4_Modelica \ No newline at end of file diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 8ca0b90..109db17 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -13,10 +13,11 @@ cd(joinpath(@__DIR__,"..")) include("../src/MAT.jl") #OpenModelica v1.19.0 -matBBO = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_res.mat") -matFBB = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_res.mat") +bbOM = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_om1.19.0.mat") +fbbOM = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_om1.19.0.mat") + #Dymola v2021 -matBBD = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") +bbDy = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") @testset "isLittleEndian" begin @@ -38,14 +39,25 @@ end @test MAT.MAT_v4_Modelica.typeBytes(Int32) == 4 end +@testset "isModelicaFormat" begin + @test MAT.MAT_v4_Modelica.isMatV4Modelica( bbOM ); + @test MAT.MAT_v4_Modelica.isMatV4Modelica( fbbOM ); + @test MAT.MAT_v4_Modelica.isMatV4Modelica( bbDy ); + + #cursorily check negative coverage + @test_throws EOFError MAT.MAT_v4_Modelica.isMatV4Modelica( joinpath( @__DIR__, "v6", "array.mat") ) + @test_throws EOFError MAT.MAT_v4_Modelica.isMatV4Modelica( joinpath( @__DIR__, "v7", "array.mat") ) + @test_throws EOFError MAT.MAT_v4_Modelica.isMatV4Modelica( joinpath( @__DIR__, "v7.3", "array.mat") ) +end + @testset "Aclass" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) @test ac.positionStart == 0 @test ac.positionEnd == 71 end @testset "readVariableNames" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) # @show vn @test length(vn.names) == 11 @@ -57,14 +69,14 @@ end end @testset "getVariableIndex" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[3]) == 3 @test MAT.MAT_v4_Modelica.getVariableIndex(vn, vn.names[10]) == 10 end @testset "readVariableDescriptions" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) @test length(vd.descriptions) == 11 @@ -74,7 +86,7 @@ end end @testset "readDataInfo" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) @@ -86,7 +98,7 @@ end end @testset "readVariable: BouncingBall OpenModelica" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBO) + ac = MAT.MAT_v4_Modelica.readAclass(bbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) @@ -113,7 +125,7 @@ end end @testset "readVariable: BouncingBall Dymola" begin - ac = MAT.MAT_v4_Modelica.readAclass(matBBD) + ac = MAT.MAT_v4_Modelica.readAclass(bbDy) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) @@ -139,8 +151,8 @@ end @test isapprox(vel[2], -0.981, rtol=1e-3) end -@testset "readVariable: FallingBodyBox" begin - ac = MAT.MAT_v4_Modelica.readAclass(matFBB) +@testset "readVariable: FallingBodyBox OpenModelica" begin + ac = MAT.MAT_v4_Modelica.readAclass(fbbOM) vn = MAT.MAT_v4_Modelica.readVariableNames(ac) vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) @@ -172,19 +184,6 @@ end @test isapprox(var[1], 1.0, rtol=1e-3) end -# @testset "all-in-one readVariable" begin -# data = MAT.MAT_v4_Modelica.readVariable(matFBB, "bodyBox.frame_a.r_0[1]") -# @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) -# @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(matFBB, "nullVariable") -# end - -# @testset "all-in-one readVariables" begin -# data = MAT.MAT_v4_Modelica.readVariables(matFBB, ["bodyBox.frame_a.r_0[1]","bodyBox.frame_a.R.T[1,1]", "world.animateGravity"] ) -# @test isapprox(data[!,"bodyBox.frame_a.r_0[1]"][16], 0.002923239, rtol=1e-3) -# @test isapprox(data[!,"bodyBox.frame_a.R.T[1,1]"][26], 0.983794001, rtol=1e-3) -# @test isapprox(data[!,"world.animateGravity"][26], 1.0, rtol=1e-3) #this constant of length 2 is filled across dataframe's time -# end - ; diff --git a/test/v4_Modelica/BouncingBall/BouncingBall_res.mat b/test/v4_Modelica/BouncingBall/BouncingBall_om1.19.0.mat similarity index 100% rename from test/v4_Modelica/BouncingBall/BouncingBall_res.mat rename to test/v4_Modelica/BouncingBall/BouncingBall_om1.19.0.mat diff --git a/test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.mat b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_om1.19.0.mat similarity index 100% rename from test/v4_Modelica/FallingBodyBox/FallingBodyBox_res.mat rename to test/v4_Modelica/FallingBodyBox/FallingBodyBox_om1.19.0.mat From 73c096fe062a7c0e521e9d1106c0593d02d7023f Mon Sep 17 00:00:00 2001 From: Brian Quadras Date: Sat, 10 Jun 2023 19:24:41 +0530 Subject: [PATCH 54/91] Added result file for FallingBodyBox_dymola2021 file and tests --- test/runtests_modelica.jl | 36 ++++++++++++++++++ .../FallingBodyBox_dymola2021.mat | Bin 0 -> 169803 bytes 2 files changed, 36 insertions(+) create mode 100644 test/v4_Modelica/FallingBodyBox/FallingBodyBox_dymola2021.mat diff --git a/test/runtests_modelica.jl b/test/runtests_modelica.jl index 109db17..d501155 100644 --- a/test/runtests_modelica.jl +++ b/test/runtests_modelica.jl @@ -3,6 +3,7 @@ # test/v4_Modelica/BouncingBall/BouncingBall_res.mat - simulated with OpenModelica v1.19.0 # test/v4_Modelica/BouncingBall/BouncingBall_dymola2021.mat - simulated with Dymola v2021 # test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with OpenModelica v1.19.0 +# test/v4_Modelica/FallingbodyBox/FallingBodyBox_res.mat - simulated with Dymola v2021 (Number of intervals = 100, Stop Time = 0.2) # These exercise every function in MAT_v4_Modelica.jl...but often use hand-observed values or otherwise require knowledge of the mat's contents import Pkg @@ -18,6 +19,7 @@ fbbOM = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_om1.19 #Dymola v2021 bbDy = joinpath(@__DIR__, "v4_Modelica","BouncingBall","BouncingBall_dymola2021.mat") +fbbDy = joinpath(@__DIR__, "v4_Modelica","FallingBodyBox","FallingBodyBox_dymola2021.mat") @testset "isLittleEndian" begin @@ -184,6 +186,40 @@ end @test isapprox(var[1], 1.0, rtol=1e-3) end +@testset "readVariable: FallingBodyBox Dymola" begin + ac = MAT.MAT_v4_Modelica.readAclass(fbbDy) + vn = MAT.MAT_v4_Modelica.readVariableNames(ac) + vd = MAT.MAT_v4_Modelica.readVariableDescriptions(ac,vn) + di = MAT.MAT_v4_Modelica.readDataInfo(ac,vd) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "Time") + + # display(var) + ret = true + for i = 2:length(var)-1 #last time is duplicated + ret &= isapprox(var[i]-var[i-1], 0.002, rtol=1e-4) + end + @test ret == true + + #point-check values read from FallingBodyBox_res.csv + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.r_0[1]") + @test isapprox(var[16], 0.002923239, rtol=1e-2) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.R.T[1, 1]") + @test isapprox(var[26], 0.983794001, rtol=1e-2) + + @test_throws ArgumentError MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_a.v_0[1]") # there is no frame_A.v_0 + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.v_0[2]") + @test isapprox(var[33], -0.58818129, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "bodyBox.frame_b.r_0[1]") + @test isapprox(var[72], 0.935886479, rtol=1e-3) + + var = MAT.MAT_v4_Modelica.readVariable(ac, vn, vd, di, "world.animateGravity") + @test isapprox(var[1], 1.0, rtol=1e-3) +end + ; diff --git a/test/v4_Modelica/FallingBodyBox/FallingBodyBox_dymola2021.mat b/test/v4_Modelica/FallingBodyBox/FallingBodyBox_dymola2021.mat new file mode 100644 index 0000000000000000000000000000000000000000..a00c2d38446a1a1877b2e4c409df79250dc5a303 GIT binary patch literal 169803 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