File: test_complex.ml

package info (click to toggle)
ocaml-ctypes 0.24.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,180 kB
  • sloc: ml: 13,406; ansic: 3,316; makefile: 72
file content (190 lines) | stat: -rw-r--r-- 6,826 bytes parent folder | download | duplicates (2)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
(*
 * Copyright (c) 2013 Jeremy Yallop.
 *
 * This file is distributed under the terms of the MIT License.
 * See the file LICENSE for details.
 *)

open OUnit2
open Ctypes

let _ = Dl.(dlopen ~filename:"../clib/clib.so" ~flags:[RTLD_NOW])

module Common_tests(S : Cstubs.FOREIGN with type 'a result = 'a
                                        and type 'a return = 'a) =
struct
  module M = Functions.Common(S)
  open M

  (*
    Test primitive operations on complex numbers.

    Arguments and return values are currently mediated through pointers,
    since libffi doesn't support passing complex numbers.
  *)
  let test_complex_primitive_operations _ =
    let wrap' typ1 typ2 f l r =
      let rv = allocate_n ~count:1 typ2 in
      f (allocate typ1 l) (allocate typ2 r) rv;
      !@rv
    in
    let wrap typ f l r = wrap' typ typ f l r in

    let addz64 = wrap complex64 add_complexd
    and mulz64 = wrap complex64 mul_complexd
    and rotz64 = wrap' complex64 double rotdist_complexd
    and addz32 = wrap complex32 add_complexf
    and mulz32 = wrap complex32 mul_complexf
    and rotz32 = wrap' complex32 float rotdist_complexf
    and addzld = wrap complexld add_complexld
    and mulzld = wrap complexld mul_complexld
    and rotzld = wrap' complexld ldouble rotdist_complexld
    in

    begin
      let open Complex in

      let eps64 = 1e-12 in
      let complex64_eq { re = lre; im = lim } { re = rre; im = rim } =
        abs_float (lre -. rre) < eps64 && abs_float (lim -. rim) < eps64 in

      let eps32 = 1e-6 in
      let complex32_eq { re = lre; im = lim } { re = rre; im = rim } =
        abs_float (lre -. rre) < eps32 && abs_float (lim -. rim) < eps32 in

      let l = { re = 3.5; im = -1.0 } and r = { re = 2.0; im = 2.7 } in

      assert_equal ~cmp:complex64_eq (Complex.add l r) (addz64 l r);
      assert_equal ~cmp:complex64_eq (Complex.mul l r) (mulz64 l r);

      assert_equal ~cmp:complex32_eq (Complex.add l r) (addz32 l r);
      assert_equal ~cmp:complex32_eq (Complex.mul l r) (mulz32 l r);

      (* test long double complex *)
      let re x = LDouble.(to_float (ComplexL.re x)) in
      let im x = LDouble.(to_float (ComplexL.im x)) in
      let to_complexld c = LDouble.(ComplexL.make (of_float c.re) (of_float c.im)) in
      let of_complexld c = { re = re c; im = im c } in

      let l', r' = to_complexld l, to_complexld r in
      assert_equal ~cmp:complex64_eq (Complex.add l r) (of_complexld @@ addzld l' r');
      assert_equal ~cmp:complex64_eq (Complex.mul l r) (of_complexld @@ mulzld l' r');

      (* The rotdist test is designed to check passing and returning long doubles.
         The function rotates a complex number by the given angle in radians,
         then returns the manhatten distance (sum of absolute value of real and
         imaginary parts) *)
      let rot x a = 
        let open Complex in
        let y = mul x { re = cos a; im = sin a } in
        abs_float y.re +. abs_float y.im
      in
      let rotzld x r = 
        let open LDouble in
        to_float (rotzld (ComplexL.make (of_float x.re) (of_float x.im)) (of_float r)) 
      in
      let test_rotdist f eps x r = 
        let a = rot x r in
        let b = f x r in
        assert_bool "rotdist" (abs_float (a -. b) < eps)
      in
      test_rotdist rotzld eps64 { re = 2.3; im = -0.6; } 1.4;
      test_rotdist rotz64 eps64 { re = 2.3; im = -0.6; } 1.4;
      test_rotdist rotz32 eps32 { re = 2.3; im = -0.6; } 1.4;

    end
end


module Build_stub_tests(S : Cstubs.FOREIGN with type 'a result = 'a
                                            and type 'a return = 'a) =
struct
  module N = Functions.Stubs(S)
  open N

  include Common_tests(S)

  (*
    Test primitive operations on complex numbers passed by value.
  *)
  let test_complex_primitive_value_operations _ =
    begin
      let open Complex in

      let eps64 = 1e-12 in
      let complex64_eq { re = lre; im = lim } { re = rre; im = rim } =
        abs_float (lre -. rre) < eps64 && abs_float (lim -. rim) < eps64 in

      let eps32 = 1e-6 in
      let complex32_eq { re = lre; im = lim } { re = rre; im = rim } =
        abs_float (lre -. rre) < eps32 && abs_float (lim -. rim) < eps32 in

      let l = { re = 3.5; im = -1.0 } and r = { re = 2.0; im = 2.7 } in

      assert_equal ~cmp:complex64_eq (Complex.add l r) (add_complexd_val l r);
      assert_equal ~cmp:complex64_eq (Complex.mul l r) (mul_complexd_val l r);

      assert_equal ~cmp:complex32_eq (Complex.add l r) (add_complexf_val l r);
      assert_equal ~cmp:complex32_eq (Complex.mul l r) (mul_complexf_val l r);

      let zinf = { re = 0.; im = infinity } in
      let res = add_complexd_val zinf zinf in
      assert_equal 0. res.re;
      assert_equal 0. (add_complexf_val zinf zinf).re;
      let ozinf = Obj.repr zinf in
      let ores = Obj.repr res in
      assert_equal (Obj.tag ozinf) (Obj.tag ores);
      assert_equal (Obj.size ozinf) (Obj.size ores);

      (* test long double complex *)
      let re x = LDouble.(to_float (ComplexL.re x)) in
      let im x = LDouble.(to_float (ComplexL.im x)) in
      let to_complexld c = LDouble.(ComplexL.make (of_float c.re) (of_float c.im)) in
      let of_complexld c = { re = re c; im = im c } in

      let l', r' = to_complexld l, to_complexld r in
      assert_equal ~cmp:complex64_eq (Complex.add l r) (of_complexld @@ add_complexld_val l' r');
      assert_equal ~cmp:complex64_eq (Complex.mul l r) (of_complexld @@ mul_complexld_val l' r');

      assert_equal 0. (re (to_complexld zinf));

      (* rot-dist test *)
      let rot x a = 
        let open Complex in
        let y = mul x { re = cos a; im = sin a } in
        abs_float y.re +. abs_float y.im
      in
      let rotdist_complexld_val x r = 
        let open LDouble in
        to_float (rotdist_complexld_val (ComplexL.make (of_float x.re) (of_float x.im)) (of_float r)) 
      in
      let test_rotdist f eps x r = 
        let a = rot x r in
        let b = f x r in
        assert_bool "rotdist" (abs_float (a -. b) < eps)
      in
      test_rotdist rotdist_complexld_val eps64 { re = 2.3; im = -0.6; } 1.4;
      test_rotdist rotdist_complexd_val eps64 { re = 2.3; im = -0.6; } 1.4;
      test_rotdist rotdist_complexf_val eps32 { re = 2.3; im = -0.6; } 1.4;
    end
end


module Foreign_tests = Common_tests(Tests_common.Foreign_binder)
module Stub_tests = Build_stub_tests(Generated_bindings)


let suite = "Complex number tests" >:::
  ["basic operations on complex numbers (foreign)"
   >:: Foreign_tests.test_complex_primitive_operations;

   "basic operations on complex numbers (stubs)"
   >:: Stub_tests.test_complex_primitive_operations;

   "basic operations on complex numbers passed by value(stubs)"
   >:: Stub_tests.test_complex_primitive_value_operations;
  ]


let _ =
  run_test_tt_main suite