summaryrefslogtreecommitdiff
path: root/src/nbc/matlab.sml
blob: 65f532d101ada3ef2b5f2459937d66fd343780e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
signature MATLAB = sig
	type t
	val openOut: string -> t
	type doubleArray
	val beginDoubleArray: t * string -> doubleArray
	val writeDouble: doubleArray * real -> unit
	val concludeDoubleArray: doubleArray -> unit
	val writeDoubleArray: t * string * real vector -> unit
	val closeOut: t -> unit
end

structure Matlab :> MATLAB = struct
	fun outputReal (c, f) = BinIO.output (c, Binary.fromReal f)
	fun outputInt32 (c, i) = BinIO.output (c, Binary.fromInt32 i)
	fun outputInt16 (c, i) = BinIO.output (c, Binary.fromInt16 i)
	type t = BinIO.outstream
	type doubleArray = {
		outstream: BinIO.outstream
		, wholeSizePos: BinIO.StreamIO.out_pos
		, paddedSize: int
		, arraySizePos: BinIO.StreamIO.out_pos
		, dataSizePos: BinIO.StreamIO.out_pos
		, elements: int ref
	}
	fun pad n =
		if n > 0 andalso n <= 4 then 4
		else (n + 7) div 8 * 8
	fun doubleArraySize (nameLength, arrayLength) =
		32 + nameLength + 4 + 8 + arrayLength * 8
	val s8Tag = 1
	val s32Tag = 5
	val u32Tag = 6
	val doubleTag = 9
	val doubleFlag = 6
	val matrixTag = 14
	fun lsl (x, y) = Word.toIntX (Word.<< (Word.fromInt x, Word.fromInt y))
	fun beginDoubleArray (c, name) =
		let
			val nameSize = Int.min (size name, 63)
			val name = Word8VectorSlice.vector (
				Word8VectorSlice.slice (Byte.stringToBytes name, 0, SOME nameSize)
			)
			val paddedSize = pad nameSize
			val padding = Word8Vector.tabulate (paddedSize - nameSize, fn _ => 0w0)
			val () = outputInt32 (c, matrixTag)
			val wholeSizePos = BinIO.getPosOut c
			val () = (
				outputInt32 (c, 0)
				; outputInt32 (c, u32Tag)
				; outputInt32 (c, 8)
				; outputInt32 (c, doubleFlag)
				; outputInt32 (c, 0)
				; outputInt32 (c, s32Tag)
				; outputInt32 (c, 8)
				; outputInt32 (c, 1)
			)
			val arraySizePos = BinIO.getPosOut c
			val () = (
				outputInt32 (c, 0)
				; if nameSize > 0 andalso nameSize <= 4 then
					outputInt32 (c, lsl (nameSize, 16) + s8Tag)
				else (
					outputInt32 (c, s8Tag)
					; outputInt32 (c, nameSize)
				); BinIO.output (c, name)
				; BinIO.output (c, padding)
				; outputInt32 (c, doubleTag)
			)
			val dataSizePos = BinIO.getPosOut c
			val () = outputInt32 (c, 0)
		in
			{
				outstream = c
				, wholeSizePos = wholeSizePos
				, paddedSize = paddedSize
				, arraySizePos = arraySizePos
				, dataSizePos = dataSizePos
				, elements = ref 0
			}
		end
	fun writeDouble (da, f) = (
		outputReal (#outstream da, f)
		; #elements da := !(#elements da) + 1
	)
	fun concludeDoubleArray da =
		let
			val saved = BinIO.getPosOut (#outstream da)
		in
			BinIO.setPosOut (#outstream da, #wholeSizePos da)
			; outputInt32 (
				#outstream da
				, doubleArraySize (#paddedSize da, !(#elements da))
			); BinIO.setPosOut (#outstream da, #arraySizePos da)
			; outputInt32 (#outstream da, !(#elements da))
			; BinIO.setPosOut (#outstream da, #dataSizePos da)
			; outputInt32 (#outstream da, (!(#elements da) * 8))
			; BinIO.setPosOut (#outstream da, saved)
		end
	fun writeDoubleArray (c, name, array) =
		let
			val da = beginDoubleArray (c, name)
		in
			Vector.app (fn x => writeDouble (da, x)) array
			; concludeDoubleArray da
		end
	fun writeHeader (c, software, version) =
		let
			val date = Date.fromTimeUniv (Time.now ())
			val text = concat [
				"MATLAB 5.0 MAT-file, written by "
				, software, " ", version, ", "
				, Date.fmt "%Y-%m-$d %H:%M:%S UTC" date
			]
			val size = size text
			val header =
				CharVector.tabulate (
					124
					, fn i =>
						if i < size then String.sub (text, i)
						else #" "
				)
		in
			BinIO.output (c, Byte.stringToBytes header)
			; outputInt16 (c, 0x0100)
			; outputInt16 (c, 0x4d49)
		end
	fun openOut name =
		let
			val c = BinIO.openOut name
		in
			writeHeader (c, Program.name, Program.version)
			; c
		end
	val closeOut = BinIO.closeOut
end