reflexion 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 385852765cb2073b46cffebf4c22bcd1e432f4d17b911f7cde7d67864add658b
4
- data.tar.gz: 8645ad9b05be24a1f69c241757b4dda8004a36411704932729872d6a0410ee79
3
+ metadata.gz: d01575dae53b6f2710df9796f20a629e11a8e697948ee53d0ca417a701d899f6
4
+ data.tar.gz: 1c80c076ab0230750ec534efa777b2e1e892322bc44f8644911980c9170e825b
5
5
  SHA512:
6
- metadata.gz: 1577fd876c000d34cb1435732f83292fcb27a940493926f1d8209f77cac8bc58da08e9782e629894a683b7e0624dba9c1786a1c4526732c051d17d3d3d40c628
7
- data.tar.gz: 121844b893ea518437ab0eb2cd255af14c9c804af32281624ecde6629358b633339294530944b209dac3cfe4fcf1b242363a208df747a5b181f9c269aa60340b
6
+ metadata.gz: 4da373a98a205f155fb33c19b7f0ae13584ae86321bf0edf23b7ed1d0121b94f9b309cbf325c814a2fc7062ba2b3a86556655ab19da2540c6498873e367f8487
7
+ data.tar.gz: 68c1cc63d9f6ed49a4146dc972f05cf64f0c3ca6bb575984a990e45fef256fbc2e1eb6fbd51d06df61b19b34bae0c58c024475213804c0700719b11abcdbe6e0
@@ -145,6 +145,8 @@ Init_reflex_application ()
145
145
  rb_define_method(cApplication, "on_preference", RUBY_METHOD_FUNC(on_preference), 1);
146
146
  rb_define_method(cApplication, "on_about", RUBY_METHOD_FUNC(on_about), 1);
147
147
  rb_define_singleton_method(cApplication, "instance", RUBY_METHOD_FUNC(instance), 0);
148
+
149
+ define_wrapper_equality_methods<Reflex::Application>(cApplication);
148
150
  }
149
151
 
150
152
 
@@ -39,12 +39,13 @@ void
39
39
  Init_reflex_device ()
40
40
  {
41
41
  Module mReflex = rb_define_module("Reflex");
42
+ rb_define_singleton_method(mReflex, "vibrate", RUBY_METHOD_FUNC(s_vibrate), 0);
42
43
 
43
44
  cDevice = mReflex.define_class("Device", Reflex::device_class());
44
45
  rb_define_alloc_func(cDevice, alloc);
45
46
  rb_define_method(cDevice, "name", RUBY_METHOD_FUNC(name), 0);
46
47
 
47
- rb_define_singleton_method(mReflex, "vibrate", RUBY_METHOD_FUNC(s_vibrate), 0);
48
+ define_wrapper_equality_methods<Reflex::Device>(cDevice);
48
49
  }
49
50
 
50
51
 
@@ -99,6 +99,8 @@ Init_reflex_ellipse_shape ()
99
99
  rb_define_method(cEllipseShape, "hole_size", RUBY_METHOD_FUNC(get_hole_size), 0);
100
100
  rb_define_method(cEllipseShape, "nsegment=", RUBY_METHOD_FUNC(set_nsegment), 1);
101
101
  rb_define_method(cEllipseShape, "nsegment", RUBY_METHOD_FUNC(get_nsegment), 0);
102
+
103
+ define_wrapper_equality_methods<Reflex::EllipseShape>(cEllipseShape);
102
104
  }
103
105
 
104
106
 
@@ -64,6 +64,8 @@ Init_reflex_filter ()
64
64
  rb_define_method(cFilter, "apply", RUBY_METHOD_FUNC(apply), 2);
65
65
  rb_define_method(cFilter, "shader=", RUBY_METHOD_FUNC(set_shader), 1);
66
66
  rb_define_method(cFilter, "shader", RUBY_METHOD_FUNC(get_shader), 0);
67
+
68
+ define_wrapper_equality_methods<Reflex::Filter>(cFilter);
67
69
  }
68
70
 
69
71
 
@@ -49,6 +49,8 @@ Init_reflex_image_view ()
49
49
  rb_define_alloc_func(cImageView, alloc);
50
50
  rb_define_private_method(cImageView, "set_image", RUBY_METHOD_FUNC(set_image), 1);
51
51
  rb_define_method(cImageView, "image", RUBY_METHOD_FUNC(get_image), 0);
52
+
53
+ define_wrapper_equality_methods<Reflex::ImageView>(cImageView);
52
54
  }
53
55
 
54
56
 
@@ -82,6 +82,8 @@ Init_reflex_line_shape ()
82
82
  rb_define_method(cLineShape, "add_points", RUBY_METHOD_FUNC(add_points), -1);
83
83
  rb_define_method(cLineShape, "loop=", RUBY_METHOD_FUNC(set_loop), 1);
84
84
  rb_define_method(cLineShape, "loop", RUBY_METHOD_FUNC(get_loop), 0);
85
+
86
+ define_wrapper_equality_methods<Reflex::LineShape>(cLineShape);
85
87
  }
86
88
 
87
89
 
@@ -79,8 +79,9 @@ Init_reflex_midi ()
79
79
  rb_define_method(cMIDI, "on_note_on", RUBY_METHOD_FUNC(on_note_on), 1);
80
80
  rb_define_method(cMIDI, "on_note_off", RUBY_METHOD_FUNC(on_note_off), 1);
81
81
  rb_define_method(cMIDI, "on_control_change", RUBY_METHOD_FUNC(on_control_change), 1);
82
-
83
82
  rb_define_singleton_method(cMIDI, "all", RUBY_METHOD_FUNC(s_get_all), 0);
83
+
84
+ define_wrapper_equality_methods<Reflex::MIDI>(cMIDI);
84
85
  }
85
86
 
86
87
 
@@ -48,6 +48,8 @@ Init_reflex_polygon_shape ()
48
48
  rb_define_alloc_func(cPolygonShape, alloc);
49
49
  rb_define_method(cPolygonShape, "polygon=", RUBY_METHOD_FUNC(set_polygon), 1);
50
50
  rb_define_method(cPolygonShape, "polygon", RUBY_METHOD_FUNC(get_polygon), 0);
51
+
52
+ define_wrapper_equality_methods<Reflex::PolygonShape>(cPolygonShape);
51
53
  }
52
54
 
53
55
 
@@ -145,6 +145,8 @@ Init_reflex_rect_shape ()
145
145
  rb_define_method(cRectShape, "round_right_bottom", RUBY_METHOD_FUNC(get_round_right_bottom), 0);
146
146
  rb_define_method(cRectShape, "nsegment=", RUBY_METHOD_FUNC(set_nsegment), 1);
147
147
  rb_define_method(cRectShape, "nsegment", RUBY_METHOD_FUNC(get_nsegment), 0);
148
+
149
+ define_wrapper_equality_methods<Reflex::RectShape>(cRectShape);
148
150
  }
149
151
 
150
152
 
@@ -167,6 +167,7 @@ Init_reflex_shape ()
167
167
  cShape.define_private_method("call_contact_begin!", on_contact_begin);
168
168
  cShape.define_private_method("call_contact_end!", on_contact_end);
169
169
 
170
+ define_wrapper_equality_methods<Reflex::Shape>(cShape);
170
171
  define_selector_methods<Reflex::Shape>(cShape);
171
172
  }
172
173
 
@@ -91,6 +91,7 @@ Init_reflex_timer ()
91
91
  rb_define_method(cTimer, "count", RUBY_METHOD_FUNC(get_count), 0);
92
92
  cTimer.define_method("finished?", is_finished);
93
93
 
94
+ define_wrapper_equality_methods<Reflex::Timer>(cTimer);
94
95
  define_selector_methods<Reflex::Timer>(cTimer);
95
96
  }
96
97
 
@@ -1267,6 +1267,7 @@ Init_reflex_view ()
1267
1267
  cView.define_const("CAPTURE_MIDI", Reflex::View::CAPTURE_MIDI);
1268
1268
  cView.define_const("CAPTURE_ALL", Reflex::View::CAPTURE_ALL);
1269
1269
 
1270
+ define_wrapper_equality_methods<Reflex::View>(cView);
1270
1271
  define_selector_methods<Reflex::View>(cView);
1271
1272
  }
1272
1273
 
@@ -465,6 +465,8 @@ Init_reflex_window ()
465
465
 
466
466
  cWindow.define_const("ORIENTATION_PORTRAIT", Reflex::Window::FLAG_PORTRAIT);
467
467
  cWindow.define_const("ORIENTATION_LANDSCAPE", Reflex::Window::FLAG_LANDSCAPE);
468
+
469
+ define_wrapper_equality_methods<Reflex::Window>(cWindow);
468
470
  }
469
471
 
470
472
 
data/CLAUDE.md CHANGED
@@ -8,7 +8,7 @@ The published gem name is **`reflexion`** (not `reflex`).
8
8
 
9
9
  ## External Libraries
10
10
 
11
- - Box2D v2.4.1 — Physics engine
11
+ - Box2D v3.1.1 — Physics engine (single-threaded by default)
12
12
  - RtMidi 6.0.0 — MIDI device support
13
13
 
14
14
  ## Platform-Specific Code
data/ChangeLog.md CHANGED
@@ -1,6 +1,17 @@
1
1
  # reflex ChangeLog
2
2
 
3
3
 
4
+ ## [v0.5.0] - 2026-06-23
5
+
6
+ - Upgrade Box2D from 2.4.1 to 3.1.1
7
+ - Add Reflex.podspec and pod.rake for CocoaPods distribution
8
+ - Define ==/eql?/hash on RefCountable wrapper classes
9
+ - Build the menu in applicationWillFinishLaunching
10
+
11
+ - Fix Timer owner use-after-free with WeakRef
12
+ - Fix rect fixture bottom edge and wall shape setup
13
+
14
+
4
15
  ## [v0.4.2] - 2026-06-12
5
16
 
6
17
  - Share single offscreen GL context across windows
data/Rakefile CHANGED
@@ -19,9 +19,10 @@ TESTS_ALONE = ['test/test_reflex_init.rb']
19
19
  install_packages apt: %w[libsdl2-dev]
20
20
 
21
21
  use_external_library 'https://github.com/erincatto/box2d',
22
- tag: 'v2.4.1',
22
+ tag: 'v3.1.1',
23
23
  incdirs: %w[include src],
24
- srcdirs: 'src'
24
+ srcdirs: 'src',
25
+ defs: ['B2_MAX_WORLDS=256', *('BOX2D_DISABLE_SIMD' if wasm?)]
25
26
 
26
27
  use_external_library 'https://github.com/thestk/rtmidi',
27
28
  tag: '6.0.0',
data/Reflex.podspec ADDED
@@ -0,0 +1,105 @@
1
+ # -*- mode: ruby -*-
2
+
3
+
4
+ Pod::Spec.new do |s|
5
+ s.name = "Reflex"
6
+ s.version = File.read(File.expand_path 'VERSION', __dir__)[/[\d\.]+/]
7
+ s.summary = "A Graphical User Interface Tool Kit"
8
+ s.description = "A Graphical User Interface Tool Kit"
9
+ s.license = "MIT"
10
+ s.source = {:git => "https://github.com/xord/reflex.git"}
11
+ s.author = {"xordog" => "xordog@gmail.com"}
12
+ s.homepage = "https://github.com/xord/reflex"
13
+
14
+ s.osx.deployment_target = "10.10"
15
+ s.ios.deployment_target = "12.0"
16
+
17
+ all = "${PODS_ROOT}/#{s.name}/all"
18
+ deps = File.read(File.expand_path 'Rakefile', __dir__)
19
+ .lines(chomp: true)
20
+ .map {_1[%r|require\s*['"](\w+)/extension['"]|, 1]}
21
+ .compact
22
+
23
+ incdirs = deps.map {"#{all}/#{_1}/include"}.concat %W[
24
+ #{all}/rays/vendor/glm
25
+ #{all}/rays/vendor/clipper/cpp
26
+ #{all}/rays/vendor/earcut.hpp/include/mapbox
27
+ #{all}/rays/vendor/splines-lib
28
+ #{all}/reflex/vendor/box2d/include
29
+ #{all}/reflex/vendor/box2d/src
30
+ #{all}/reflex/vendor/rtmidi/rtmidi
31
+ ${PODS_ROOT}/CRuby/CRuby/include
32
+ ]
33
+
34
+ s.prepare_command = 'rake -f pod.rake setup'
35
+ s.preserve_paths = deps.product(%w[include src ext vendor]).map {"all/#{_1}/#{_2}"}
36
+ s.requires_arc = false
37
+ s.osx.compiler_flags = "-DOSX"
38
+ s.ios.compiler_flags = "-DIOS"
39
+ s.library = %w[c++]
40
+ s.xcconfig = {
41
+ "CLANG_CXX_LANGUAGE_STANDARD" => 'c++20',
42
+ "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) B2_MAX_WORLDS=256',
43
+ "HEADER_SEARCH_PATHS" => incdirs.join(' ')
44
+ }
45
+
46
+ s.resource_bundles =
47
+ deps.each_with_object({}) do |dep, hash|
48
+ hash[dep.capitalize] = %w[lib VERSION].map {"all/#{dep}/#{_1}"}
49
+ end
50
+
51
+ s.subspec "Xot" do |spec|
52
+ spec.source_files = "all/xot/src/*.cpp"
53
+ end
54
+
55
+ s.subspec "Rucy" do |spec|
56
+ spec.source_files = "all/rucy/src/*.cpp"
57
+
58
+ spec.subspec "Ext" do |ext|
59
+ ext.source_files = "all/rucy/ext/rucy/*.cpp"
60
+ end
61
+ end
62
+
63
+ s.subspec "Rays" do |spec|
64
+ spec .source_files = "all/rays/src/*.cpp", "all/rays/src/opengl/*.cpp"
65
+ spec.osx.source_files = "all/rays/src/**/osx/*.{cpp,mm}"
66
+ spec.ios.source_files = "all/rays/src/**/ios/*.{cpp,mm}"
67
+ spec.osx.frameworks = %w[AppKit OpenGL CoreImage CoreVideo CoreMedia AVFoundation]
68
+ spec.ios.frameworks = %w[GLKit MobileCoreServices AVFoundation]
69
+
70
+ spec.subspec "Clipper" do |sub|
71
+ sub.source_files = "all/rays/vendor/clipper/cpp/*.cpp"
72
+ end
73
+
74
+ spec.subspec "SplineLib" do |sub|
75
+ sub.source_files = "all/rays/vendor/splines-lib/Splines.cpp"
76
+ end
77
+
78
+ spec.subspec "Ext" do |ext|
79
+ ext.source_files = "all/rays/ext/rays/*.cpp"
80
+ end
81
+ end
82
+
83
+ s.subspec "Reflex" do |spec|
84
+ spec .source_files = "all/reflex/src/*.cpp"
85
+ spec.osx.source_files = "all/reflex/src/osx/*.{cpp,mm}"
86
+ spec.ios.source_files = "all/reflex/src/ios/*.{cpp,mm}"
87
+ spec.osx.frameworks = %w[Cocoa IOKit GameController]
88
+ spec.ios.frameworks = %w[CoreMotion GameController]
89
+
90
+ spec.subspec "Box2D" do |sub|
91
+ # Box2D 3.x is written in C
92
+ sub.source_files = "all/reflex/vendor/box2d/src/**/*.c"
93
+ end
94
+
95
+ spec.subspec "RtMidi" do |sub|
96
+ sub.source_files = "all/reflex/vendor/rtmidi/rtmidi/**/*.cpp"
97
+ sub.osx.compiler_flags = "-D__MACOSX_CORE__"
98
+ sub.osx.frameworks = %w[CoreMIDI CoreAudio]
99
+ end
100
+
101
+ spec.subspec "Ext" do |ext|
102
+ ext.source_files = "all/reflex/ext/reflex/*.cpp"
103
+ end
104
+ end
105
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.2
1
+ 0.5.0
@@ -159,6 +159,8 @@ Init_reflex_application ()
159
159
  cApplication.define_method("on_preference", on_preference);
160
160
  cApplication.define_method("on_about", on_about);
161
161
  cApplication.define_singleton_method("instance", instance);
162
+
163
+ define_wrapper_equality_methods<Reflex::Application>(cApplication);
162
164
  }
163
165
 
164
166
 
@@ -42,12 +42,13 @@ void
42
42
  Init_reflex_device ()
43
43
  {
44
44
  Module mReflex = define_module("Reflex");
45
+ mReflex.define_singleton_method("vibrate", s_vibrate);
45
46
 
46
47
  cDevice = mReflex.define_class("Device", Reflex::device_class());
47
48
  cDevice.define_alloc_func(alloc);
48
49
  cDevice.define_method("name", name);
49
50
 
50
- mReflex.define_singleton_method("vibrate", s_vibrate);
51
+ define_wrapper_equality_methods<Reflex::Device>(cDevice);
51
52
  }
52
53
 
53
54
 
@@ -108,6 +108,8 @@ Init_reflex_ellipse_shape ()
108
108
  cEllipseShape.define_method("hole_size", get_hole_size);
109
109
  cEllipseShape.define_method("nsegment=", set_nsegment);
110
110
  cEllipseShape.define_method("nsegment", get_nsegment);
111
+
112
+ define_wrapper_equality_methods<Reflex::EllipseShape>(cEllipseShape);
111
113
  }
112
114
 
113
115
 
@@ -69,6 +69,8 @@ Init_reflex_filter ()
69
69
  cFilter.define_method("apply", apply);
70
70
  cFilter.define_method("shader=", set_shader);
71
71
  cFilter.define_method("shader", get_shader);
72
+
73
+ define_wrapper_equality_methods<Reflex::Filter>(cFilter);
72
74
  }
73
75
 
74
76
 
@@ -52,6 +52,8 @@ Init_reflex_image_view ()
52
52
  cImageView.define_alloc_func(alloc);
53
53
  cImageView.define_private_method("set_image", set_image);
54
54
  cImageView.define_method( "image", get_image);
55
+
56
+ define_wrapper_equality_methods<Reflex::ImageView>(cImageView);
55
57
  }
56
58
 
57
59
 
@@ -87,6 +87,8 @@ Init_reflex_line_shape ()
87
87
  cLineShape.define_method("add_points", add_points);
88
88
  cLineShape.define_method("loop=", set_loop);
89
89
  cLineShape.define_method("loop", get_loop);
90
+
91
+ define_wrapper_equality_methods<Reflex::LineShape>(cLineShape);
90
92
  }
91
93
 
92
94
 
data/ext/reflex/midi.cpp CHANGED
@@ -86,8 +86,9 @@ Init_reflex_midi ()
86
86
  cMIDI.define_method("on_note_on", on_note_on);
87
87
  cMIDI.define_method("on_note_off", on_note_off);
88
88
  cMIDI.define_method("on_control_change", on_control_change);
89
-
90
89
  cMIDI.define_singleton_method("all", s_get_all);
90
+
91
+ define_wrapper_equality_methods<Reflex::MIDI>(cMIDI);
91
92
  }
92
93
 
93
94
 
@@ -51,6 +51,8 @@ Init_reflex_polygon_shape ()
51
51
  cPolygonShape.define_alloc_func(alloc);
52
52
  cPolygonShape.define_method("polygon=", set_polygon);
53
53
  cPolygonShape.define_method("polygon", get_polygon);
54
+
55
+ define_wrapper_equality_methods<Reflex::PolygonShape>(cPolygonShape);
54
56
  }
55
57
 
56
58
 
@@ -158,6 +158,8 @@ Init_reflex_rect_shape ()
158
158
  cRectShape.define_method("round_right_bottom", get_round_right_bottom);
159
159
  cRectShape.define_method("nsegment=", set_nsegment);
160
160
  cRectShape.define_method("nsegment", get_nsegment);
161
+
162
+ define_wrapper_equality_methods<Reflex::RectShape>(cRectShape);
161
163
  }
162
164
 
163
165
 
data/ext/reflex/shape.cpp CHANGED
@@ -184,6 +184,7 @@ Init_reflex_shape ()
184
184
  cShape.define_private_method("call_contact_begin!", on_contact_begin);
185
185
  cShape.define_private_method("call_contact_end!", on_contact_end);
186
186
 
187
+ define_wrapper_equality_methods<Reflex::Shape>(cShape);
187
188
  define_selector_methods<Reflex::Shape>(cShape);
188
189
  }
189
190
 
data/ext/reflex/timer.cpp CHANGED
@@ -99,6 +99,7 @@ Init_reflex_timer ()
99
99
  cTimer.define_method("count", get_count);
100
100
  cTimer.define_method("finished?", is_finished);
101
101
 
102
+ define_wrapper_equality_methods<Reflex::Timer>(cTimer);
102
103
  define_selector_methods<Reflex::Timer>(cTimer);
103
104
  }
104
105
 
data/ext/reflex/view.cpp CHANGED
@@ -1393,6 +1393,7 @@ Init_reflex_view ()
1393
1393
  cView.define_const("CAPTURE_MIDI", Reflex::View::CAPTURE_MIDI);
1394
1394
  cView.define_const("CAPTURE_ALL", Reflex::View::CAPTURE_ALL);
1395
1395
 
1396
+ define_wrapper_equality_methods<Reflex::View>(cView);
1396
1397
  define_selector_methods<Reflex::View>(cView);
1397
1398
  }
1398
1399
 
@@ -514,6 +514,8 @@ Init_reflex_window ()
514
514
 
515
515
  cWindow.define_const("ORIENTATION_PORTRAIT", Reflex::Window::FLAG_PORTRAIT);
516
516
  cWindow.define_const("ORIENTATION_LANDSCAPE", Reflex::Window::FLAG_LANDSCAPE);
517
+
518
+ define_wrapper_equality_methods<Reflex::Window>(cWindow);
517
519
  }
518
520
 
519
521
 
data/pod.rake ADDED
@@ -0,0 +1,41 @@
1
+ # -*- mode: ruby -*-
2
+ #
3
+ # Pins the dependencies to this checkout's commit by cloning the monorepo
4
+ # (xord/all) and checking out the commit named by HEAD's [[UUID]] trailer.
5
+
6
+
7
+ ALL_REPO = 'https://github.com/xord/all.git'
8
+ ALL_DIR = 'all'
9
+
10
+ DEPS = File.readlines('Rakefile', chomp: true)
11
+ .map {|line| line[%r|require\s*['"](\w+)/extension["']|, 1]}
12
+ .compact
13
+
14
+
15
+ def head_uuid()
16
+ `git log -1 --format=%B`[/^\[\[([0-9a-fA-F-]+)\]\]$/, 1] ||
17
+ raise('pod.rake: HEAD has no [[UUID]] trailer; cannot pin dependencies')
18
+ end
19
+
20
+ def checkout_monorepo(uuid)
21
+ sh %( git clone --no-tags #{ALL_REPO} #{ALL_DIR} ) unless File.directory? ALL_DIR
22
+
23
+ commit = `git -C #{ALL_DIR} log origin/HEAD -F --grep='[[#{uuid}]]' --format=%H -1`
24
+ .strip.then {_1.empty? ? nil : _1}
25
+ raise "pod.rake: no commit for [[#{uuid}]] in #{ALL_REPO}" unless commit
26
+
27
+ sh %( git -C #{ALL_DIR} checkout -q #{commit} )
28
+ end
29
+
30
+
31
+ task :clobber do
32
+ sh %( rm -rf #{ALL_DIR} )
33
+ end
34
+
35
+ task :setup do
36
+ checkout_monorepo head_uuid
37
+
38
+ Dir.chdir ALL_DIR do
39
+ sh %( VENDOR_NOCOMPILE=1 rake #{DEPS.join ' '} vendor erb )
40
+ end
41
+ end
data/reflex.gemspec CHANGED
@@ -25,9 +25,9 @@ Gem::Specification.new do |s|
25
25
  s.platform = Gem::Platform::RUBY
26
26
  s.required_ruby_version = '>= 3.0.0'
27
27
 
28
- s.add_dependency 'xot', '~> 0.3.14'
29
- s.add_dependency 'rucy', '~> 0.3.14'
30
- s.add_dependency 'rays', '~> 0.3.15'
28
+ s.add_dependency 'xot', '~> 0.3.15'
29
+ s.add_dependency 'rucy', '~> 0.3.15'
30
+ s.add_dependency 'rays', '~> 0.3.16'
31
31
 
32
32
  s.files = `git ls-files`.split $/
33
33
  s.executables = s.files.grep(%r{^bin/}) {|f| File.basename f}