safestruct 0.0.1 → 0.1.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 +4 -4
- data/Manifest.txt +7 -0
- data/lib/safestruct.rb +59 -2
- data/lib/safestruct/safe_array.rb +69 -0
- data/lib/safestruct/safe_hash.rb +77 -0
- data/lib/safestruct/safe_struct.rb +79 -0
- data/lib/safestruct/version.rb +2 -2
- data/test/helper.rb +8 -0
- data/test/test_array.rb +52 -0
- data/test/test_hash.rb +77 -0
- data/test/test_struct.rb +77 -0
- metadata +8 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aabbb2a559ec2421cc0ac43cdcfb0764c8f01633
|
4
|
+
data.tar.gz: 7c4aa56416aa26f67f06aa09260d0af12f28cf27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b51b663bc4580a435e39d74ea015bc6e407dec307126de09fe818977de527ced949d4d58a2a04f27a10a25074e76fcc17f47139152782804177b28bf0cd5d20
|
7
|
+
data.tar.gz: 5285c818c0b4240f186bbc3f9c4221aa4cdf204ec20f0ac187da6b3b1d2a8a339fe5162802b9fabc8b09c80c2ce77e8a013bb3fb391074229dc978fb886024dd
|
data/Manifest.txt
CHANGED
data/lib/safestruct.rb
CHANGED
@@ -6,6 +6,63 @@ require 'pp'
|
|
6
6
|
## our own code
|
7
7
|
require 'safestruct/version' # note: let version always go first
|
8
8
|
|
9
|
+
require 'safestruct/safe_array'
|
10
|
+
require 'safestruct/safe_hash'
|
11
|
+
require 'safestruct/safe_struct'
|
9
12
|
|
10
|
-
|
11
|
-
|
13
|
+
|
14
|
+
|
15
|
+
####################################
|
16
|
+
## add dummy bool class for mapping and (payable) method signature
|
17
|
+
|
18
|
+
class Integer
|
19
|
+
def self.zero() 0; end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Bool
|
23
|
+
def self.zero() false; end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
#####
|
29
|
+
# add "beautiful" convenience helpers
|
30
|
+
|
31
|
+
class Mapping
|
32
|
+
|
33
|
+
def self.of( *args )
|
34
|
+
## e.g. gets passed in [{Address=>Integer}]
|
35
|
+
## check for Integer - use Hash.new(0)
|
36
|
+
## check for Bool - use Hash.new(False)
|
37
|
+
if args[0].is_a? Hash
|
38
|
+
arg = args[0].to_a ## convert to array (for easier access)
|
39
|
+
klass_key = arg[0][0]
|
40
|
+
klass_value = arg[0][1]
|
41
|
+
klass = SafeHash.build_class( klass_key, klass_value )
|
42
|
+
klass.new
|
43
|
+
else
|
44
|
+
## todo/fix: throw argument error/exception
|
45
|
+
Hash.new ## that is, "plain" {} with all "standard" defaults
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
class Array
|
52
|
+
## "typed" safe array "constructor"
|
53
|
+
## e.g. Array.of( Address ) or Array.of( Money ) or Array.of( Proposal, size: 2 ) etc.
|
54
|
+
def self.of( klass_value )
|
55
|
+
klass = SafeArray.build_class( klass_value )
|
56
|
+
klass.new ## todo: add klass.new( **kwargs ) for size: 2 etc.
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
############################
|
62
|
+
# note: HACK redefine built in struct
|
63
|
+
# => warning: already initialized constant Struct
|
64
|
+
OldStruct = Struct ## save old struct class
|
65
|
+
Struct = SafeStruct
|
66
|
+
|
67
|
+
|
68
|
+
puts Safe.banner ## say hello
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class SafeArray
|
4
|
+
|
5
|
+
## e.g.
|
6
|
+
## Array.of( Address ), Array.of( Integer), etc.
|
7
|
+
|
8
|
+
def self.build_class( klass_value )
|
9
|
+
## note: care for now only about value type / class
|
10
|
+
|
11
|
+
## note: keep a class cache
|
12
|
+
cache = @@cache ||= {}
|
13
|
+
klass = cache[ klass_value ]
|
14
|
+
return klass if klass
|
15
|
+
|
16
|
+
klass = Class.new( SafeArray )
|
17
|
+
klass.class_eval( <<RUBY )
|
18
|
+
def self.klass_value
|
19
|
+
@klass_value ||= #{klass_value}
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
## add to cache for later (re)use
|
23
|
+
cache[ klass_value ] = klass
|
24
|
+
klass
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def self.new_zero() new; end
|
29
|
+
def self.zero() @zero ||= new_zero; end
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
## todo/check: if array works if value is a (nested/multi-dimensional) array
|
35
|
+
@ary = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def []=(index, value)
|
39
|
+
@ary[index] = value
|
40
|
+
end
|
41
|
+
|
42
|
+
def [](index)
|
43
|
+
item = @ary[ index ]
|
44
|
+
if item.nil?
|
45
|
+
## todo/check:
|
46
|
+
## always return (deep) frozen zero object - why? why not?
|
47
|
+
## let user change the returned zero object - why? why not?
|
48
|
+
if self.class.klass_value.respond_to?( :new_zero )
|
49
|
+
## note: use a new unfrozen copy of the zero object
|
50
|
+
## changes to the object MUST be possible (new "empty" modifable object expected)
|
51
|
+
item = self.class.klass_value.new_zero
|
52
|
+
else # assume value semantics e.g. Integer, Bool, etc. zero values gets replaced
|
53
|
+
## puts "use value semantics"
|
54
|
+
item = self.class.klass_value.zero
|
55
|
+
end
|
56
|
+
end
|
57
|
+
item
|
58
|
+
end
|
59
|
+
|
60
|
+
def push( item )
|
61
|
+
## todo/fix: check if item.is_a? @type
|
62
|
+
## note: Address might be a String too (Address | String)
|
63
|
+
## store Address always as String!!! - why? why not?
|
64
|
+
@ary.push( item )
|
65
|
+
end
|
66
|
+
|
67
|
+
def size() @ary.size; end
|
68
|
+
def length() size; end
|
69
|
+
end # class SafeArray
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
class SafeHash
|
5
|
+
|
6
|
+
## e.g.
|
7
|
+
## Mapping.of( Address => Money )
|
8
|
+
|
9
|
+
## note: need to create new class!! for every mapping
|
10
|
+
## make klass_key class and
|
11
|
+
## klass_value class into class instance variables
|
12
|
+
## that can get used by zero
|
13
|
+
## self.new returns a Hash.new/SafeHash.new like object
|
14
|
+
|
15
|
+
def self.build_class( klass_key, klass_value )
|
16
|
+
## note: care for now only about value type / class
|
17
|
+
|
18
|
+
## note: keep a class cache
|
19
|
+
cache = @@cache ||= {}
|
20
|
+
klass = cache[ klass_value ]
|
21
|
+
return klass if klass
|
22
|
+
|
23
|
+
klass = Class.new( SafeHash )
|
24
|
+
klass.class_eval( <<RUBY )
|
25
|
+
def self.klass_key
|
26
|
+
@klass_key ||= #{klass_key}
|
27
|
+
end
|
28
|
+
def self.klass_value
|
29
|
+
@klass_value ||= #{klass_value}
|
30
|
+
end
|
31
|
+
RUBY
|
32
|
+
## add to cache for later (re)use
|
33
|
+
cache[ klass_value ] = klass
|
34
|
+
klass
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def self.new_zero() new; end
|
39
|
+
def self.zero() @zero ||= new_zero; end
|
40
|
+
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
## todo/check: if hash works if value is a (nested) hash
|
44
|
+
@h = {}
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def []=(key, value)
|
49
|
+
@h[key] = value
|
50
|
+
end
|
51
|
+
|
52
|
+
def [](key)
|
53
|
+
item = @h[ key ]
|
54
|
+
if item.nil?
|
55
|
+
## pp self.class.klass_value
|
56
|
+
## pp self.class.klass_value.zero
|
57
|
+
|
58
|
+
#####
|
59
|
+
# todo/check:
|
60
|
+
# add zero to hash on lookup (increases size/length)
|
61
|
+
# why? why not?
|
62
|
+
|
63
|
+
if self.class.klass_value.respond_to?( :new_zero )
|
64
|
+
## note: use a dup(licated) unfrozen copy of the zero object
|
65
|
+
## changes to the object MUST be possible (new "empty" modifable object expected)
|
66
|
+
item = @h[ key ] = self.class.klass_value.new_zero
|
67
|
+
else # assume value semantics e.g. Integer, Bool, etc. zero values gets replaced
|
68
|
+
## puts "use value semantics"
|
69
|
+
item = @h[ key ] = self.class.klass_value.zero
|
70
|
+
end
|
71
|
+
end
|
72
|
+
item
|
73
|
+
end
|
74
|
+
|
75
|
+
def size() @h.size; end
|
76
|
+
def length() size; end
|
77
|
+
end # class SafeHash
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class SafeStruct
|
4
|
+
|
5
|
+
def self.build_class( **attributes )
|
6
|
+
klass = Class.new( SafeStruct ) do
|
7
|
+
define_method( :initialize ) do |*args|
|
8
|
+
attributes.keys.zip( args ).each do |key, arg|
|
9
|
+
instance_variable_set( "@#{key}", arg )
|
10
|
+
end
|
11
|
+
self ## note: return reference to self for chaining method calls
|
12
|
+
end
|
13
|
+
|
14
|
+
attributes.each do |key,value|
|
15
|
+
define_method( key ) do
|
16
|
+
instance_variable_get( "@#{key}" )
|
17
|
+
end
|
18
|
+
if value == false ## note: for Bool add getter with question mark (e.g. voted? etc.)
|
19
|
+
define_method( "#{key}?" ) do
|
20
|
+
instance_variable_get( "@#{key}" )
|
21
|
+
end
|
22
|
+
end
|
23
|
+
define_method( "#{key}=" ) do |arg|
|
24
|
+
instance_variable_set( "@#{key}", arg )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
define_method( :== ) do |other|
|
29
|
+
return false unless other.is_a?( klass )
|
30
|
+
attributes.keys.all? do |key|
|
31
|
+
__send__( key ) == other.__send__( key )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
alias_method :eql?, :==
|
35
|
+
end
|
36
|
+
|
37
|
+
## add self.new too - note: call/forward to "old" orginal self.new of Event (base) class
|
38
|
+
klass.define_singleton_method( :new ) do |*args|
|
39
|
+
if args.size != attributes.size
|
40
|
+
## check for required args/params - all MUST be passed in!!!
|
41
|
+
raise ArgumentError.new( "[SafeStruct] wrong number of arguments for #{name}.new - #{args.size} for #{attributes.size}" )
|
42
|
+
end
|
43
|
+
old_new( *args )
|
44
|
+
end
|
45
|
+
|
46
|
+
klass.define_singleton_method( :new_zero ) do
|
47
|
+
## note: if attribute value is a composite
|
48
|
+
## use new_zero to create a new instance!!!
|
49
|
+
## do NOT use the passed in reference!!!!
|
50
|
+
values = attributes.values.map do |value|
|
51
|
+
if value.is_a?(SafeStruct) ||
|
52
|
+
value.is_a?(SafeArray) ||
|
53
|
+
value.is_a?(SafeHash)
|
54
|
+
value.class.new_zero
|
55
|
+
else
|
56
|
+
## assume "value" object / semantics (e.g. 0, false, '0x0000' etc.) and pass through
|
57
|
+
value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
old_new( *values )
|
61
|
+
end
|
62
|
+
|
63
|
+
klass
|
64
|
+
end # method build_class
|
65
|
+
|
66
|
+
class << self
|
67
|
+
alias_method :old_new, :new # note: store "old" orginal version of new
|
68
|
+
alias_method :new, :build_class # replace original version with create
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.zero
|
72
|
+
## note: freeze return new zero (for "singelton" & "immutable" zero instance)
|
73
|
+
## todo/fix:
|
74
|
+
## in build_class add freeze for composite/reference objects
|
75
|
+
## that is, arrays, hash mappings, structs etc.
|
76
|
+
## freeze only works for now for "value" objects e.g. integer, bool, etc.
|
77
|
+
@zero ||= new_zero.freeze
|
78
|
+
end
|
79
|
+
end # class SafeStruct
|
data/lib/safestruct/version.rb
CHANGED
data/test/helper.rb
ADDED
data/test/test_array.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_array.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
|
11
|
+
class TestArray < MiniTest::Test
|
12
|
+
|
13
|
+
Array_Integer = SafeArray.build_class( Integer )
|
14
|
+
Array_Bool = SafeArray.build_class( Bool )
|
15
|
+
|
16
|
+
def test_integer
|
17
|
+
pp Array_Integer
|
18
|
+
pp ary = Array_Integer.new
|
19
|
+
|
20
|
+
assert_equal Integer, Array_Integer.klass_value
|
21
|
+
assert_equal 0, ary[0]
|
22
|
+
assert_equal 0, ary[1]
|
23
|
+
|
24
|
+
ary[0] = 101
|
25
|
+
ary[1] = 102
|
26
|
+
assert_equal 101, ary[0]
|
27
|
+
assert_equal 102, ary[1]
|
28
|
+
|
29
|
+
## check Array.of (uses cached classes)
|
30
|
+
assert_equal Array_Integer, Array.of( Integer ).class
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def test_bool
|
35
|
+
pp Array_Bool
|
36
|
+
pp ary = Array_Bool.new
|
37
|
+
|
38
|
+
assert_equal Bool, Array_Bool.klass_value
|
39
|
+
assert_equal false, ary[0]
|
40
|
+
assert_equal false, ary[1]
|
41
|
+
|
42
|
+
ary[0] = true
|
43
|
+
ary[1] = true
|
44
|
+
assert_equal true, ary[0]
|
45
|
+
assert_equal true, ary[1]
|
46
|
+
|
47
|
+
## check Array.of (uses cached classes)
|
48
|
+
assert_equal Array_Bool, Array.of( Bool ).class
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
end # class TestArray
|
data/test/test_hash.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_hash.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
|
11
|
+
class TestHash < MiniTest::Test
|
12
|
+
|
13
|
+
## sig: [Integer, Bool, Integer, Address]
|
14
|
+
Voter = SafeStruct.new( weight: 0, voted: false, vote: 0, delegate: '0x0000' )
|
15
|
+
|
16
|
+
Hash_X_Integer = SafeHash.build_class( String, Integer )
|
17
|
+
Hash_X_Bool = SafeHash.build_class( String, Bool )
|
18
|
+
Hash_X_Voter = SafeHash.build_class( String, Voter )
|
19
|
+
|
20
|
+
def test_integer
|
21
|
+
pp Hash_X_Integer
|
22
|
+
pp h = Hash_X_Integer.new
|
23
|
+
|
24
|
+
assert_equal Integer, Hash_X_Integer.klass_value
|
25
|
+
assert_equal 0, h['0x1111']
|
26
|
+
assert_equal 0, h['0x2222']
|
27
|
+
|
28
|
+
h['0x1111'] = 101
|
29
|
+
h['0x2222'] = 102
|
30
|
+
assert_equal 101, h['0x1111']
|
31
|
+
assert_equal 102, h['0x2222']
|
32
|
+
|
33
|
+
## check Mapping.of (uses cached classes)
|
34
|
+
assert_equal Hash_X_Integer, Mapping.of( String => Integer ).class
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def test_bool
|
39
|
+
pp Hash_X_Bool
|
40
|
+
pp h = Hash_X_Bool.new
|
41
|
+
|
42
|
+
assert_equal Bool, Hash_X_Bool.klass_value
|
43
|
+
assert_equal false, h['0x1111']
|
44
|
+
assert_equal false, h['0x2222']
|
45
|
+
|
46
|
+
h['0x1111'] = true
|
47
|
+
h['0x2222'] = true
|
48
|
+
assert_equal true, h['0x1111']
|
49
|
+
assert_equal true, h['0x2222']
|
50
|
+
|
51
|
+
## check Mapping.of (uses cached classes)
|
52
|
+
assert_equal Hash_X_Bool, Mapping.of( String => Bool ).class
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def test_voter
|
57
|
+
pp Hash_X_Voter
|
58
|
+
pp h = Hash_X_Voter.new
|
59
|
+
|
60
|
+
assert_equal Voter, Hash_X_Voter.klass_value
|
61
|
+
assert_equal Voter.zero, h['0x1111']
|
62
|
+
assert_equal Voter.zero, h['0x2222']
|
63
|
+
|
64
|
+
h['0x1111'].voted = true
|
65
|
+
h['0x2222'].voted = true
|
66
|
+
assert_equal true, h['0x1111'].voted?
|
67
|
+
assert_equal true, h['0x2222'].voted?
|
68
|
+
|
69
|
+
pp h['0x1111']
|
70
|
+
pp h['0x2222']
|
71
|
+
|
72
|
+
## check Mapping.of (uses cached classes)
|
73
|
+
assert_equal Hash_X_Voter, Mapping.of( String => Voter ).class
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
end # class TestHash
|
data/test/test_struct.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
###
|
4
|
+
# to run use
|
5
|
+
# ruby -I ./lib -I ./test test/test_struct.rb
|
6
|
+
|
7
|
+
|
8
|
+
require 'helper'
|
9
|
+
|
10
|
+
|
11
|
+
class TestStruct < MiniTest::Test
|
12
|
+
|
13
|
+
## sig: [Integer, Bool, Integer, Address]
|
14
|
+
Voter = SafeStruct.new( weight: 0, voted: false, vote: 0, delegate: '0x0000' )
|
15
|
+
|
16
|
+
## sig: [Address, Integer, Integer, Money]
|
17
|
+
Bet = SafeStruct.new( user: '0x0000', block: 0, cap: 0, amount: 0 )
|
18
|
+
|
19
|
+
|
20
|
+
def test_voter
|
21
|
+
assert_equal Voter.zero, Voter.zero
|
22
|
+
assert_equal '0x0000', Voter.zero.delegate
|
23
|
+
assert_equal false, Voter.zero.voted?
|
24
|
+
assert_equal 0, Voter.zero.weight
|
25
|
+
assert_equal 0, Voter.zero.vote
|
26
|
+
assert_equal true, Voter.zero.frozen?
|
27
|
+
|
28
|
+
voter = Voter.new_zero
|
29
|
+
|
30
|
+
assert_equal false, voter.frozen?
|
31
|
+
assert_equal Voter.zero, voter
|
32
|
+
assert Voter.zero == voter
|
33
|
+
assert Voter.zero.eql?( voter )
|
34
|
+
|
35
|
+
voter.delegate = '0x1111'
|
36
|
+
pp voter
|
37
|
+
|
38
|
+
assert Voter.zero != voter
|
39
|
+
|
40
|
+
voter2 = Voter.new( 0, false, 0, '0x0000' )
|
41
|
+
assert_equal Voter.zero, voter2
|
42
|
+
assert_equal false, voter2.frozen?
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_bet
|
46
|
+
pp Bet
|
47
|
+
bet = Bet.new_zero
|
48
|
+
pp bet
|
49
|
+
|
50
|
+
assert_equal Bet.zero, bet
|
51
|
+
|
52
|
+
bet.cap = 20_000
|
53
|
+
bet.amount = 100
|
54
|
+
|
55
|
+
assert_equal false, bet.frozen?
|
56
|
+
assert_equal 20_000, bet.cap
|
57
|
+
assert_equal 100, bet.amount
|
58
|
+
assert Bet.zero != bet
|
59
|
+
|
60
|
+
pp bet
|
61
|
+
|
62
|
+
pp Bet.zero
|
63
|
+
pp Bet.zero
|
64
|
+
|
65
|
+
assert_equal Bet.zero, Bet.zero
|
66
|
+
assert_equal '0x0000', Bet.zero.user
|
67
|
+
assert_equal 0, Bet.zero.block
|
68
|
+
assert_equal 0, Bet.zero.cap
|
69
|
+
assert_equal 0, Bet.zero.amount
|
70
|
+
assert_equal true, Bet.zero.frozen?
|
71
|
+
|
72
|
+
bet2 = Bet.new( '0x0000', 0, 0, 0 )
|
73
|
+
assert_equal Bet.zero, bet2
|
74
|
+
assert_equal false, bet2.frozen?
|
75
|
+
end
|
76
|
+
|
77
|
+
end # class TestStruct
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: safestruct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
@@ -53,7 +53,14 @@ files:
|
|
53
53
|
- README.md
|
54
54
|
- Rakefile
|
55
55
|
- lib/safestruct.rb
|
56
|
+
- lib/safestruct/safe_array.rb
|
57
|
+
- lib/safestruct/safe_hash.rb
|
58
|
+
- lib/safestruct/safe_struct.rb
|
56
59
|
- lib/safestruct/version.rb
|
60
|
+
- test/helper.rb
|
61
|
+
- test/test_array.rb
|
62
|
+
- test/test_hash.rb
|
63
|
+
- test/test_struct.rb
|
57
64
|
homepage: https://github.com/s6ruby/safestruct
|
58
65
|
licenses:
|
59
66
|
- Public Domain
|