From 58d67155b7e9f142acb683622694274b7dfb6b62 Mon Sep 17 00:00:00 2001
From: Vladlen Popolitov <vladlenpopolitov@list.ru>
Date: Thu, 9 Jul 2026 09:46:16 +0300
Subject: [PATCH v1] add SQL type INT16 based on int128

---
 contrib/int16/Makefile                    |   27 +
 contrib/int16/README                      |  218 ++
 contrib/int16/expected/init.out           |    1 +
 contrib/int16/expected/int16.out          |  652 +++++
 contrib/int16/expected/int16_funcs.out    |  463 ++++
 contrib/int16/expected/int16_numeric.out  |  456 ++++
 contrib/int16/expected/int16_overflow.out |  811 ++++++
 contrib/int16/gen_expected_values.c       |  398 +++
 contrib/int16/int16--1.0.sql              | 1253 +++++++++
 contrib/int16/int16.c                     | 2862 +++++++++++++++++++++
 contrib/int16/int16.control               |    6 +
 contrib/int16/int16.h                     |   65 +
 contrib/int16/int16_funcs.c               |  607 +++++
 contrib/int16/int16_numeric.c             |  100 +
 contrib/int16/meson.build                 |   54 +
 contrib/int16/sql/init.sql                |    1 +
 contrib/int16/sql/int16.sql               |  187 ++
 contrib/int16/sql/int16_funcs.sql         |  216 ++
 contrib/int16/sql/int16_numeric.sql       |  179 ++
 contrib/int16/sql/int16_overflow.sql      |  257 ++
 contrib/meson.build                       |    1 +
 21 files changed, 8814 insertions(+)
 create mode 100644 contrib/int16/Makefile
 create mode 100644 contrib/int16/README
 create mode 100644 contrib/int16/expected/init.out
 create mode 100644 contrib/int16/expected/int16.out
 create mode 100644 contrib/int16/expected/int16_funcs.out
 create mode 100644 contrib/int16/expected/int16_numeric.out
 create mode 100644 contrib/int16/expected/int16_overflow.out
 create mode 100644 contrib/int16/gen_expected_values.c
 create mode 100644 contrib/int16/int16--1.0.sql
 create mode 100644 contrib/int16/int16.c
 create mode 100644 contrib/int16/int16.control
 create mode 100644 contrib/int16/int16.h
 create mode 100644 contrib/int16/int16_funcs.c
 create mode 100644 contrib/int16/int16_numeric.c
 create mode 100644 contrib/int16/meson.build
 create mode 100644 contrib/int16/sql/init.sql
 create mode 100644 contrib/int16/sql/int16.sql
 create mode 100644 contrib/int16/sql/int16_funcs.sql
 create mode 100644 contrib/int16/sql/int16_numeric.sql
 create mode 100644 contrib/int16/sql/int16_overflow.sql

diff --git a/contrib/int16/Makefile b/contrib/int16/Makefile
new file mode 100644
index 00000000000..9539387adc7
--- /dev/null
+++ b/contrib/int16/Makefile
@@ -0,0 +1,27 @@
+# contrib/int16/Makefile
+
+MODULE_big = int16
+OBJS = \
+	$(WIN32RES) \
+	int16.o \
+	int16_numeric.o \
+	int16_funcs.o
+
+EXTENSION = int16
+DATA = int16--1.0.sql
+PGFILEDESC = "int16 - 128-bit signed integer data type"
+
+HEADERS = int16.h
+
+REGRESS = init int16 int16_overflow int16_numeric int16_funcs
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/int16
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/int16/README b/contrib/int16/README
new file mode 100644
index 00000000000..43e782003b5
--- /dev/null
+++ b/contrib/int16/README
@@ -0,0 +1,218 @@
+PostgreSQL contrib module int16
+===================================
+
+This module provides the int16 data type, a 128-bit signed integer with a
+range of approximately -1.7e38 to +1.7e38.
+
+DESCRIPTION
+-----------
+
+The int16 extension adds a 128-bit signed integer data type to PostgreSQL,
+extending the built-in int8 (bigint) type. It is designed for applications
+that require integer values beyond the 64-bit range.
+
+Key features:
+- Storage: 16 bytes, passed by reference, aligned on 8-byte boundary
+- Full arithmetic: +, -, *, /, %, unary -, unary +, abs()
+- Full comparison: =, <>, <, >, <=, >=, and 3-way cmp()
+- Bitwise operators: &, |, #, ~, <<, >>
+- Cross-type operations with int2, int4, int8 (arithmetic and comparison)
+- Type casts: implicit from int2/int4/int8/numeric, assignment to int2/int4/int8/numeric
+- B-tree and Hash index support (including deduplication and skip scan)
+- Aggregates: sum(), avg(), min(), max() (parallel-safe)
+- Window functions: RANGE support via in_range()
+- generate_series() for int16 ranges
+- Mathematical functions: gcd(), lcm(), factorial()
+
+
+BUILDING
+--------
+
+To build the module from the PostgreSQL source tree:
+
+    cd contrib/int16
+    make USE_PGXS=1
+    make USE_PGXS=1 install
+
+After installation, enable the extension in your database:
+
+    CREATE EXTENSION int16;
+
+USAGE
+-----
+
+Once installed, the int16 type is available in all schemas.
+
+Basic usage:
+
+    SELECT '123456789012345678901234567890'::int16;
+
+Create tables with int16 columns:
+
+    CREATE TABLE t (id serial PRIMARY KEY, value int16);
+    INSERT INTO t (value) VALUES ('170141183460469231731687303715884105727'::int16);
+    SELECT * FROM t WHERE value > '100000000000000000000'::int16;
+
+FUNCTIONS AND OPERATORS
+-----------------------
+
+Input/Output:
+    int16in(cstring)       - Parse string to int16
+    int16out(int16)        - Convert int16 to string
+    int16recv(internal)    - Receive binary data (16 bytes, network byte order)
+    int16send(int16)       - Send binary data
+
+Arithmetic (int16 op int16):
+    +   int16pl   - Addition
+    -   int16mi   - Subtraction
+    *   int16mul  - Multiplication
+    /   int16div  - Integer division
+    %   int16mod  - Remainder
+    -   int16um   - Unary minus
+    +   int16up   - Unary plus
+    @   int16abs  - Absolute value
+
+Comparison (int16 op int16):
+    =   int16eq  - Equal
+    <>  int16ne  - Not equal
+    <   int16lt  - Less than
+    >   int16gt  - Greater than
+    <=  int16le  - Less than or equal
+    >=  int16ge  - Greater than or equal
+    int16_cmp(a,b) - Three-way comparison (-1, 0, 1)
+
+Bitwise:
+    &   int16and - Bitwise AND
+    |   int16or  - Bitwise OR
+    #   int16xor - Bitwise XOR
+    ~   int16not - Bitwise NOT
+    <<  int16shl - Left shift (n is int4)
+    >>  int16shr - Arithmetic right shift (n is int4)
+
+Cross-type arithmetic (int16 with int8/int4/int2, both directions):
+    +, -, *, /   - Functions: int168pl, int816pl, int164pl, int416pl, etc.
+
+Cross-type comparison (int16 with int8/int4/int2, both directions):
+    =, <>, <, >, <=, >=  - Functions: int168eq, int816eq, int164eq, etc.
+    _cmp functions       - int168cmp, int816cmp, int164cmp, etc.
+
+Type casts:
+    int8/int4/int2 -> int16          IMPLICIT (widening)
+    int16 -> int8/int4/int2          ASSIGNMENT (narrowing)
+    int16 -> numeric                 IMPLICIT (widening)
+    numeric -> int16                 ASSIGNMENT (narrowing)
+
+Aggregates (parallel-safe):
+    sum(int16)    -> int16      - Sum (error on overflow)
+    avg(int16)    -> numeric    - Average
+    min(int16)    -> int16      - Minimum
+    max(int16)    -> int16      - Maximum
+
+Mathematical functions:
+    gcd(int16, int16)    - Greatest common divisor
+    lcm(int16, int16)    - Least common multiple
+    factorial(int16)     - Factorial (returns numeric)
+
+generate_series:
+    generate_series(start int16, stop int16)                     - Step 1
+    generate_series(start int16, stop int16, step int16)         - Custom step
+
+Window functions:
+    in_range(val int16, base int16, offset int16, ...) - RANGE frame support
+
+Index support:
+    B-tree  - int16_ops (includes sortsupport, in_range, equalimage, skipsupport)
+    Hash    - int16_ops
+
+EXAMPLES
+--------
+
+1. Basic arithmetic:
+
+    SELECT '100000000000000000000'::int16 + '1'::int16;
+    -- 100000000000000000001
+
+    SELECT '100000000000000000000'::int16 * '2'::int16;
+    -- 200000000000000000000
+
+    SELECT '100000000000000000000'::int16 / '3'::int16;
+    -- 33333333333333333333
+
+    SELECT @('-100000000000000000000'::int16);
+    -- 100000000000000000000
+
+2. Cross-type operations:
+
+    SELECT '100'::int16 + 50;        -- int16 + int4 -> int16
+    SELECT 50 + '100'::int16;        -- int4 + int16 -> int16
+    SELECT '100'::int16 + 50::int8;  -- int16 + int8 -> int16
+    SELECT '100'::int16 = 100;       -- true
+
+3. Aggregates:
+
+    CREATE TABLE big_values (val int16);
+    INSERT INTO big_values VALUES
+        ('100000000000000000000'::int16),
+        ('200000000000000000000'::int16),
+        ('300000000000000000000'::int16);
+
+    SELECT sum(val) FROM big_values;   -- 600000000000000000000
+    SELECT avg(val) FROM big_values;   -- 200000000000000000000.0000000000000000
+
+4. generate_series:
+
+    SELECT * FROM generate_series('1'::int16, '5'::int16);
+    SELECT * FROM generate_series('5'::int16, '1'::int16, '-1'::int16);
+
+5. Window functions with RANGE:
+
+    SELECT id, amount, count(*) OVER (
+        ORDER BY amount
+        RANGE BETWEEN '10'::int16 PRECEDING AND '10'::int16 FOLLOWING
+    ) FROM sales;
+
+6. B-tree and Hash indexes:
+
+    CREATE TABLE t (val int16);
+    CREATE INDEX ON t USING btree (val);
+    CREATE INDEX ON t USING hash (val);
+
+LIMITATIONS
+-----------
+
+- Passed by reference: int16 is 16 bytes and passed by reference (like uuid),
+  not by value (like int8). This may introduce slight overhead compared to int8.
+
+- Missing functions: ceil(), floor(), round(), sign(), sqrt(), width_bucket()
+  are not provided. Cast to numeric for these operations:
+    SELECT round('123'::int16::numeric);
+
+- avg() returns numeric: Average is computed in numeric because int16 cannot
+  always represent the exact division result.
+
+- sum() can overflow: Sum is computed as int16 and raises an error on overflow
+  (unlike sum(int8) which uses numeric internally).
+
+- factorial() limited to 32177: For n > 32177, the result overflows numeric.
+
+- No GiST, GIN, SP-GiST, BRIN support: Only B-tree and Hash indexes are
+  implemented.
+
+- Binary protocol: int16recv expects exactly 16 bytes in network byte order
+  (most significant 64-bit block first).
+
+- gcd and lcm with INT16_MIN:
+    gcd(INT16_MIN, 0), gcd(INT16_MIN, INT16_MIN), and lcm(INT16_MAX, 2)
+    raise overflow errors because the absolute value of INT16_MIN is not
+    representable in 128 bits. gcd(INT16_MIN, -1) returns 1.
+
+
+TESTING
+-------
+
+Run the regression tests (requires a running PostgreSQL server):
+
+    cd contrib/int16
+    make installcheck
+
+Test files are located in sql/, expected results in expected/.
diff --git a/contrib/int16/expected/init.out b/contrib/int16/expected/init.out
new file mode 100644
index 00000000000..5a7a6cc6411
--- /dev/null
+++ b/contrib/int16/expected/init.out
@@ -0,0 +1 @@
+CREATE EXTENSION int16;
diff --git a/contrib/int16/expected/int16.out b/contrib/int16/expected/int16.out
new file mode 100644
index 00000000000..bbae58045fa
--- /dev/null
+++ b/contrib/int16/expected/int16.out
@@ -0,0 +1,652 @@
+-- Test the int16 (128-bit signed integer) extension
+--
+-- I/O tests
+--
+SELECT '0'::int16;
+ int16 
+-------
+ 0
+(1 row)
+
+SELECT '1'::int16;
+ int16 
+-------
+ 1
+(1 row)
+
+SELECT '-1'::int16;
+ int16 
+-------
+ -1
+(1 row)
+
+SELECT '42'::int16;
+ int16 
+-------
+ 42
+(1 row)
+
+SELECT '-42'::int16;
+ int16 
+-------
+ -42
+(1 row)
+
+SELECT '9223372036854775807'::int16;  -- INT8_MAX
+        int16        
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT '-9223372036854775808'::int16; -- INT8_MIN
+        int16         
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16;  -- INT16_MAX
+                  int16                  
+-----------------------------------------
+ 170141183460469231731687303715884105727
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16; -- INT16_MIN
+                  int16                   
+------------------------------------------
+ -170141183460469231731687303715884105728
+(1 row)
+
+SELECT '  123  '::int16;  -- whitespace
+ int16 
+-------
+ 123
+(1 row)
+
+SELECT '+42'::int16;
+ int16 
+-------
+ 42
+(1 row)
+
+-- Invalid input
+SELECT ''::int16;
+ERROR:  invalid input syntax for type int16: ""
+LINE 1: SELECT ''::int16;
+               ^
+SELECT 'abc'::int16;
+ERROR:  invalid input syntax for type int16: "abc"
+LINE 1: SELECT 'abc'::int16;
+               ^
+SELECT '1.5'::int16;
+ERROR:  invalid input syntax for type int16: "1.5"
+LINE 1: SELECT '1.5'::int16;
+               ^
+SELECT '1 2'::int16;
+ERROR:  invalid input syntax for type int16: "1 2"
+LINE 1: SELECT '1 2'::int16;
+               ^
+SELECT '170141183460469231731687303715884105728'::int16;  -- overflow (MAX+1)
+ERROR:  value "170141183460469231731687303715884105728" is out of range for type int16
+LINE 1: SELECT '170141183460469231731687303715884105728'::int16;
+               ^
+SELECT '-170141183460469231731687303715884105729'::int16; -- overflow (MIN-1)
+ERROR:  value "-170141183460469231731687303715884105729" is out of range for type int16
+LINE 1: SELECT '-170141183460469231731687303715884105729'::int16;
+               ^
+--
+-- Arithmetic: int16 vs int16
+--
+SELECT '100'::int16 + '50'::int16;
+ ?column? 
+----------
+ 150
+(1 row)
+
+SELECT '100'::int16 - '50'::int16;
+ ?column? 
+----------
+ 50
+(1 row)
+
+SELECT '100'::int16 * '50'::int16;
+ ?column? 
+----------
+ 5000
+(1 row)
+
+SELECT '100'::int16 / '50'::int16;
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT '100'::int16 % '30'::int16;
+ ?column? 
+----------
+ 10
+(1 row)
+
+SELECT -'42'::int16;
+ ?column? 
+----------
+ -42
+(1 row)
+
+SELECT +'42'::int16;
+ ?column? 
+----------
+ 42
+(1 row)
+
+SELECT @('-42'::int16);
+ ?column? 
+----------
+ 42
+(1 row)
+
+-- Overflow on arithmetic
+SELECT '170141183460469231731687303715884105727'::int16 + '1'::int16;
+ERROR:  integer out of range
+SELECT '-170141183460469231731687303715884105728'::int16 - '1'::int16;
+ERROR:  integer out of range
+SELECT '85070591730234615865843651857942052864'::int16 * '3'::int16;
+ERROR:  integer out of range
+SELECT '-170141183460469231731687303715884105728'::int16 / '-1'::int16;
+ERROR:  integer out of range
+-- Division by zero
+SELECT '1'::int16 / '0'::int16;
+ERROR:  division by zero
+SELECT '1'::int16 % '0'::int16;
+ERROR:  division by zero
+--
+-- Comparison: int16 vs int16
+--
+SELECT '1'::int16 = '1'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '1'::int16 = '2'::int16;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '1'::int16 <> '2'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '1'::int16 < '2'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '2'::int16 > '1'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '1'::int16 <= '1'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '1'::int16 >= '1'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT int16_cmp('5'::int16, '3'::int16);
+ int16_cmp 
+-----------
+         1
+(1 row)
+
+SELECT int16_cmp('3'::int16, '5'::int16);
+ int16_cmp 
+-----------
+        -1
+(1 row)
+
+SELECT int16_cmp('5'::int16, '5'::int16);
+ int16_cmp 
+-----------
+         0
+(1 row)
+
+--
+-- Bitwise operators
+--
+SELECT '255'::int16 & '15'::int16;
+ ?column? 
+----------
+ 15
+(1 row)
+
+SELECT '240'::int16 | '15'::int16;
+ ?column? 
+----------
+ 255
+(1 row)
+
+SELECT '255'::int16 # '15'::int16;
+ ?column? 
+----------
+ 240
+(1 row)
+
+SELECT ~'0'::int16;
+ ?column? 
+----------
+ -1
+(1 row)
+
+SELECT '1'::int16 << 4;
+ ?column? 
+----------
+ 16
+(1 row)
+
+SELECT '256'::int16 >> 4;
+ ?column? 
+----------
+ 16
+(1 row)
+
+SELECT '-1'::int16 >> 1;  -- arithmetic shift
+ ?column? 
+----------
+ -1
+(1 row)
+
+--
+-- Cross-type arithmetic: int16 op int8
+--
+SELECT '100'::int16 + '50'::int8;
+ ?column? 
+----------
+ 150
+(1 row)
+
+SELECT '50'::int8 + '100'::int16;
+ ?column? 
+----------
+ 150
+(1 row)
+
+SELECT '100'::int16 - '50'::int8;
+ ?column? 
+----------
+ 50
+(1 row)
+
+SELECT '50'::int8 - '100'::int16;
+ ?column? 
+----------
+ -50
+(1 row)
+
+SELECT '100'::int16 * '50'::int8;
+ ?column? 
+----------
+ 5000
+(1 row)
+
+SELECT '50'::int8 * '100'::int16;
+ ?column? 
+----------
+ 5000
+(1 row)
+
+SELECT '100'::int16 / '50'::int8;
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT '50'::int8 / '100'::int16;
+ ?column? 
+----------
+ 0
+(1 row)
+
+--
+-- Cross-type arithmetic: int16 op int4
+--
+SELECT '100'::int16 + 50;
+ ?column? 
+----------
+ 150
+(1 row)
+
+SELECT 50 + '100'::int16;
+ ?column? 
+----------
+ 150
+(1 row)
+
+SELECT '100'::int16 - 50;
+ ?column? 
+----------
+ 50
+(1 row)
+
+SELECT 50 - '100'::int16;
+ ?column? 
+----------
+ -50
+(1 row)
+
+SELECT '100'::int16 * 50;
+ ?column? 
+----------
+ 5000
+(1 row)
+
+SELECT 50 * '100'::int16;
+ ?column? 
+----------
+ 5000
+(1 row)
+
+SELECT '100'::int16 / 50;
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 50 / '100'::int16;
+ ?column? 
+----------
+ 0
+(1 row)
+
+--
+-- Cross-type arithmetic: int16 op int2
+--
+SELECT '100'::int16 + 50::int2;
+ ?column? 
+----------
+ 150
+(1 row)
+
+SELECT 50::int2 + '100'::int16;
+ ?column? 
+----------
+ 150
+(1 row)
+
+SELECT '100'::int16 - 50::int2;
+ ?column? 
+----------
+ 50
+(1 row)
+
+SELECT 50::int2 - '100'::int16;
+ ?column? 
+----------
+ -50
+(1 row)
+
+SELECT '100'::int16 * 50::int2;
+ ?column? 
+----------
+ 5000
+(1 row)
+
+SELECT 50::int2 * '100'::int16;
+ ?column? 
+----------
+ 5000
+(1 row)
+
+SELECT '100'::int16 / 50::int2;
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT 50::int2 / '100'::int16;
+ ?column? 
+----------
+ 0
+(1 row)
+
+--
+-- Cross-type comparison
+--
+SELECT '100'::int16 = '100'::int8;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '100'::int8 = '100'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '100'::int16 < '101'::int8;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '101'::int8 > '100'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '100'::int16 = 100;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 100 = '100'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '100'::int16 < 101;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 101 > '100'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '100'::int16 = 100::int2;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 100::int2 = '100'::int16;
+ ?column? 
+----------
+ t
+(1 row)
+
+--
+-- Casts
+--
+SELECT '9223372036854775807'::int8::int16;
+        int16        
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT '-9223372036854775808'::int8::int16;
+        int16         
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT '100'::int16::int8;
+ int8 
+------
+  100
+(1 row)
+
+SELECT '100'::int4::int16;
+ int16 
+-------
+ 100
+(1 row)
+
+SELECT '100'::int16::int4;
+ int4 
+------
+  100
+(1 row)
+
+SELECT '100'::int2::int16;
+ int16 
+-------
+ 100
+(1 row)
+
+SELECT '100'::int16::int2;
+ int2 
+------
+  100
+(1 row)
+
+-- Cast overflow
+SELECT '170141183460469231731687303715884105727'::int16::int8;
+ERROR:  integer out of range
+SELECT '2147483648'::int16::int4;
+ERROR:  integer out of range
+SELECT '32768'::int16::int2;
+ERROR:  integer out of range
+--
+-- Aggregates
+--
+CREATE TABLE int16_test (x int16);
+INSERT INTO int16_test VALUES ('1'), ('2'), ('3'), ('4'), ('5'), (NULL);
+SELECT sum(x) FROM int16_test;
+ sum 
+-----
+ 15
+(1 row)
+
+SELECT avg(x) FROM int16_test;
+        avg         
+--------------------
+ 3.0000000000000000
+(1 row)
+
+SELECT min(x) FROM int16_test;
+ min 
+-----
+ 1
+(1 row)
+
+SELECT max(x) FROM int16_test;
+ max 
+-----
+ 5
+(1 row)
+
+SELECT sum(x) FROM int16_test WHERE x IS NOT NULL;
+ sum 
+-----
+ 15
+(1 row)
+
+-- Sum overflow
+INSERT INTO int16_test VALUES ('170141183460469231731687303715884105727');
+SELECT sum(x) FROM int16_test;
+ERROR:  integer out of range
+-- Clean up the overflow row
+DELETE FROM int16_test WHERE x = '170141183460469231731687303715884105727'::int16;
+-- Empty table
+DELETE FROM int16_test;
+SELECT sum(x) FROM int16_test;
+ sum 
+-----
+ 
+(1 row)
+
+SELECT avg(x) FROM int16_test;
+ avg 
+-----
+    
+(1 row)
+
+SELECT min(x) FROM int16_test;
+ min 
+-----
+ 
+(1 row)
+
+SELECT max(x) FROM int16_test;
+ max 
+-----
+ 
+(1 row)
+
+DROP TABLE int16_test;
+--
+-- B-tree index test
+--
+CREATE TABLE int16_bt (x int16);
+INSERT INTO int16_bt VALUES ('5'), ('3'), ('8'), ('1'), ('9'), ('2'), ('7'), ('4'), ('6');
+CREATE INDEX idx_int16_bt ON int16_bt USING btree (x);
+SET enable_seqscan = off;
+SELECT * FROM int16_bt WHERE x > '4'::int16 ORDER BY x;
+ x 
+---
+ 5
+ 6
+ 7
+ 8
+ 9
+(5 rows)
+
+SELECT * FROM int16_bt WHERE x <= '3'::int16 ORDER BY x;
+ x 
+---
+ 1
+ 2
+ 3
+(3 rows)
+
+SELECT * FROM int16_bt WHERE x = '7'::int16;
+ x 
+---
+ 7
+(1 row)
+
+RESET enable_seqscan;
+DROP TABLE int16_bt;
+--
+-- Hash index test
+--
+CREATE TABLE int16_hash (x int16);
+INSERT INTO int16_hash VALUES ('5'), ('3'), ('8'), ('1'), ('9');
+CREATE INDEX idx_int16_hash ON int16_hash USING hash (x);
+SET enable_seqscan = off;
+SELECT * FROM int16_hash WHERE x = '8'::int16;
+ x 
+---
+ 8
+(1 row)
+
+RESET enable_seqscan;
+DROP TABLE int16_hash;
diff --git a/contrib/int16/expected/int16_funcs.out b/contrib/int16/expected/int16_funcs.out
new file mode 100644
index 00000000000..8a0b420446a
--- /dev/null
+++ b/contrib/int16/expected/int16_funcs.out
@@ -0,0 +1,463 @@
+--
+-- int16_funcs.sql
+--
+-- Regression tests for additional int16 functions: gcd, lcm, factorial,
+-- generate_series, in_range (window support), and btree support functions.
+--
+--
+-- gcd
+--
+-- Basic cases
+SELECT gcd('12'::int16, '8'::int16);   -- 4
+ gcd 
+-----
+ 4
+(1 row)
+
+SELECT gcd('17'::int16, '5'::int16);   -- 1 (coprime)
+ gcd 
+-----
+ 1
+(1 row)
+
+SELECT gcd('100'::int16, '10'::int16); -- 10
+ gcd 
+-----
+ 10
+(1 row)
+
+SELECT gcd('48'::int16, '36'::int16);  -- 12
+ gcd 
+-----
+ 12
+(1 row)
+
+-- gcd with zero
+SELECT gcd('0'::int16, '5'::int16);    -- 5
+ gcd 
+-----
+ 5
+(1 row)
+
+SELECT gcd('5'::int16, '0'::int16);    -- 5
+ gcd 
+-----
+ 5
+(1 row)
+
+SELECT gcd('0'::int16, '0'::int16);    -- 0
+ gcd 
+-----
+ 0
+(1 row)
+
+-- gcd with negatives
+SELECT gcd('-12'::int16, '8'::int16);  -- 4
+ gcd 
+-----
+ 4
+(1 row)
+
+SELECT gcd('12'::int16, '-8'::int16);  -- 4
+ gcd 
+-----
+ 4
+(1 row)
+
+SELECT gcd('-12'::int16, '-8'::int16); -- 4
+ gcd 
+-----
+ 4
+(1 row)
+
+-- gcd with 1
+SELECT gcd('1'::int16, '1'::int16);    -- 1
+ gcd 
+-----
+ 1
+(1 row)
+
+SELECT gcd('100'::int16, '1'::int16);  -- 1
+ gcd 
+-----
+ 1
+(1 row)
+
+-- gcd with same value
+SELECT gcd('42'::int16, '42'::int16);  -- 42
+ gcd 
+-----
+ 42
+(1 row)
+
+-- gcd(INT128_MIN, -1) = 1 (special case)
+SELECT gcd('-170141183460469231731687303715884105728'::int16, '-1'::int16); -- 1
+ gcd 
+-----
+ 1
+(1 row)
+
+-- gcd(INT128_MIN, 0) should error (abs overflow)
+SELECT gcd('-170141183460469231731687303715884105728'::int16, '0'::int16);
+ERROR:  integer out of range
+-- gcd(INT128_MIN, INT128_MIN) should error (abs overflow)
+SELECT gcd('-170141183460469231731687303715884105728'::int16,
+           '-170141183460469231731687303715884105728'::int16);
+ERROR:  integer out of range
+--
+-- lcm
+--
+-- Basic cases
+SELECT lcm('4'::int16, '6'::int16);    -- 12
+ lcm 
+-----
+ 12
+(1 row)
+
+SELECT lcm('3'::int16, '5'::int16);    -- 15 (coprime)
+ lcm 
+-----
+ 15
+(1 row)
+
+SELECT lcm('12'::int16, '8'::int16);   -- 24
+ lcm 
+-----
+ 24
+(1 row)
+
+SELECT lcm('10'::int16, '10'::int16);  -- 10
+ lcm 
+-----
+ 10
+(1 row)
+
+-- lcm with zero
+SELECT lcm('0'::int16, '5'::int16);    -- 0
+ lcm 
+-----
+ 0
+(1 row)
+
+SELECT lcm('5'::int16, '0'::int16);    -- 0
+ lcm 
+-----
+ 0
+(1 row)
+
+SELECT lcm('0'::int16, '0'::int16);    -- 0
+ lcm 
+-----
+ 0
+(1 row)
+
+-- lcm with negatives
+SELECT lcm('-4'::int16, '6'::int16);   -- 12
+ lcm 
+-----
+ 12
+(1 row)
+
+SELECT lcm('4'::int16, '-6'::int16);   -- 12
+ lcm 
+-----
+ 12
+(1 row)
+
+SELECT lcm('-4'::int16, '-6'::int16);  -- 12
+ lcm 
+-----
+ 12
+(1 row)
+
+-- lcm with 1
+SELECT lcm('1'::int16, '7'::int16);    -- 7
+ lcm 
+-----
+ 7
+(1 row)
+
+-- lcm overflow
+SELECT lcm('170141183460469231731687303715884105727'::int16, '2'::int16);
+ERROR:  integer out of range
+--
+-- factorial
+--
+SELECT factorial('0'::int16);  -- 1
+ factorial 
+-----------
+         1
+(1 row)
+
+SELECT factorial('1'::int16);  -- 1
+ factorial 
+-----------
+         1
+(1 row)
+
+SELECT factorial('2'::int16);  -- 2
+ factorial 
+-----------
+         2
+(1 row)
+
+SELECT factorial('5'::int16);  -- 120
+ factorial 
+-----------
+       120
+(1 row)
+
+SELECT factorial('10'::int16); -- 3628800
+ factorial 
+-----------
+   3628800
+(1 row)
+
+SELECT factorial('20'::int16); -- 2432902008176640000
+      factorial      
+---------------------
+ 2432902008176640000
+(1 row)
+
+-- factorial of negative number (error)
+SELECT factorial('-1'::int16);
+ERROR:  factorial of a negative number is undefined
+--
+-- generate_series
+--
+-- Default step (1)
+SELECT * FROM generate_series('1'::int16, '5'::int16);
+ generate_series 
+-----------------
+ 1
+ 2
+ 3
+ 4
+ 5
+(5 rows)
+
+-- Positive step
+SELECT * FROM generate_series('1'::int16, '10'::int16, '2'::int16);
+ generate_series 
+-----------------
+ 1
+ 3
+ 5
+ 7
+ 9
+(5 rows)
+
+-- Negative step (counting down)
+SELECT * FROM generate_series('5'::int16, '1'::int16, '-1'::int16);
+ generate_series 
+-----------------
+ 5
+ 4
+ 3
+ 2
+ 1
+(5 rows)
+
+-- Step of 0 (error)
+SELECT * FROM generate_series('1'::int16, '5'::int16, '0'::int16);
+ERROR:  step size cannot equal zero
+-- Empty range (start > stop with positive step)
+SELECT * FROM generate_series('5'::int16, '1'::int16);
+ generate_series 
+-----------------
+(0 rows)
+
+-- Single element
+SELECT * FROM generate_series('3'::int16, '3'::int16);
+ generate_series 
+-----------------
+ 3
+(1 row)
+
+-- Large step
+SELECT * FROM generate_series('0'::int16, '100000000000000000000'::int16,
+                              '100000000000000000000'::int16);
+    generate_series    
+-----------------------
+ 0
+ 100000000000000000000
+(2 rows)
+
+-- generate_series in a subquery
+SELECT count(*) FROM generate_series('1'::int16, '100'::int16);
+ count 
+-------
+   100
+(1 row)
+
+-- generate_series with large values near INT128_MAX
+SELECT * FROM generate_series(
+    '170141183460469231731687303715884105725'::int16,
+    '170141183460469231731687303715884105727'::int16
+);
+             generate_series             
+-----------------------------------------
+ 170141183460469231731687303715884105725
+ 170141183460469231731687303715884105726
+ 170141183460469231731687303715884105727
+(3 rows)
+
+--
+-- in_range (window function support)
+--
+-- Basic in_range tests (direct function call)
+SELECT in_range('5'::int16, '0'::int16, '10'::int16, false, true);  -- true (5 <= 0+10)
+ in_range 
+----------
+ t
+(1 row)
+
+SELECT in_range('15'::int16, '0'::int16, '10'::int16, false, true); -- false (15 > 10)
+ in_range 
+----------
+ f
+(1 row)
+
+SELECT in_range('5'::int16, '0'::int16, '10'::int16, false, false); -- true (5 >= 0+10? no) false
+ in_range 
+----------
+ f
+(1 row)
+
+-- in_range with sub=true (preceding)
+SELECT in_range('5'::int16, '10'::int16, '3'::int16, true, true);   -- true (5 <= 10-3=7)
+ in_range 
+----------
+ t
+(1 row)
+
+-- Negative offset (error)
+SELECT in_range('5'::int16, '0'::int16, '-1'::int16, false, true);
+ERROR:  invalid preceding or following size in window function
+-- Window function using RANGE with int16
+CREATE TABLE int16_window_test (id int4, val int16);
+INSERT INTO int16_window_test VALUES
+    (1, '10'::int16),
+    (2, '20'::int16),
+    (3, '30'::int16),
+    (4, '40'::int16),
+    (5, '50'::int16);
+-- RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING
+SELECT id, val, count(*) OVER (
+    ORDER BY val
+    RANGE BETWEEN '10'::int16 PRECEDING AND '10'::int16 FOLLOWING
+) AS cnt
+FROM int16_window_test
+ORDER BY id;
+ id | val | cnt 
+----+-----+-----
+  1 | 10  |   2
+  2 | 20  |   3
+  3 | 30  |   3
+  4 | 40  |   3
+  5 | 50  |   2
+(5 rows)
+
+-- RANGE BETWEEN 15 PRECEDING AND 5 FOLLOWING
+SELECT id, val, sum(val) OVER (
+    ORDER BY val
+    RANGE BETWEEN '15'::int16 PRECEDING AND '5'::int16 FOLLOWING
+) AS range_sum
+FROM int16_window_test
+ORDER BY id;
+ id | val | range_sum 
+----+-----+-----------
+  1 | 10  | 10
+  2 | 20  | 30
+  3 | 30  | 50
+  4 | 40  | 70
+  5 | 50  | 90
+(5 rows)
+
+DROP TABLE int16_window_test;
+--
+-- B-tree operator class support functions (sort support, equalimage, skip support)
+--
+-- Verify btree index creation and queries work
+CREATE TABLE int16_btree_test (val int16);
+INSERT INTO int16_btree_test VALUES
+    ('5'::int16), ('3'::int16), ('8'::int16), ('1'::int16), ('9'::int16);
+CREATE INDEX idx_int16_btree ON int16_btree_test USING btree (val);
+-- Query using the index
+SET enable_seqscan = off;
+EXPLAIN (COSTS OFF) SELECT * FROM int16_btree_test WHERE val = '5'::int16;
+                        QUERY PLAN                         
+-----------------------------------------------------------
+ Index Only Scan using idx_int16_btree on int16_btree_test
+   Index Cond: (val = '5'::int16)
+(2 rows)
+
+SELECT * FROM int16_btree_test WHERE val = '5'::int16;
+ val 
+-----
+ 5
+(1 row)
+
+-- Ordered query
+SELECT * FROM int16_btree_test ORDER BY val;
+ val 
+-----
+ 1
+ 3
+ 5
+ 8
+ 9
+(5 rows)
+
+-- Deduplication (btequalimage) - create table with duplicates and verify
+CREATE TABLE int16_dedup_test (val int16);
+INSERT INTO int16_dedup_test VALUES
+    ('5'::int16), ('5'::int16), ('5'::int16), ('3'::int16), ('3'::int16);
+CREATE INDEX idx_int16_dedup ON int16_dedup_test USING btree (val);
+-- Verify deduplication is working (index should have fewer entries)
+SELECT count(*) FROM int16_dedup_test;
+ count 
+-------
+     5
+(1 row)
+
+SET enable_seqscan = off;
+SELECT * FROM int16_dedup_test WHERE val = '5'::int16;
+ val 
+-----
+ 5
+ 5
+ 5
+(3 rows)
+
+SET enable_seqscan = on;
+DROP TABLE int16_dedup_test;
+-- Skip scan test: query on a column with a leading skipped column
+CREATE TABLE int16_skip_test (grp int16, val int4);
+INSERT INTO int16_skip_test VALUES
+    ('1'::int16, 10),
+    ('1'::int16, 20),
+    ('2'::int16, 30),
+    ('2'::int16, 40),
+    ('3'::int16, 50);
+CREATE INDEX idx_int16_skip ON int16_skip_test (grp, val);
+-- DISTINCT on grp should be able to use skip scan
+SET enable_seqscan = off;
+EXPLAIN (COSTS OFF) SELECT DISTINCT grp FROM int16_skip_test ORDER BY grp;
+                          QUERY PLAN                           
+---------------------------------------------------------------
+ Unique
+   ->  Index Only Scan using idx_int16_skip on int16_skip_test
+(2 rows)
+
+SELECT DISTINCT grp FROM int16_skip_test ORDER BY grp;
+ grp 
+-----
+ 1
+ 2
+ 3
+(3 rows)
+
+SET enable_seqscan = on;
+DROP TABLE int16_skip_test;
+DROP TABLE int16_btree_test;
diff --git a/contrib/int16/expected/int16_numeric.out b/contrib/int16/expected/int16_numeric.out
new file mode 100644
index 00000000000..99d3bed7ea7
--- /dev/null
+++ b/contrib/int16/expected/int16_numeric.out
@@ -0,0 +1,456 @@
+--
+-- int16_numeric.sql
+--
+-- Regression tests for casts between int16 (128-bit signed integer) and numeric.
+-- Tests cover: round-trip conversions, edge cases (0, ±1, MIN, MAX, powers of
+-- 10), fractional rounding behavior, NaN/Infinity rejection, overflow detection,
+-- NULL handling, and cast usage in INSERT/SELECT contexts.
+--
+--
+-- int16 → numeric cast (implicit)
+--
+-- Basic values
+SELECT '0'::int16::numeric;
+ numeric 
+---------
+       0
+(1 row)
+
+SELECT '1'::int16::numeric;
+ numeric 
+---------
+       1
+(1 row)
+
+SELECT '-1'::int16::numeric;
+ numeric 
+---------
+      -1
+(1 row)
+
+SELECT '42'::int16::numeric;
+ numeric 
+---------
+      42
+(1 row)
+
+SELECT '-42'::int16::numeric;
+ numeric 
+---------
+     -42
+(1 row)
+
+-- INT128_MAX and INT128_MIN
+SELECT '170141183460469231731687303715884105727'::int16::numeric;  -- INT16_MAX
+                 numeric                 
+-----------------------------------------
+ 170141183460469231731687303715884105727
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16::numeric; -- INT16_MIN
+                 numeric                  
+------------------------------------------
+ -170141183460469231731687303715884105728
+(1 row)
+
+-- Powers of 10 (testing digit boundaries)
+SELECT '1000000000000000000'::int16::numeric;      -- 10^18
+       numeric       
+---------------------
+ 1000000000000000000
+(1 row)
+
+SELECT '10000000000000000000'::int16::numeric;     -- 10^19
+       numeric        
+----------------------
+ 10000000000000000000
+(1 row)
+
+SELECT '100000000000000000000000000000000000000'::int16::numeric; -- 10^38
+                 numeric                 
+-----------------------------------------
+ 100000000000000000000000000000000000000
+(1 row)
+
+-- Values around int64 boundaries
+SELECT '9223372036854775807'::int16::numeric;  -- INT64_MAX
+       numeric       
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT '-9223372036854775808'::int16::numeric; -- INT64_MIN
+       numeric        
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT '9223372036854775808'::int16::numeric;  -- INT64_MAX + 1
+       numeric       
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '-9223372036854775809'::int16::numeric; -- INT64_MIN - 1
+       numeric        
+----------------------
+ -9223372036854775809
+(1 row)
+
+-- 2^64
+SELECT '18446744073709551616'::int16::numeric;
+       numeric        
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '-18446744073709551616'::int16::numeric;
+        numeric        
+-----------------------
+ -18446744073709551616
+(1 row)
+
+-- Use int16_numeric() function directly
+SELECT int16_numeric('123'::int16);
+ int16_numeric 
+---------------
+           123
+(1 row)
+
+SELECT int16_numeric('-456'::int16);
+ int16_numeric 
+---------------
+          -456
+(1 row)
+
+--
+-- numeric → int16 cast (assignment)
+--
+-- Basic values
+SELECT '0'::numeric::int16;
+ int16 
+-------
+ 0
+(1 row)
+
+SELECT '1'::numeric::int16;
+ int16 
+-------
+ 1
+(1 row)
+
+SELECT '-1'::numeric::int16;
+ int16 
+-------
+ -1
+(1 row)
+
+SELECT '42'::numeric::int16;
+ int16 
+-------
+ 42
+(1 row)
+
+SELECT '-42'::numeric::int16;
+ int16 
+-------
+ -42
+(1 row)
+
+-- INT128_MAX and INT128_MIN
+SELECT '170141183460469231731687303715884105727'::numeric::int16;  -- INT16_MAX
+                  int16                  
+-----------------------------------------
+ 170141183460469231731687303715884105727
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::numeric::int16; -- INT16_MIN
+                  int16                   
+------------------------------------------
+ -170141183460469231731687303715884105728
+(1 row)
+
+-- Powers of 10
+SELECT '1000000000000000000'::numeric::int16;      -- 10^18
+        int16        
+---------------------
+ 1000000000000000000
+(1 row)
+
+SELECT '10000000000000000000'::numeric::int16;     -- 10^19
+        int16         
+----------------------
+ 10000000000000000000
+(1 row)
+
+SELECT '100000000000000000000000000000000000000'::numeric::int16; -- 10^38
+                  int16                  
+-----------------------------------------
+ 100000000000000000000000000000000000000
+(1 row)
+
+-- Use numeric_int16() function directly
+SELECT numeric_int16('789'::numeric);
+ numeric_int16 
+---------------
+ 789
+(1 row)
+
+SELECT numeric_int16('-321'::numeric);
+ numeric_int16 
+---------------
+ -321
+(1 row)
+
+--
+-- Fractional rounding (matches int8 behavior: round-half-away-from-zero)
+--
+SELECT '1.4'::numeric::int16;   -- rounds to 1
+ int16 
+-------
+ 1
+(1 row)
+
+SELECT '1.5'::numeric::int16;   -- rounds to 2
+ int16 
+-------
+ 2
+(1 row)
+
+SELECT '1.6'::numeric::int16;   -- rounds to 2
+ int16 
+-------
+ 2
+(1 row)
+
+SELECT '-1.4'::numeric::int16;  -- rounds to -1
+ int16 
+-------
+ -1
+(1 row)
+
+SELECT '-1.5'::numeric::int16;  -- rounds to -2
+ int16 
+-------
+ -2
+(1 row)
+
+SELECT '-1.6'::numeric::int16;  -- rounds to -2
+ int16 
+-------
+ -2
+(1 row)
+
+SELECT '2.5'::numeric::int16;   -- rounds to 3 (round half away from zero)
+ int16 
+-------
+ 3
+(1 row)
+
+SELECT '-2.5'::numeric::int16;  -- rounds to -3
+ int16 
+-------
+ -3
+(1 row)
+
+SELECT '0.4'::numeric::int16;   -- rounds to 0
+ int16 
+-------
+ 0
+(1 row)
+
+SELECT '0.5'::numeric::int16;   -- rounds to 1
+ int16 
+-------
+ 1
+(1 row)
+
+SELECT '-0.5'::numeric::int16;  -- rounds to -1
+ int16 
+-------
+ -1
+(1 row)
+
+SELECT '99999999999999999999.5'::numeric::int16;  -- rounds to 10^20
+         int16         
+-----------------------
+ 100000000000000000000
+(1 row)
+
+--
+-- Round-trip: int16 → numeric → int16
+--
+SELECT '170141183460469231731687303715884105727'::int16::numeric::int16;  -- MAX
+                  int16                  
+-----------------------------------------
+ 170141183460469231731687303715884105727
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16::numeric::int16; -- MIN
+                  int16                   
+------------------------------------------
+ -170141183460469231731687303715884105728
+(1 row)
+
+SELECT '9223372036854775807'::int16::numeric::int16;  -- INT64_MAX
+        int16        
+---------------------
+ 9223372036854775807
+(1 row)
+
+SELECT '18446744073709551616'::int16::numeric::int16; -- 2^64
+        int16         
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '123456789012345678901234567890'::int16::numeric::int16;
+             int16              
+--------------------------------
+ 123456789012345678901234567890
+(1 row)
+
+--
+-- Round-trip: numeric → int16 → numeric
+--
+SELECT '170141183460469231731687303715884105727'::numeric::int16::numeric;
+                 numeric                 
+-----------------------------------------
+ 170141183460469231731687303715884105727
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::numeric::int16::numeric;
+                 numeric                  
+------------------------------------------
+ -170141183460469231731687303715884105728
+(1 row)
+
+SELECT '99999999999999999999999999999999'::numeric::int16::numeric;
+             numeric              
+----------------------------------
+ 99999999999999999999999999999999
+(1 row)
+
+--
+-- NaN and Infinity (should error, matching int8 behavior)
+--
+SELECT 'NaN'::numeric::int16;
+ERROR:  cannot convert NaN to int16
+SELECT 'Infinity'::numeric::int16;
+ERROR:  cannot convert infinity to int16
+SELECT '-Infinity'::numeric::int16;
+ERROR:  cannot convert infinity to int16
+--
+-- Overflow: values outside the int16 range (should error)
+--
+SELECT '170141183460469231731687303715884105728'::numeric::int16;  -- MAX + 1
+ERROR:  value "170141183460469231731687303715884105728" is out of range for type int16
+SELECT '-170141183460469231731687303715884105729'::numeric::int16; -- MIN - 1
+ERROR:  value "-170141183460469231731687303715884105729" is out of range for type int16
+SELECT '999999999999999999999999999999999999999999999999'::numeric::int16; -- way too big
+ERROR:  value "999999999999999999999999999999999999999999999999" is out of range for type int16
+--
+-- NULL handling
+--
+SELECT NULL::int16::numeric;
+ numeric 
+---------
+        
+(1 row)
+
+SELECT NULL::numeric::int16;
+ int16 
+-------
+ 
+(1 row)
+
+--
+-- Cast usage in INSERT/SELECT
+--
+CREATE TABLE int16_numeric_test (
+    id serial PRIMARY KEY,
+    big_val int16,
+    num_val numeric
+);
+-- Insert int16 values, cast to numeric on retrieval
+INSERT INTO int16_numeric_test (big_val) VALUES
+    ('1'),
+    ('-1'),
+    ('170141183460469231731687303715884105727'),
+    ('-170141183460469231731687303715884105728'),
+    ('9223372036854775807');
+SELECT id, big_val::numeric FROM int16_numeric_test ORDER BY id;
+ id |                 big_val                  
+----+------------------------------------------
+  1 |                                        1
+  2 |                                       -1
+  3 |  170141183460469231731687303715884105727
+  4 | -170141183460469231731687303715884105728
+  5 |                      9223372036854775807
+(5 rows)
+
+-- Insert numeric values, cast to int16
+INSERT INTO int16_numeric_test (num_val) VALUES
+    ('100'),
+    ('-200'),
+    ('99999999999999999999999999999999');
+SELECT id, num_val::int16 FROM int16_numeric_test WHERE num_val IS NOT NULL ORDER BY id;
+ id |             num_val              
+----+----------------------------------
+  6 | 100
+  7 | -200
+  8 | 99999999999999999999999999999999
+(3 rows)
+
+-- Use cast in WHERE clause comparison
+SELECT id, big_val FROM int16_numeric_test
+WHERE big_val::numeric > '0'::numeric
+ORDER BY id;
+ id |                 big_val                 
+----+-----------------------------------------
+  1 | 1
+  3 | 170141183460469231731687303715884105727
+  5 | 9223372036854775807
+(3 rows)
+
+-- Use cast in arithmetic
+SELECT '100'::int16::numeric + '50'::numeric AS sum_result;
+ sum_result 
+------------
+        150
+(1 row)
+
+SELECT '100'::numeric::int16 + '50'::int16 AS sum_int16;
+ sum_int16 
+-----------
+ 150
+(1 row)
+
+-- Drop the test table
+DROP TABLE int16_numeric_test;
+--
+-- Comparison of cast results with expected values
+--
+-- int16 values cast to numeric should compare equal
+SELECT '170141183460469231731687303715884105727'::int16::numeric =
+       '170141183460469231731687303715884105727'::numeric AS max_equal;
+ max_equal 
+-----------
+ t
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16::numeric =
+       '-170141183460469231731687303715884105728'::numeric AS min_equal;
+ min_equal 
+-----------
+ t
+(1 row)
+
+-- Numeric cast to int16 should compare equal
+SELECT '123456789012345678901234567890'::numeric::int16 =
+       '123456789012345678901234567890'::int16 AS roundtrip_equal;
+ roundtrip_equal 
+-----------------
+ t
+(1 row)
+
diff --git a/contrib/int16/expected/int16_overflow.out b/contrib/int16/expected/int16_overflow.out
new file mode 100644
index 00000000000..1be3128ce27
--- /dev/null
+++ b/contrib/int16/expected/int16_overflow.out
@@ -0,0 +1,811 @@
+--
+-- int16_overflow.sql
+--
+-- Regression tests for the int16 (128-bit signed integer) extension focusing
+-- on values that exceed the int64 range, overflow detection, boundary
+-- conditions, and sign combinations.  These tests are designed to verify
+-- correctness of both the native __int128 and the struct-based INT128
+-- fallback code paths.
+--
+-- This file assumes the extension is already loaded (by the preceding
+-- int16.sql test).
+--
+--
+-- Constants for readability
+--
+-- INT64_MAX  = 9223372036854775807
+-- INT64_MIN  = -9223372036854775808
+-- 2^63       = 9223372036854775808
+-- 2^63+1     = 9223372036854775809
+-- 2^64       = 18446744073709551616
+-- 2^126      = 85070591730234615865843651857942052864
+-- INT16_MAX  = 170141183460469231731687303715884105727
+-- INT16_MIN  = -170141183460469231731687303715884105728
+-- =========================================================================
+-- Addition
+-- =========================================================================
+SELECT '9223372036854775808'::int16 + '9223372036854775808'::int16;  -- 2^63 + 2^63 = 2^64
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '-9223372036854775809'::int16 + '-9223372036854775809'::int16; -- -(2^63+1) + -(2^63+1)
+       ?column?        
+-----------------------
+ -18446744073709551618
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 + '-170141183460469231731687303715884105728'::int16; -- MAX + MIN
+ ?column? 
+----------
+ -1
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 + '1'::int16; -- overflow
+ERROR:  integer out of range
+SELECT '-170141183460469231731687303715884105728'::int16 + '-1'::int16; -- overflow
+ERROR:  integer out of range
+SELECT '18446744073709551616'::int16 + '0'::int16; -- 2^64 + 0
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '-18446744073709551616'::int16 + '18446744073709551616'::int16; -- -2^64 + 2^64
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '9223372036854775807'::int16 + '9223372036854775808'::int16; -- INT64_MAX + 2^63
+       ?column?       
+----------------------
+ 18446744073709551615
+(1 row)
+
+SELECT '-9223372036854775808'::int16 + '-9223372036854775808'::int16; -- INT64_MIN + INT64_MIN
+       ?column?        
+-----------------------
+ -18446744073709551616
+(1 row)
+
+SELECT '85070591730234615865843651857942052864'::int16 + '85070591730234615865843651857942052864'::int16; -- 2^126 + 2^126 (overflow)
+ERROR:  integer out of range
+-- =========================================================================
+-- Subtraction
+-- =========================================================================
+SELECT '-9223372036854775809'::int16 - '9223372036854775808'::int16; -- -(2^63+1) - 2^63
+       ?column?        
+-----------------------
+ -18446744073709551617
+(1 row)
+
+SELECT '9223372036854775808'::int16 - '-9223372036854775809'::int16; -- 2^63 - -(2^63+1)
+       ?column?       
+----------------------
+ 18446744073709551617
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 - '-1'::int16; -- MAX - (-1) = overflow
+ERROR:  integer out of range
+SELECT '-170141183460469231731687303715884105728'::int16 - '1'::int16; -- MIN - 1 = overflow
+ERROR:  integer out of range
+SELECT '18446744073709551616'::int16 - '1'::int16; -- 2^64 - 1
+       ?column?       
+----------------------
+ 18446744073709551615
+(1 row)
+
+SELECT '0'::int16 - '-18446744073709551616'::int16; -- 0 - (-2^64)
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 - '170141183460469231731687303715884105726'::int16; -- MAX - (MAX-1)
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '-170141183460469231731687303715884105727'::int16 - '-170141183460469231731687303715884105728'::int16; -- -(MIN+1) - MIN
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '9223372036854775808'::int16 - '9223372036854775807'::int16; -- 2^63 - INT64_MAX
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '-9223372036854775809'::int16 - '-9223372036854775808'::int16; -- -(2^63+1) - INT64_MIN
+ ?column? 
+----------
+ -1
+(1 row)
+
+-- =========================================================================
+-- Multiplication
+-- =========================================================================
+SELECT '3000000000'::int16 * '3000000000'::int16;  -- 9e18 > INT64_MAX
+      ?column?       
+---------------------
+ 9000000000000000000
+(1 row)
+
+SELECT '-3000000000'::int16 * '3000000000'::int16; -- -9e18
+       ?column?       
+----------------------
+ -9000000000000000000
+(1 row)
+
+SELECT '3000000000'::int16 * '-3000000000'::int16; -- -9e18
+       ?column?       
+----------------------
+ -9000000000000000000
+(1 row)
+
+SELECT '-3000000000'::int16 * '-3000000000'::int16; -- 9e18
+      ?column?       
+---------------------
+ 9000000000000000000
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 * '2'::int16; -- overflow
+ERROR:  integer out of range
+SELECT '18446744073709551616'::int16 * '18446744073709551616'::int16; -- 2^64 * 2^64 overflow
+ERROR:  integer out of range
+SELECT '4294967296'::int16 * '4294967296'::int16;  -- 2^32 * 2^32 = 2^64
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '-4294967296'::int16 * '4294967296'::int16; -- -2^64
+       ?column?        
+-----------------------
+ -18446744073709551616
+(1 row)
+
+SELECT '9223372036854775807'::int16 * '9223372036854775807'::int16; -- INT64_MAX^2
+                ?column?                
+----------------------------------------
+ 85070591730234615847396907784232501249
+(1 row)
+
+SELECT '0'::int16 * '18446744073709551616'::int16; -- 0 * 2^64
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '18446744073709551616'::int16 * '1'::int16; -- 2^64 * 1
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '18446744073709551615'::int16 * '2'::int16; -- (2^64-1) * 2
+       ?column?       
+----------------------
+ 36893488147419103230
+(1 row)
+
+-- =========================================================================
+-- Division
+-- =========================================================================
+SELECT '170141183460469231731687303715884105727'::int16 / '170141183460469231731687303715884105727'::int16; -- MAX / MAX = 1
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 / '-1'::int16; -- MAX / -1 = -MAX
+                 ?column?                 
+------------------------------------------
+ -170141183460469231731687303715884105727
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16 / '-1'::int16; -- MIN / -1 = overflow
+ERROR:  integer out of range
+SELECT '170141183460469231731687303715884105727'::int16 / '2'::int16; -- MAX / 2
+                ?column?                
+----------------------------------------
+ 85070591730234615865843651857942052863
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16 / '2'::int16; -- MIN / 2
+                ?column?                 
+-----------------------------------------
+ -85070591730234615865843651857942052864
+(1 row)
+
+SELECT '18446744073709551616'::int16 / '9223372036854775808'::int16; -- 2^64 / 2^63 = 2
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT '-18446744073709551616'::int16 / '9223372036854775808'::int16; -- -2^64 / 2^63 = -2
+ ?column? 
+----------
+ -2
+(1 row)
+
+SELECT '18446744073709551616'::int16 / '-9223372036854775808'::int16; -- 2^64 / -2^63 = -2
+ ?column? 
+----------
+ -2
+(1 row)
+
+SELECT '-18446744073709551616'::int16 / '-9223372036854775808'::int16; -- -2^64 / -2^63 = 2
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 / '9223372036854775807'::int16; -- MAX / INT64_MAX
+       ?column?       
+----------------------
+ 18446744073709551618
+(1 row)
+
+SELECT '1'::int16 / '0'::int16;  -- division by zero
+ERROR:  division by zero
+-- =========================================================================
+-- Remainder (modulo)
+-- =========================================================================
+SELECT '170141183460469231731687303715884105727'::int16 % '10'::int16;  -- MAX % 10
+ ?column? 
+----------
+ 7
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16 % '10'::int16; -- MIN % 10
+ ?column? 
+----------
+ -8
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 % '-10'::int16;  -- MAX % -10
+ ?column? 
+----------
+ 7
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16 % '-10'::int16; -- MIN % -10
+ ?column? 
+----------
+ -8
+(1 row)
+
+SELECT '18446744073709551616'::int16 % '9223372036854775808'::int16; -- 2^64 % 2^63 = 0
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '18446744073709551617'::int16 % '9223372036854775808'::int16; -- (2^64+1) % 2^63 = 1
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '-18446744073709551617'::int16 % '9223372036854775808'::int16; -- -(2^64+1) % 2^63 = -1
+ ?column? 
+----------
+ -1
+(1 row)
+
+SELECT '18446744073709551617'::int16 % '-9223372036854775808'::int16; -- (2^64+1) % -2^63 = 1
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '-18446744073709551617'::int16 % '-9223372036854775808'::int16; -- -(2^64+1) % -2^63 = -1
+ ?column? 
+----------
+ -1
+(1 row)
+
+SELECT '1'::int16 % '0'::int16;  -- division by zero
+ERROR:  division by zero
+-- =========================================================================
+-- Unary minus
+-- =========================================================================
+SELECT -'170141183460469231731687303715884105727'::int16;  -- -MAX
+                 ?column?                 
+------------------------------------------
+ -170141183460469231731687303715884105727
+(1 row)
+
+SELECT -'-170141183460469231731687303715884105728'::int16; -- -MIN = overflow
+ERROR:  integer out of range
+SELECT -'9223372036854775808'::int16;  -- -2^63
+       ?column?       
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT -'-9223372036854775808'::int16; -- 2^63
+      ?column?       
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT -'18446744073709551616'::int16; -- -2^64
+       ?column?        
+-----------------------
+ -18446744073709551616
+(1 row)
+
+-- =========================================================================
+-- Bitwise AND
+-- =========================================================================
+SELECT '18446744073709551616'::int16 & '18446744073709551616'::int16; -- 2^64 & 2^64
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '18446744073709551616'::int16 & '1'::int16; -- 2^64 & 1 = 0
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '18446744073709551617'::int16 & '1'::int16; -- 2^64+1 & 1 = 1
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '-1'::int16 & '18446744073709551616'::int16; -- -1 & 2^64 = 2^64
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '-1'::int16 & '0'::int16; -- -1 & 0 = 0
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 & '0'::int16; -- MAX & 0
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16 & '0'::int16; -- MIN & 0
+ ?column? 
+----------
+ 0
+(1 row)
+
+-- =========================================================================
+-- Bitwise OR
+-- =========================================================================
+SELECT '18446744073709551616'::int16 | '1'::int16; -- 2^64 | 1 = 2^64+1
+       ?column?       
+----------------------
+ 18446744073709551617
+(1 row)
+
+SELECT '18446744073709551616'::int16 | '0'::int16; -- 2^64 | 0 = 2^64
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '0'::int16 | '18446744073709551616'::int16; -- 0 | 2^64 = 2^64
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '-1'::int16 | '0'::int16; -- -1 | 0 = -1
+ ?column? 
+----------
+ -1
+(1 row)
+
+SELECT '18446744073709551615'::int16 | '1'::int16; -- (2^64-1) | 1 = 2^64-1
+       ?column?       
+----------------------
+ 18446744073709551615
+(1 row)
+
+-- =========================================================================
+-- Bitwise XOR
+-- =========================================================================
+SELECT '18446744073709551616'::int16 # '1'::int16; -- 2^64 # 1 = 2^64+1
+       ?column?       
+----------------------
+ 18446744073709551617
+(1 row)
+
+SELECT '18446744073709551616'::int16 # '18446744073709551616'::int16; -- 2^64 # 2^64 = 0
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT '0'::int16 # '18446744073709551616'::int16; -- 0 # 2^64 = 2^64
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '-1'::int16 # '0'::int16; -- -1 # 0 = -1
+ ?column? 
+----------
+ -1
+(1 row)
+
+SELECT '-1'::int16 # '18446744073709551616'::int16; -- -1 # 2^64 = -(2^64+1)
+       ?column?        
+-----------------------
+ -18446744073709551617
+(1 row)
+
+SELECT '-1'::int16 # '-1'::int16; -- -1 # -1 = 0
+ ?column? 
+----------
+ 0
+(1 row)
+
+-- =========================================================================
+-- Bitwise NOT
+-- =========================================================================
+SELECT ~'0'::int16; -- ~0 = -1
+ ?column? 
+----------
+ -1
+(1 row)
+
+SELECT ~'-1'::int16; -- ~(-1) = 0
+ ?column? 
+----------
+ 0
+(1 row)
+
+SELECT ~'18446744073709551616'::int16; -- ~(2^64)
+       ?column?        
+-----------------------
+ -18446744073709551617
+(1 row)
+
+SELECT ~'170141183460469231731687303715884105727'::int16; -- ~MAX = MIN
+                 ?column?                 
+------------------------------------------
+ -170141183460469231731687303715884105728
+(1 row)
+
+SELECT ~'-170141183460469231731687303715884105728'::int16; -- ~MIN = MAX
+                ?column?                 
+-----------------------------------------
+ 170141183460469231731687303715884105727
+(1 row)
+
+-- =========================================================================
+-- Left shift (shifts 0-127 only; behavior matches int8: raw C shift)
+-- =========================================================================
+SELECT '1'::int16 << 0;   -- 1
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '1'::int16 << 1;   -- 2
+ ?column? 
+----------
+ 2
+(1 row)
+
+SELECT '1'::int16 << 64;  -- 2^64
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '1'::int16 << 127; -- 2^127 = INT16_MIN
+                 ?column?                 
+------------------------------------------
+ -170141183460469231731687303715884105728
+(1 row)
+
+SELECT '18446744073709551616'::int16 << 1;  -- 2^64 << 1 = 2^65
+       ?column?       
+----------------------
+ 36893488147419103232
+(1 row)
+
+SELECT '18446744073709551616'::int16 << 63; -- 2^64 << 63 = 2^127 = INT16_MIN
+                 ?column?                 
+------------------------------------------
+ -170141183460469231731687303715884105728
+(1 row)
+
+SELECT '-1'::int16 << 1;  -- -1 << 1 = -2
+ ?column? 
+----------
+ -2
+(1 row)
+
+SELECT '-1'::int16 << 0;  -- -1 << 0 = -1
+ ?column? 
+----------
+ -1
+(1 row)
+
+-- =========================================================================
+-- Right shift (shifts 0-127 only; arithmetic shift for negative values)
+-- =========================================================================
+SELECT '-1'::int16 >> 1;  -- -1 >> 1 = -1 (arithmetic)
+ ?column? 
+----------
+ -1
+(1 row)
+
+SELECT '-1'::int16 >> 64; -- -1 >> 64 = -1 (arithmetic)
+ ?column? 
+----------
+ -1
+(1 row)
+
+SELECT '18446744073709551616'::int16 >> 1;  -- 2^64 >> 1 = 2^63
+      ?column?       
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '18446744073709551616'::int16 >> 64; -- 2^64 >> 64 = 1
+ ?column? 
+----------
+ 1
+(1 row)
+
+SELECT '18446744073709551616'::int16 >> 0;  -- 2^64 >> 0 = 2^64
+       ?column?       
+----------------------
+ 18446744073709551616
+(1 row)
+
+SELECT '170141183460469231731687303715884105727'::int16 >> 1; -- MAX >> 1
+                ?column?                
+----------------------------------------
+ 85070591730234615865843651857942052863
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16 >> 1; -- MIN >> 1
+                ?column?                 
+-----------------------------------------
+ -85070591730234615865843651857942052864
+(1 row)
+
+SELECT '-170141183460469231731687303715884105728'::int16 >> 64; -- MIN >> 64
+       ?column?       
+----------------------
+ -9223372036854775808
+(1 row)
+
+SELECT '0'::int16 >> 10; -- 0 >> 10 = 0
+ ?column? 
+----------
+ 0
+(1 row)
+
+-- =========================================================================
+-- Cross-type arithmetic (minimal: one test per operator per type)
+-- =========================================================================
+-- int16 op int8
+SELECT '18446744073709551616'::int16 + '1'::int8;
+       ?column?       
+----------------------
+ 18446744073709551617
+(1 row)
+
+SELECT '1'::int8 + '18446744073709551616'::int16;
+       ?column?       
+----------------------
+ 18446744073709551617
+(1 row)
+
+SELECT '18446744073709551616'::int16 - '1'::int8;
+       ?column?       
+----------------------
+ 18446744073709551615
+(1 row)
+
+SELECT '1'::int8 - '18446744073709551616'::int16;
+       ?column?        
+-----------------------
+ -18446744073709551615
+(1 row)
+
+SELECT '18446744073709551616'::int16 * '2'::int8;
+       ?column?       
+----------------------
+ 36893488147419103232
+(1 row)
+
+SELECT '2'::int8 * '18446744073709551616'::int16;
+       ?column?       
+----------------------
+ 36893488147419103232
+(1 row)
+
+SELECT '18446744073709551616'::int16 / '2'::int8;
+      ?column?       
+---------------------
+ 9223372036854775808
+(1 row)
+
+SELECT '2'::int8 / '18446744073709551616'::int16;
+ ?column? 
+----------
+ 0
+(1 row)
+
+-- int16 op int4
+SELECT '18446744073709551616'::int16 + 1;
+       ?column?       
+----------------------
+ 18446744073709551617
+(1 row)
+
+SELECT 1 + '18446744073709551616'::int16;
+       ?column?       
+----------------------
+ 18446744073709551617
+(1 row)
+
+SELECT '18446744073709551616'::int16 - 1;
+       ?column?       
+----------------------
+ 18446744073709551615
+(1 row)
+
+SELECT 1 - '18446744073709551616'::int16;
+       ?column?        
+-----------------------
+ -18446744073709551615
+(1 row)
+
+SELECT '18446744073709551616'::int16 * 2;
+       ?column?       
+----------------------
+ 36893488147419103232
+(1 row)
+
+SELECT 2 * '18446744073709551616'::int16;
+       ?column?       
+----------------------
+ 36893488147419103232
+(1 row)
+
+SELECT '18446744073709551616'::int16 / 2;
+      ?column?       
+---------------------
+ 9223372036854775808
+(1 row)
+
+-- int16 op int2
+SELECT '18446744073709551616'::int16 + 1::int2;
+       ?column?       
+----------------------
+ 18446744073709551617
+(1 row)
+
+SELECT 1::int2 + '18446744073709551616'::int16;
+       ?column?       
+----------------------
+ 18446744073709551617
+(1 row)
+
+SELECT '18446744073709551616'::int16 - 1::int2;
+       ?column?       
+----------------------
+ 18446744073709551615
+(1 row)
+
+SELECT '18446744073709551616'::int16 * 2::int2;
+       ?column?       
+----------------------
+ 36893488147419103232
+(1 row)
+
+SELECT '18446744073709551616'::int16 / 2::int2;
+      ?column?       
+---------------------
+ 9223372036854775808
+(1 row)
+
+-- =========================================================================
+-- Aggregates with values exceeding int64 range
+-- =========================================================================
+CREATE TABLE int16_overflow_test (x int16);
+INSERT INTO int16_overflow_test VALUES ('9223372036854775808'), ('9223372036854775808'); -- two 2^63 values
+-- sum = 2^64 (exceeds int64, fits in int16)
+SELECT sum(x) FROM int16_overflow_test;
+         sum          
+----------------------
+ 18446744073709551616
+(1 row)
+
+-- avg = 2^63 (returns numeric)
+SELECT avg(x) FROM int16_overflow_test;
+         avg         
+---------------------
+ 9223372036854775808
+(1 row)
+
+-- min/max
+SELECT min(x), max(x) FROM int16_overflow_test;
+         min         |         max         
+---------------------+---------------------
+ 9223372036854775808 | 9223372036854775808
+(1 row)
+
+-- Sum overflow: add MAX then add 2^63
+INSERT INTO int16_overflow_test VALUES ('170141183460469231731687303715884105727');
+SELECT sum(x) FROM int16_overflow_test; -- overflow
+ERROR:  integer out of range
+-- Clean up the overflow row
+DELETE FROM int16_overflow_test WHERE x = '170141183460469231731687303715884105727'::int16;
+-- avg with 3 values all = 2^64
+DELETE FROM int16_overflow_test;
+INSERT INTO int16_overflow_test VALUES ('18446744073709551616'), ('18446744073709551616'), ('18446744073709551616');
+SELECT sum(x) FROM int16_overflow_test; -- 3 * 2^64
+         sum          
+----------------------
+ 55340232221128654848
+(1 row)
+
+SELECT avg(x) FROM int16_overflow_test; -- 2^64 as numeric
+         avg          
+----------------------
+ 18446744073709551616
+(1 row)
+
+DROP TABLE int16_overflow_test;
+-- =========================================================================
+-- int16_avg_state type (internal aggregate state for avg)
+-- =========================================================================
+-- The state type is internal-only (cannot accept direct input), so we
+-- verify it works correctly through the avg() aggregate with large values
+-- that exercise the INT128 sum accumulator.
+CREATE TABLE int16_avg_state_test (x int16);
+INSERT INTO int16_avg_state_test VALUES
+    ('9223372036854775808'),   -- 2^63
+    ('9223372036854775808'),   -- 2^63
+    ('9223372036854775808'),   -- 2^63
+    ('9223372036854775808'),   -- 2^63
+    (NULL);                     -- should be skipped
+-- avg = 2^63 (sum = 4 * 2^63 = 2^65, count = 4, avg = 2^63)
+SELECT avg(x) FROM int16_avg_state_test;
+         avg         
+---------------------
+ 9223372036854775808
+(1 row)
+
+-- Larger values: sum exceeds int64 range significantly
+DELETE FROM int16_avg_state_test;
+INSERT INTO int16_avg_state_test VALUES
+    ('18446744073709551616'),   -- 2^64
+    ('18446744073709551616'),   -- 2^64
+    ('18446744073709551616');   -- 2^64
+-- avg = 2^64
+SELECT avg(x) FROM int16_avg_state_test;
+         avg          
+----------------------
+ 18446744073709551616
+(1 row)
+
+DROP TABLE int16_avg_state_test;
diff --git a/contrib/int16/gen_expected_values.c b/contrib/int16/gen_expected_values.c
new file mode 100644
index 00000000000..e35260f3710
--- /dev/null
+++ b/contrib/int16/gen_expected_values.c
@@ -0,0 +1,398 @@
+/*
+ * gen_expected_values.c
+ *
+ * Standalone program to compute expected values for int16_overflow regression
+ * tests using native __int128 arithmetic.  This is NOT compiled as part of the
+ * extension — it exists solely as documentation for how the expected output
+ * was generated.
+ *
+ * Build (for reference, not needed for the extension):
+ *   gcc -o gen_expected_values gen_expected_values.c
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+typedef __int128 int128_t;
+typedef unsigned __int128 uint128_t;
+
+/* INT128_MIN in two's complement = (uint128_t)1 << 127, reinterpreted as signed */
+#define INT16_MAX_VAL  ((int128_t)(((uint128_t)1 << 127) - 1))
+#define INT16_MIN_VAL  ((int128_t)((uint128_t)1 << 127))
+#define TWO_POW_63     ((int128_t)1 << 63)
+#define TWO_POW_64     ((int128_t)1 << 64)
+
+static void
+print_int128(int128_t v)
+{
+	if (v == 0)
+	{
+		printf("0");
+		return;
+	}
+	if (v == INT16_MIN_VAL)
+	{
+		/* Can't negate INT128_MIN; print the constant directly */
+		printf("-170141183460469231731687303715884105728");
+		return;
+	}
+	if (v < 0)
+	{
+		printf("-");
+		v = -v;
+	}
+	char		buf[40];
+	int			i = 0;
+
+	while (v > 0)
+	{
+		buf[i++] = '0' + (int) (v % 10);
+		v /= 10;
+	}
+	while (i > 0)
+		printf("%c", buf[--i]);
+}
+
+static int128_t
+parse_int128(const char *str)
+{
+	int128_t	v = 0;
+	int			neg = 0;
+	const char *p = str;
+
+	if (*p == '-')
+	{
+		neg = 1;
+		p++;
+	}
+	while (*p)
+		v = v * 10 + (*p++ - '0');
+	if (neg)
+		v = -v;
+	return v;
+}
+
+static void
+print_add(const char *a_str, const char *b_str)
+{
+	int128_t	a = parse_int128(a_str);
+	int128_t	b = parse_int128(b_str);
+	int128_t	r;
+
+	printf("ADD %s + %s = ", a_str, b_str);
+	if (__builtin_add_overflow(a, b, &r))
+		printf("OVERFLOW (integer out of range)");
+	else
+		print_int128(r);
+	printf("\n");
+}
+
+static void
+print_sub(const char *a_str, const char *b_str)
+{
+	int128_t	a = parse_int128(a_str);
+	int128_t	b = parse_int128(b_str);
+	int128_t	r;
+
+	printf("SUB %s - %s = ", a_str, b_str);
+	if (__builtin_sub_overflow(a, b, &r))
+		printf("OVERFLOW (integer out of range)");
+	else
+		print_int128(r);
+	printf("\n");
+}
+
+static void
+print_mul(const char *a_str, const char *b_str)
+{
+	int128_t	a = parse_int128(a_str);
+	int128_t	b = parse_int128(b_str);
+
+	printf("MUL %s * %s = ", a_str, b_str);
+	if (a == 0 || b == 0)
+	{
+		printf("0");
+	}
+	else
+	{
+		int128_t	r;
+
+		if (__builtin_mul_overflow(a, b, &r))
+			printf("OVERFLOW (integer out of range)");
+		else
+			print_int128(r);
+	}
+	printf("\n");
+}
+
+static void
+print_div(const char *a_str, const char *b_str)
+{
+	int128_t	a = parse_int128(a_str);
+	int128_t	b = parse_int128(b_str);
+
+	printf("DIV %s / %s = ", a_str, b_str);
+	if (b == 0)
+		printf("OVERFLOW (division by zero)");
+	else if (a == INT16_MIN_VAL && b == -1)
+		printf("OVERFLOW (integer out of range)");
+	else
+		print_int128(a / b);
+	printf("\n");
+}
+
+static void
+print_mod(const char *a_str, const char *b_str)
+{
+	int128_t	a = parse_int128(a_str);
+	int128_t	b = parse_int128(b_str);
+
+	printf("MOD %s %% %s = ", a_str, b_str);
+	if (b == 0)
+		printf("OVERFLOW (division by zero)");
+	else if (a == INT16_MIN_VAL && b == -1)
+		printf("OVERFLOW (integer out of range)");
+	else
+		print_int128(a % b);	/* C % follows dividend sign */
+	printf("\n");
+}
+
+static void
+print_neg(const char *a_str)
+{
+	int128_t	a = parse_int128(a_str);
+
+	printf("NEG -(%s) = ", a_str);
+	if (a == INT16_MIN_VAL)
+		printf("OVERFLOW (integer out of range)");
+	else
+		print_int128(-a);
+	printf("\n");
+}
+
+static void
+print_bitand(const char *a_str, const char *b_str)
+{
+	int128_t	a = parse_int128(a_str);
+	int128_t	b = parse_int128(b_str);
+
+	printf("AND %s & %s = ", a_str, b_str);
+	print_int128(a & b);
+	printf("\n");
+}
+
+static void
+print_bitor(const char *a_str, const char *b_str)
+{
+	int128_t	a = parse_int128(a_str);
+	int128_t	b = parse_int128(b_str);
+
+	printf("OR %s | %s = ", a_str, b_str);
+	print_int128(a | b);
+	printf("\n");
+}
+
+static void
+print_bitxor(const char *a_str, const char *b_str)
+{
+	int128_t	a = parse_int128(a_str);
+	int128_t	b = parse_int128(b_str);
+
+	printf("XOR %s # %s = ", a_str, b_str);
+	print_int128(a ^ b);
+	printf("\n");
+}
+
+static void
+print_bitnot(const char *a_str)
+{
+	int128_t	a = parse_int128(a_str);
+
+	printf("NOT ~(%s) = ", a_str);
+	print_int128(~a);
+	printf("\n");
+}
+
+static void
+print_shl(const char *a_str, int shift)
+{
+	int128_t	a = parse_int128(a_str);
+
+	printf("SHL %s << %d = ", a_str, shift);
+	print_int128(a << shift);
+	printf("\n");
+}
+
+static void
+print_shr(const char *a_str, int shift)
+{
+	int128_t	a = parse_int128(a_str);
+
+	printf("SHR %s >> %d = ", a_str, shift);
+	print_int128(a >> shift);	/* arithmetic shift */
+	printf("\n");
+}
+
+int
+main(void)
+{
+	printf("=== Addition ===\n");
+	print_add("9223372036854775808", "9223372036854775808");	/* 2^63 + 2^63 = 2^64 */
+	print_add("-9223372036854775809", "-9223372036854775809");	/* -2^63-1 + -2^63-1 */
+	print_add("170141183460469231731687303715884105727", "-170141183460469231731687303715884105728");	/* MAX + MIN */
+	print_add("170141183460469231731687303715884105727", "1");	/* overflow */
+	print_add("-170141183460469231731687303715884105728", "-1");	/* overflow */
+	print_add("18446744073709551616", "0"); /* 2^64 + 0 */
+	print_add("-18446744073709551616", "18446744073709551616"); /* -2^64 + 2^64 */
+	print_add("9223372036854775807", "9223372036854775808");	/* INT64_MAX + 2^63 */
+	print_add("-9223372036854775808", "-9223372036854775808");	/* INT64_MIN + INT64_MIN */
+	print_add("85070591730234615865843651857942052864", "85070591730234615865843651857942052864");	/* 2^126 + 2^126 */
+
+	printf("\n=== Subtraction ===\n");
+	print_sub("-9223372036854775809", "9223372036854775808");	/* -2^63-1 - 2^63 */
+	print_sub("9223372036854775808", "-9223372036854775809");	/* 2^63 - (-2^63-1) */
+	print_sub("170141183460469231731687303715884105727", "-1"); /* MAX - (-1) = overflow */
+	print_sub("-170141183460469231731687303715884105728", "1"); /* MIN - 1 = overflow */
+	print_sub("18446744073709551616", "1"); /* 2^64 - 1 */
+	print_sub("0", "-18446744073709551616");	/* 0 - (-2^64) */
+	print_sub("170141183460469231731687303715884105727", "170141183460469231731687303715884105726");	/* MAX - (MAX-1) */
+	print_sub("-170141183460469231731687303715884105727", "-170141183460469231731687303715884105728");	/* -(MIN+1) - MIN */
+	print_sub("9223372036854775808", "9223372036854775807");	/* 2^63 - INT64_MAX */
+	print_sub("-9223372036854775809", "-9223372036854775808");	/* -(2^63+1) - INT64_MIN */
+
+	printf("\n=== Multiplication ===\n");
+	print_mul("3000000000", "3000000000");	/* 9e18 > INT64_MAX */
+	print_mul("-3000000000", "3000000000"); /* -9e18 */
+	print_mul("3000000000", "-3000000000"); /* -9e18 */
+	print_mul("-3000000000", "-3000000000");	/* 9e18 */
+	print_mul("170141183460469231731687303715884105727", "2");	/* overflow */
+	print_mul("18446744073709551616", "18446744073709551616");	/* 2^64 * 2^64 = 2^128
+																 * overflow */
+	print_mul("4294967296", "4294967296");	/* 2^32 * 2^32 = 2^64 */
+	print_mul("-4294967296", "4294967296"); /* -2^64 */
+	print_mul("9223372036854775807", "9223372036854775807");	/* INT64_MAX^2 */
+	print_mul("0", "18446744073709551616"); /* 0 * 2^64 */
+	print_mul("18446744073709551616", "1"); /* 2^64 * 1 */
+	print_mul("18446744073709551615", "2"); /* (2^64-1) * 2 */
+
+	printf("\n=== Division ===\n");
+	print_div("170141183460469231731687303715884105727", "170141183460469231731687303715884105727");	/* MAX / MAX = 1 */
+	print_div("170141183460469231731687303715884105727", "-1"); /* MAX / -1 = -MAX */
+	print_div("-170141183460469231731687303715884105728", "-1");	/* MIN / -1 = overflow */
+	print_div("170141183460469231731687303715884105727", "2");	/* MAX / 2 */
+	print_div("-170141183460469231731687303715884105728", "2"); /* MIN / 2 */
+	print_div("18446744073709551616", "9223372036854775808");	/* 2^64 / 2^63 = 2 */
+	print_div("-18446744073709551616", "9223372036854775808");	/* -2^64 / 2^63 = -2 */
+	print_div("18446744073709551616", "-9223372036854775808");	/* 2^64 / -2^63 = -2 */
+	print_div("-18446744073709551616", "-9223372036854775808"); /* -2^64 / -2^63 = 2 */
+	print_div("170141183460469231731687303715884105727", "9223372036854775807");	/* MAX / INT64_MAX */
+	print_div("1", "0");		/* division by zero */
+
+	printf("\n=== Remainder ===\n");
+	print_mod("170141183460469231731687303715884105727", "10"); /* MAX % 10 */
+	print_mod("-170141183460469231731687303715884105728", "10");	/* MIN % 10 */
+	print_mod("170141183460469231731687303715884105727", "-10");	/* MAX % -10 */
+	print_mod("-170141183460469231731687303715884105728", "-10");	/* MIN % -10 */
+	print_mod("18446744073709551616", "9223372036854775808");	/* 2^64 % 2^63 = 0 */
+	print_mod("18446744073709551617", "9223372036854775808");	/* 2^64+1 % 2^63 = 1 */
+	print_mod("-18446744073709551617", "9223372036854775808");	/* -(2^64+1) % 2^63 = -1 */
+	print_mod("18446744073709551617", "-9223372036854775808");	/* 2^64+1 % -2^63 = 1 */
+	print_mod("-18446744073709551617", "-9223372036854775808"); /* -(2^64+1) % -2^63 =
+																 * -1 */
+	print_mod("1", "0");		/* division by zero */
+
+	printf("\n=== Unary Minus ===\n");
+	print_neg("170141183460469231731687303715884105727");	/* -MAX */
+	print_neg("-170141183460469231731687303715884105728");	/* -MIN = overflow */
+	print_neg("9223372036854775808");	/* -2^63 */
+	print_neg("-9223372036854775808");	/* 2^63 */
+	print_neg("18446744073709551616");	/* -2^64 */
+
+	printf("\n=== Bitwise AND ===\n");
+	print_bitand("18446744073709551616", "18446744073709551616");	/* 2^64 & 2^64 */
+	print_bitand("18446744073709551616", "1");	/* 2^64 & 1 = 0 */
+	print_bitand("18446744073709551617", "1");	/* 2^64+1 & 1 = 1 */
+	print_bitand("-1", "18446744073709551616"); /* -1 & 2^64 = 2^64 */
+	print_bitand("-1", "0");	/* -1 & 0 = 0 */
+	print_bitand("170141183460469231731687303715884105727", "0");	/* MAX & 0 */
+	print_bitand("-170141183460469231731687303715884105728", "0");	/* MIN & 0 */
+
+	printf("\n=== Bitwise OR ===\n");
+	print_bitor("18446744073709551616", "1");	/* 2^64 | 1 = 2^64+1 */
+	print_bitor("18446744073709551616", "0");	/* 2^64 | 0 = 2^64 */
+	print_bitor("0", "18446744073709551616");	/* 0 | 2^64 = 2^64 */
+	print_bitor("-1", "0");		/* -1 | 0 = -1 */
+	print_bitor("18446744073709551615", "1");	/* (2^64-1) | 1 = 2^64-1 */
+
+	printf("\n=== Bitwise XOR ===\n");
+	print_bitxor("18446744073709551616", "1");	/* 2^64 # 1 = 2^64+1 */
+	print_bitxor("18446744073709551616", "18446744073709551616");	/* 2^64 # 2^64 = 0 */
+	print_bitxor("0", "18446744073709551616");	/* 0 # 2^64 = 2^64 */
+	print_bitxor("-1", "0");	/* -1 # 0 = -1 */
+	print_bitxor("-1", "18446744073709551616"); /* -1 # 2^64 = -(2^64+1) */
+	print_bitxor("-1", "-1");	/* -1 # -1 = 0 */
+
+	printf("\n=== Bitwise NOT ===\n");
+	print_bitnot("0");			/* ~0 = -1 */
+	print_bitnot("-1");			/* ~(-1) = 0 */
+	print_bitnot("18446744073709551616");	/* ~(2^64) */
+	print_bitnot("170141183460469231731687303715884105727");	/* ~MAX = MIN */
+	print_bitnot("-170141183460469231731687303715884105728");	/* ~MIN = MAX */
+
+	printf("\n=== Left Shift ===\n");
+	print_shl("1", 0);			/* 1 */
+	print_shl("1", 1);			/* 2 */
+	print_shl("1", 64);			/* 2^64 */
+	print_shl("1", 127);		/* 2^127 = INT16_MIN */
+	print_shl("18446744073709551616", 1);	/* 2^64 << 1 = 2^65 */
+	print_shl("18446744073709551616", 63);	/* 2^64 << 63 = 2^127 = INT16_MIN */
+	print_shl("-1", 1);			/* -1 << 1 = -2 */
+	print_shl("-1", 0);			/* -1 << 0 = -1 */
+
+	printf("\n=== Right Shift ===\n");
+	print_shr("-1", 1);			/* -1 >> 1 = -1 (arithmetic) */
+	print_shr("-1", 64);		/* -1 >> 64 = -1 (arithmetic) */
+	print_shr("18446744073709551616", 1);	/* 2^64 >> 1 = 2^63 */
+	print_shr("18446744073709551616", 64);	/* 2^64 >> 64 = 1 */
+	print_shr("18446744073709551616", 0);	/* 2^64 >> 0 = 2^64 */
+	print_shr("170141183460469231731687303715884105727", 1);	/* MAX >> 1 */
+	print_shr("-170141183460469231731687303715884105728", 1);	/* MIN >> 1 */
+	print_shr("-170141183460469231731687303715884105728", 64);	/* MIN >> 64 */
+	print_shr("0", 10);			/* 0 >> 10 = 0 */
+
+	printf("\n=== Aggregates ===\n");
+	/* sum of two 2^63 values = 2^64 */
+	int128_t	sum = 0;
+
+	sum += TWO_POW_63;
+	sum += TWO_POW_63;
+	printf("SUM(2^63, 2^63) = ");
+	print_int128(sum);
+	printf("\n");
+
+	/* sum with overflow: MAX + 2^63 */
+	sum = INT16_MAX_VAL;
+	printf("SUM(MAX, 2^63) = ");
+	if (__builtin_add_overflow(sum, TWO_POW_63, &sum))
+		printf("OVERFLOW (integer out of range)");
+	else
+		print_int128(sum);
+	printf("\n");
+
+	/* avg of (2^63, 2^63) = 2^63 */
+	sum = TWO_POW_63 + TWO_POW_63;
+	printf("AVG(2^63, 2^63) = ");
+	print_int128(sum / 2);
+	printf("\n");
+
+	/* avg of (2^64, 2^64, 2^64) = 2^64 */
+	sum = TWO_POW_64 + TWO_POW_64 + TWO_POW_64;
+	printf("AVG(2^64, 2^64, 2^64) = ");
+	print_int128(sum / 3);
+	printf("\n");
+
+	return 0;
+}
diff --git a/contrib/int16/int16--1.0.sql b/contrib/int16/int16--1.0.sql
new file mode 100644
index 00000000000..6171a4bee2c
--- /dev/null
+++ b/contrib/int16/int16--1.0.sql
@@ -0,0 +1,1253 @@
+/* contrib/int16/int16--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION int16" to load this file. \quit
+
+--
+-- 128-bit signed integer data type.
+--
+
+-- Shell type
+CREATE TYPE int16;
+
+--
+-- Input/output functions
+--
+CREATE FUNCTION int16in(cstring)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16out(int16)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16recv(internal)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16send(int16)
+RETURNS bytea
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- The type itself
+--
+CREATE TYPE int16 (
+    INPUT          = int16in,
+    OUTPUT         = int16out,
+    RECEIVE        = int16recv,
+    SEND           = int16send,
+    INTERNALLENGTH = 16,
+    ALIGNMENT      = double,
+    PASSEDBYVALUE  = false,
+    CATEGORY       = 'N'
+);
+
+--
+-- Arithmetic functions (int16 vs int16)
+--
+CREATE FUNCTION int16pl(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16mi(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16mul(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16div(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16mod(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16um(int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16up(int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16abs(int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- Comparison functions (int16 vs int16)
+--
+CREATE FUNCTION int16eq(int16, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16ne(int16, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16lt(int16, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16gt(int16, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16le(int16, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16ge(int16, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16_cmp(int16, int16)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- Operators (int16 vs int16)
+--
+CREATE OPERATOR + (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16pl,
+    COMMUTATOR = +
+);
+
+CREATE OPERATOR - (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16mi
+);
+
+CREATE OPERATOR * (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16mul,
+    COMMUTATOR = *
+);
+
+CREATE OPERATOR / (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16div
+);
+
+CREATE OPERATOR % (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16mod
+);
+
+CREATE OPERATOR - (
+    RIGHTARG = int16,
+    PROCEDURE = int16um
+);
+
+CREATE OPERATOR + (
+    RIGHTARG = int16,
+    PROCEDURE = int16up
+);
+
+CREATE OPERATOR @ (
+    RIGHTARG = int16,
+    PROCEDURE = int16abs
+);
+
+CREATE OPERATOR = (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16eq,
+    COMMUTATOR = =, NEGATOR = <>,
+    RESTRICT = eqsel, JOIN = eqjoinsel,
+    HASHES, MERGES
+);
+
+CREATE OPERATOR <> (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16ne,
+    COMMUTATOR = <>, NEGATOR = =,
+    RESTRICT = neqsel, JOIN = neqjoinsel
+);
+
+CREATE OPERATOR < (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16lt,
+    COMMUTATOR = >, NEGATOR = >=,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR > (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16gt,
+    COMMUTATOR = <, NEGATOR = <=,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR <= (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16le,
+    COMMUTATOR = >=, NEGATOR = >,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR >= (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16ge,
+    COMMUTATOR = <=, NEGATOR = <,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+--
+-- Bitwise functions and operators
+--
+CREATE FUNCTION int16and(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16or(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16xor(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16not(int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16shl(int16, int4)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16shr(int16, int4)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR & (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16and,
+    COMMUTATOR = &
+);
+
+CREATE OPERATOR | (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16or,
+    COMMUTATOR = |
+);
+
+CREATE OPERATOR # (
+    LEFTARG = int16, RIGHTARG = int16,
+    PROCEDURE = int16xor,
+    COMMUTATOR = #
+);
+
+CREATE OPERATOR ~ (
+    RIGHTARG = int16,
+    PROCEDURE = int16not
+);
+
+CREATE OPERATOR << (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int16shl
+);
+
+CREATE OPERATOR >> (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int16shr
+);
+
+--
+-- Conversion functions (for casts)
+--
+CREATE FUNCTION int16(int8)
+RETURNS int16
+AS 'MODULE_PATHNAME', 'int16_from_int8'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int8(int16)
+RETURNS int8
+AS 'MODULE_PATHNAME', 'int16_to_int8'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16(int4)
+RETURNS int16
+AS 'MODULE_PATHNAME', 'int16_from_int4'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int4(int16)
+RETURNS int4
+AS 'MODULE_PATHNAME', 'int16_to_int4'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16(int2)
+RETURNS int16
+AS 'MODULE_PATHNAME', 'int16_from_int2'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int2(int16)
+RETURNS int2
+AS 'MODULE_PATHNAME', 'int16_to_int2'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- Casts
+--
+CREATE CAST (int8 AS int16) WITH FUNCTION int16(int8) AS IMPLICIT;
+CREATE CAST (int16 AS int8) WITH FUNCTION int8(int16) AS ASSIGNMENT;
+CREATE CAST (int4 AS int16) WITH FUNCTION int16(int4) AS IMPLICIT;
+CREATE CAST (int16 AS int4) WITH FUNCTION int4(int16) AS ASSIGNMENT;
+CREATE CAST (int2 AS int16) WITH FUNCTION int16(int2) AS IMPLICIT;
+CREATE CAST (int16 AS int2) WITH FUNCTION int2(int16) AS ASSIGNMENT;
+
+-- int16 ↔ numeric casts (matching int8↔numeric policy: widening=implicit, narrowing=assignment)
+CREATE FUNCTION int16_numeric(int16)
+RETURNS numeric
+AS 'MODULE_PATHNAME', 'int16_numeric'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION numeric_int16(numeric)
+RETURNS int16
+AS 'MODULE_PATHNAME', 'numeric_int16'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE CAST (int16 AS numeric) WITH FUNCTION int16_numeric(int16) AS IMPLICIT;
+CREATE CAST (numeric AS int16) WITH FUNCTION numeric_int16(numeric) AS ASSIGNMENT;
+
+--
+-- Cross-type arithmetic: int16 op int8
+--
+CREATE FUNCTION int168pl(int16, int8)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int168mi(int16, int8)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int168mul(int16, int8)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int168div(int16, int8)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR + (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168pl
+);
+
+CREATE OPERATOR - (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168mi
+);
+
+CREATE OPERATOR * (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168mul
+);
+
+CREATE OPERATOR / (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168div
+);
+
+--
+-- Cross-type arithmetic: int8 op int16
+--
+CREATE FUNCTION int816pl(int8, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int816mi(int8, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int816mul(int8, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int816div(int8, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR + (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816pl
+);
+
+CREATE OPERATOR - (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816mi
+);
+
+CREATE OPERATOR * (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816mul
+);
+
+CREATE OPERATOR / (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816div
+);
+
+--
+-- Cross-type arithmetic: int16 op int4
+--
+CREATE FUNCTION int164pl(int16, int4)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int164mi(int16, int4)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int164mul(int16, int4)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int164div(int16, int4)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR + (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164pl
+);
+
+CREATE OPERATOR - (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164mi
+);
+
+CREATE OPERATOR * (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164mul
+);
+
+CREATE OPERATOR / (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164div
+);
+
+--
+-- Cross-type arithmetic: int4 op int16
+--
+CREATE FUNCTION int416pl(int4, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int416mi(int4, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int416mul(int4, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int416div(int4, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR + (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416pl
+);
+
+CREATE OPERATOR - (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416mi
+);
+
+CREATE OPERATOR * (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416mul
+);
+
+CREATE OPERATOR / (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416div
+);
+
+--
+-- Cross-type arithmetic: int16 op int2
+--
+CREATE FUNCTION int162pl(int16, int2)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int162mi(int16, int2)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int162mul(int16, int2)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int162div(int16, int2)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR + (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162pl
+);
+
+CREATE OPERATOR - (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162mi
+);
+
+CREATE OPERATOR * (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162mul
+);
+
+CREATE OPERATOR / (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162div
+);
+
+--
+-- Cross-type arithmetic: int2 op int16
+--
+CREATE FUNCTION int216pl(int2, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int216mi(int2, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int216mul(int2, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int216div(int2, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR + (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216pl
+);
+
+CREATE OPERATOR - (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216mi
+);
+
+CREATE OPERATOR * (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216mul
+);
+
+CREATE OPERATOR / (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216div
+);
+
+--
+-- Cross-type comparison: int16 vs int8
+--
+CREATE FUNCTION int168eq(int16, int8)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int168ne(int16, int8)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int168lt(int16, int8)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int168gt(int16, int8)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int168le(int16, int8)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int168ge(int16, int8)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int168cmp(int16, int8)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR = (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168eq,
+    COMMUTATOR = =, NEGATOR = <>,
+    RESTRICT = eqsel, JOIN = eqjoinsel,
+    HASHES, MERGES
+);
+
+CREATE OPERATOR <> (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168ne,
+    COMMUTATOR = <>, NEGATOR = =,
+    RESTRICT = neqsel, JOIN = neqjoinsel
+);
+
+CREATE OPERATOR < (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168lt,
+    COMMUTATOR = >, NEGATOR = >=,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR > (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168gt,
+    COMMUTATOR = <, NEGATOR = <=,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR <= (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168le,
+    COMMUTATOR = >=, NEGATOR = >,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR >= (
+    LEFTARG = int16, RIGHTARG = int8,
+    PROCEDURE = int168ge,
+    COMMUTATOR = <=, NEGATOR = <,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+--
+-- Cross-type comparison: int8 vs int16
+--
+CREATE FUNCTION int816eq(int8, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int816ne(int8, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int816lt(int8, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int816gt(int8, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int816le(int8, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int816ge(int8, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int816cmp(int8, int16)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR = (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816eq,
+    COMMUTATOR = =, NEGATOR = <>,
+    RESTRICT = eqsel, JOIN = eqjoinsel,
+    HASHES, MERGES
+);
+
+CREATE OPERATOR <> (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816ne,
+    COMMUTATOR = <>, NEGATOR = =,
+    RESTRICT = neqsel, JOIN = neqjoinsel
+);
+
+CREATE OPERATOR < (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816lt,
+    COMMUTATOR = >, NEGATOR = >=,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR > (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816gt,
+    COMMUTATOR = <, NEGATOR = <=,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR <= (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816le,
+    COMMUTATOR = >=, NEGATOR = >,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR >= (
+    LEFTARG = int8, RIGHTARG = int16,
+    PROCEDURE = int816ge,
+    COMMUTATOR = <=, NEGATOR = <,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+--
+-- Cross-type comparison: int16 vs int4
+--
+CREATE FUNCTION int164eq(int16, int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int164ne(int16, int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int164lt(int16, int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int164gt(int16, int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int164le(int16, int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int164ge(int16, int4)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int164cmp(int16, int4)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR = (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164eq,
+    COMMUTATOR = =, NEGATOR = <>,
+    RESTRICT = eqsel, JOIN = eqjoinsel,
+    HASHES, MERGES
+);
+
+CREATE OPERATOR <> (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164ne,
+    COMMUTATOR = <>, NEGATOR = =,
+    RESTRICT = neqsel, JOIN = neqjoinsel
+);
+
+CREATE OPERATOR < (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164lt,
+    COMMUTATOR = >, NEGATOR = >=,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR > (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164gt,
+    COMMUTATOR = <, NEGATOR = <=,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR <= (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164le,
+    COMMUTATOR = >=, NEGATOR = >,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR >= (
+    LEFTARG = int16, RIGHTARG = int4,
+    PROCEDURE = int164ge,
+    COMMUTATOR = <=, NEGATOR = <,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+--
+-- Cross-type comparison: int4 vs int16
+--
+CREATE FUNCTION int416eq(int4, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int416ne(int4, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int416lt(int4, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int416gt(int4, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int416le(int4, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int416ge(int4, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int416cmp(int4, int16)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR = (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416eq,
+    COMMUTATOR = =, NEGATOR = <>,
+    RESTRICT = eqsel, JOIN = eqjoinsel,
+    HASHES, MERGES
+);
+
+CREATE OPERATOR <> (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416ne,
+    COMMUTATOR = <>, NEGATOR = =,
+    RESTRICT = neqsel, JOIN = neqjoinsel
+);
+
+CREATE OPERATOR < (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416lt,
+    COMMUTATOR = >, NEGATOR = >=,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR > (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416gt,
+    COMMUTATOR = <, NEGATOR = <=,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR <= (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416le,
+    COMMUTATOR = >=, NEGATOR = >,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR >= (
+    LEFTARG = int4, RIGHTARG = int16,
+    PROCEDURE = int416ge,
+    COMMUTATOR = <=, NEGATOR = <,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+--
+-- Cross-type comparison: int16 vs int2
+--
+CREATE FUNCTION int162eq(int16, int2)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int162ne(int16, int2)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int162lt(int16, int2)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int162gt(int16, int2)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int162le(int16, int2)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int162ge(int16, int2)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int162cmp(int16, int2)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR = (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162eq,
+    COMMUTATOR = =, NEGATOR = <>,
+    RESTRICT = eqsel, JOIN = eqjoinsel,
+    HASHES, MERGES
+);
+
+CREATE OPERATOR <> (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162ne,
+    COMMUTATOR = <>, NEGATOR = =,
+    RESTRICT = neqsel, JOIN = neqjoinsel
+);
+
+CREATE OPERATOR < (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162lt,
+    COMMUTATOR = >, NEGATOR = >=,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR > (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162gt,
+    COMMUTATOR = <, NEGATOR = <=,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR <= (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162le,
+    COMMUTATOR = >=, NEGATOR = >,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR >= (
+    LEFTARG = int16, RIGHTARG = int2,
+    PROCEDURE = int162ge,
+    COMMUTATOR = <=, NEGATOR = <,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+--
+-- Cross-type comparison: int2 vs int16
+--
+CREATE FUNCTION int216eq(int2, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int216ne(int2, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int216lt(int2, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int216gt(int2, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int216le(int2, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int216ge(int2, int16)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int216cmp(int2, int16)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE OPERATOR = (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216eq,
+    COMMUTATOR = =, NEGATOR = <>,
+    RESTRICT = eqsel, JOIN = eqjoinsel,
+    HASHES, MERGES
+);
+
+CREATE OPERATOR <> (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216ne,
+    COMMUTATOR = <>, NEGATOR = =,
+    RESTRICT = neqsel, JOIN = neqjoinsel
+);
+
+CREATE OPERATOR < (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216lt,
+    COMMUTATOR = >, NEGATOR = >=,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR > (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216gt,
+    COMMUTATOR = <, NEGATOR = <=,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR <= (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216le,
+    COMMUTATOR = >=, NEGATOR = >,
+    RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR >= (
+    LEFTARG = int2, RIGHTARG = int16,
+    PROCEDURE = int216ge,
+    COMMUTATOR = <=, NEGATOR = <,
+    RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+--
+-- Hash function
+--
+CREATE FUNCTION int16_hash(int16)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- Aggregate support functions
+--
+CREATE FUNCTION int16larger(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16smaller(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+-- sum(int16) uses int16pl as both transition and combine function
+
+-- avg(int16) uses a dedicated state type to hold sum + count
+CREATE TYPE int16_avg_state;
+
+CREATE FUNCTION int16_avg_state_in(cstring)
+RETURNS int16_avg_state
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16_avg_state_out(int16_avg_state)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE TYPE int16_avg_state (
+    INPUT          = int16_avg_state_in,
+    OUTPUT         = int16_avg_state_out,
+    INTERNALLENGTH = 24,
+    ALIGNMENT      = double,
+    PASSEDBYVALUE  = false
+);
+
+CREATE FUNCTION int16_avg_accum(int16_avg_state, int16)
+RETURNS int16_avg_state
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION int16_avg_accum_combine(int16_avg_state, int16_avg_state)
+RETURNS int16_avg_state
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE PARALLEL SAFE;
+
+CREATE FUNCTION int16_avg(int16_avg_state)
+RETURNS numeric
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE PARALLEL SAFE;
+
+--
+-- Aggregates
+--
+CREATE AGGREGATE sum(int16) (
+    SFUNC = int16pl,
+    STYPE = int16,
+    COMBINEFUNC = int16pl,
+    PARALLEL = SAFE
+);
+
+CREATE AGGREGATE avg(int16) (
+    SFUNC = int16_avg_accum,
+    STYPE = int16_avg_state,
+    FINALFUNC = int16_avg,
+    COMBINEFUNC = int16_avg_accum_combine,
+    PARALLEL = SAFE
+);
+
+CREATE AGGREGATE min(int16) (
+    SFUNC = int16smaller,
+    STYPE = int16,
+    COMBINEFUNC = int16smaller,
+    PARALLEL = SAFE
+);
+
+CREATE AGGREGATE max(int16) (
+    SFUNC = int16larger,
+    STYPE = int16,
+    COMBINEFUNC = int16larger,
+    PARALLEL = SAFE
+);
+
+--
+-- Additional mathematical functions (gcd, lcm, factorial)
+--
+CREATE FUNCTION gcd(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME', 'int16gcd'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION lcm(int16, int16)
+RETURNS int16
+AS 'MODULE_PATHNAME', 'int16lcm'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION factorial(int16)
+RETURNS numeric
+AS 'MODULE_PATHNAME', 'int16factorial'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- Set-returning function: generate_series
+--
+CREATE FUNCTION generate_series(int16, int16)
+RETURNS SETOF int16
+AS 'MODULE_PATHNAME', 'generate_series_int16'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE
+ROWS 1000;
+
+CREATE FUNCTION generate_series(int16, int16, int16)
+RETURNS SETOF int16
+AS 'MODULE_PATHNAME', 'generate_series_step_int16'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE
+ROWS 1000;
+
+--
+-- in_range support function for window frames
+--
+CREATE FUNCTION in_range(int16, int16, int16, boolean, boolean)
+RETURNS boolean
+AS 'MODULE_PATHNAME', 'in_range_int16_int16'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- B-tree support functions
+--
+CREATE FUNCTION int16sortsupport(internal)
+RETURNS void
+AS 'MODULE_PATHNAME', 'int16sortsupport'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION int16skipsupport(internal)
+RETURNS void
+AS 'MODULE_PATHNAME', 'int16skipsupport'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- B-tree operator class
+--
+CREATE OPERATOR CLASS int16_ops
+    DEFAULT FOR TYPE int16 USING btree AS
+        OPERATOR        1       < ,
+        OPERATOR        2       <= ,
+        OPERATOR        3       = ,
+        OPERATOR        4       >= ,
+        OPERATOR        5       > ,
+        FUNCTION        1       int16_cmp(int16, int16),
+        FUNCTION        2       int16sortsupport(internal),
+        FUNCTION        3       in_range(int16, int16, int16, boolean, boolean),
+        FUNCTION        4       btequalimage(oid),
+        FUNCTION        6       int16skipsupport(internal);
+
+--
+-- Hash operator class
+--
+CREATE OPERATOR CLASS int16_ops
+    DEFAULT FOR TYPE int16 USING hash AS
+        OPERATOR        1       = ,
+        FUNCTION        1       int16_hash(int16);
diff --git a/contrib/int16/int16.c b/contrib/int16/int16.c
new file mode 100644
index 00000000000..2c788af7821
--- /dev/null
+++ b/contrib/int16/int16.c
@@ -0,0 +1,2862 @@
+/*-------------------------------------------------------------------------
+ *
+ * int16.c
+ *	  128-bit signed integer operations for the "int16" data type.
+ *
+ * Portions Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * contrib/int16/int16.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <ctype.h>
+#include <limits.h>
+
+#include "common/hashfn.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/numeric.h"
+#include "int16.h"
+
+PG_MODULE_MAGIC;
+
+/* I/O */
+PG_FUNCTION_INFO_V1(int16in);
+PG_FUNCTION_INFO_V1(int16out);
+PG_FUNCTION_INFO_V1(int16recv);
+PG_FUNCTION_INFO_V1(int16send);
+
+/* Arithmetic */
+PG_FUNCTION_INFO_V1(int16pl);
+PG_FUNCTION_INFO_V1(int16mi);
+PG_FUNCTION_INFO_V1(int16mul);
+PG_FUNCTION_INFO_V1(int16div);
+PG_FUNCTION_INFO_V1(int16mod);
+PG_FUNCTION_INFO_V1(int16um);
+PG_FUNCTION_INFO_V1(int16up);
+PG_FUNCTION_INFO_V1(int16abs);
+
+/* Comparison */
+PG_FUNCTION_INFO_V1(int16eq);
+PG_FUNCTION_INFO_V1(int16ne);
+PG_FUNCTION_INFO_V1(int16lt);
+PG_FUNCTION_INFO_V1(int16gt);
+PG_FUNCTION_INFO_V1(int16le);
+PG_FUNCTION_INFO_V1(int16ge);
+PG_FUNCTION_INFO_V1(int16_cmp);
+
+/* Bitwise */
+PG_FUNCTION_INFO_V1(int16and);
+PG_FUNCTION_INFO_V1(int16or);
+PG_FUNCTION_INFO_V1(int16xor);
+PG_FUNCTION_INFO_V1(int16not);
+PG_FUNCTION_INFO_V1(int16shl);
+PG_FUNCTION_INFO_V1(int16shr);
+
+/* Conversions */
+PG_FUNCTION_INFO_V1(int16_from_int8);
+PG_FUNCTION_INFO_V1(int16_to_int8);
+PG_FUNCTION_INFO_V1(int16_from_int4);
+PG_FUNCTION_INFO_V1(int16_to_int4);
+PG_FUNCTION_INFO_V1(int16_from_int2);
+PG_FUNCTION_INFO_V1(int16_to_int2);
+
+/* Cross-type arithmetic: int16 op int8 */
+PG_FUNCTION_INFO_V1(int168pl);
+PG_FUNCTION_INFO_V1(int168mi);
+PG_FUNCTION_INFO_V1(int168mul);
+PG_FUNCTION_INFO_V1(int168div);
+PG_FUNCTION_INFO_V1(int816pl);
+PG_FUNCTION_INFO_V1(int816mi);
+PG_FUNCTION_INFO_V1(int816mul);
+PG_FUNCTION_INFO_V1(int816div);
+
+/* Cross-type arithmetic: int16 op int4 */
+PG_FUNCTION_INFO_V1(int164pl);
+PG_FUNCTION_INFO_V1(int164mi);
+PG_FUNCTION_INFO_V1(int164mul);
+PG_FUNCTION_INFO_V1(int164div);
+PG_FUNCTION_INFO_V1(int416pl);
+PG_FUNCTION_INFO_V1(int416mi);
+PG_FUNCTION_INFO_V1(int416mul);
+PG_FUNCTION_INFO_V1(int416div);
+
+/* Cross-type arithmetic: int16 op int2 */
+PG_FUNCTION_INFO_V1(int162pl);
+PG_FUNCTION_INFO_V1(int162mi);
+PG_FUNCTION_INFO_V1(int162mul);
+PG_FUNCTION_INFO_V1(int162div);
+PG_FUNCTION_INFO_V1(int216pl);
+PG_FUNCTION_INFO_V1(int216mi);
+PG_FUNCTION_INFO_V1(int216mul);
+PG_FUNCTION_INFO_V1(int216div);
+
+/* Cross-type comparison: int16 vs int8 */
+PG_FUNCTION_INFO_V1(int168eq);
+PG_FUNCTION_INFO_V1(int168ne);
+PG_FUNCTION_INFO_V1(int168lt);
+PG_FUNCTION_INFO_V1(int168gt);
+PG_FUNCTION_INFO_V1(int168le);
+PG_FUNCTION_INFO_V1(int168ge);
+PG_FUNCTION_INFO_V1(int168cmp);
+PG_FUNCTION_INFO_V1(int816eq);
+PG_FUNCTION_INFO_V1(int816ne);
+PG_FUNCTION_INFO_V1(int816lt);
+PG_FUNCTION_INFO_V1(int816gt);
+PG_FUNCTION_INFO_V1(int816le);
+PG_FUNCTION_INFO_V1(int816ge);
+PG_FUNCTION_INFO_V1(int816cmp);
+
+/* Cross-type comparison: int16 vs int4 */
+PG_FUNCTION_INFO_V1(int164eq);
+PG_FUNCTION_INFO_V1(int164ne);
+PG_FUNCTION_INFO_V1(int164lt);
+PG_FUNCTION_INFO_V1(int164gt);
+PG_FUNCTION_INFO_V1(int164le);
+PG_FUNCTION_INFO_V1(int164ge);
+PG_FUNCTION_INFO_V1(int164cmp);
+PG_FUNCTION_INFO_V1(int416eq);
+PG_FUNCTION_INFO_V1(int416ne);
+PG_FUNCTION_INFO_V1(int416lt);
+PG_FUNCTION_INFO_V1(int416gt);
+PG_FUNCTION_INFO_V1(int416le);
+PG_FUNCTION_INFO_V1(int416ge);
+PG_FUNCTION_INFO_V1(int416cmp);
+
+/* Cross-type comparison: int16 vs int2 */
+PG_FUNCTION_INFO_V1(int162eq);
+PG_FUNCTION_INFO_V1(int162ne);
+PG_FUNCTION_INFO_V1(int162lt);
+PG_FUNCTION_INFO_V1(int162gt);
+PG_FUNCTION_INFO_V1(int162le);
+PG_FUNCTION_INFO_V1(int162ge);
+PG_FUNCTION_INFO_V1(int162cmp);
+PG_FUNCTION_INFO_V1(int216eq);
+PG_FUNCTION_INFO_V1(int216ne);
+PG_FUNCTION_INFO_V1(int216lt);
+PG_FUNCTION_INFO_V1(int216gt);
+PG_FUNCTION_INFO_V1(int216le);
+PG_FUNCTION_INFO_V1(int216ge);
+PG_FUNCTION_INFO_V1(int216cmp);
+
+/* Hash */
+PG_FUNCTION_INFO_V1(int16_hash);
+
+/* Aggregates */
+PG_FUNCTION_INFO_V1(int16larger);
+PG_FUNCTION_INFO_V1(int16smaller);
+PG_FUNCTION_INFO_V1(int16_avg_state_in);
+PG_FUNCTION_INFO_V1(int16_avg_state_out);
+PG_FUNCTION_INFO_V1(int16_avg_accum);
+PG_FUNCTION_INFO_V1(int16_avg_accum_combine);
+PG_FUNCTION_INFO_V1(int16_avg);
+
+/*
+ * Internal helpers
+ */
+
+/* Add two INT128 values with overflow check; returns true on success */
+bool
+int128_add_overflow(INT128 a, INT128 b, INT128 *result)
+{
+#if USE_NATIVE_INT128
+	return __builtin_add_overflow(a, b, result);
+#else
+	INT128		r = make_int128(0, 0);
+
+	int128_add_int128(&r, a);
+	int128_add_int128(&r, b);
+
+	/*
+	 * Overflow check: if a and b have the same sign and the result has a
+	 * different sign, overflow occurred.
+	 */
+	if ((a.hi < 0) == (b.hi < 0))
+	{
+		if ((a.hi < 0) != (r.hi < 0))
+			return true;
+	}
+	*result = r;
+	return false;
+#endif
+}
+
+/* Subtract INT128 v from INT128 variable *i128 */
+static inline void
+int128_sub_int128(INT128 *i128, INT128 v)
+{
+#if USE_NATIVE_INT128
+	*i128 -= v;
+#else
+	int128_sub_uint64(i128, v.lo);
+	i128->hi -= v.hi;
+#endif
+}
+
+/* Negate an INT128 value with overflow check (INT128_MIN overflows) */
+bool
+int128_negate_overflow(INT128 a, INT128 *result)
+{
+#if USE_NATIVE_INT128
+	return __builtin_sub_overflow(0, a, result);
+#else
+	/* INT128_MIN is the only value that overflows on negation */
+	if (a.hi == INT64CONST(0x8000000000000000) && a.lo == 0)
+		return true;
+	*result = make_int128(0, 0);
+	int128_sub_int128(result, a);
+	return false;
+#endif
+}
+
+/* Subtract b from a with overflow check */
+bool
+int128_sub_overflow(INT128 a, INT128 b, INT128 *result)
+{
+#if USE_NATIVE_INT128
+	return __builtin_sub_overflow(a, b, result);
+#else
+	INT128		r = a;
+
+	int128_sub_int128(&r, b);
+
+	/*
+	 * Overflow: if a and b have different signs, and the result's sign
+	 * differs from a's sign, then overflow occurred.
+	 */
+	if ((a.hi < 0) != (b.hi < 0))
+	{
+		if ((a.hi < 0) != (r.hi < 0))
+			return true;
+	}
+	*result = r;
+	return false;
+#endif
+}
+
+/* Multiply two INT128 values with overflow check */
+bool
+int128_mul_overflow(INT128 a, INT128 b, INT128 *result)
+{
+#if USE_NATIVE_INT128
+	return __builtin_mul_overflow(a, b, result);
+#else
+	/*
+	 * Schoolbook multiply using absolute values, with overflow detection. a =
+	 * a_hi*2^64 + a_lo, b = b_hi*2^64 + b_lo (unsigned). a*b =
+	 * (a_hi*b_hi)*2^128 + (a_hi*b_lo + a_lo*b_hi)*2^64 + a_lo*b_lo For the
+	 * result to fit in 128 bits, a_hi*b_hi must be zero, and the cross terms
+	 * must fit when added to the upper 64 bits.
+	 */
+	bool		a_neg = (a.hi < 0);
+	bool		b_neg = (b.hi < 0);
+	bool		result_neg = (a_neg != b_neg);
+	uint64		a_hi,
+				a_lo,
+				b_hi,
+				b_lo;
+	uint64		prod_hi,
+				prod_lo;
+	uint64		cross,
+				old;
+
+	/* Get absolute values */
+	a_lo = a.lo;
+	a_hi = (uint64) a.hi;
+	if (a_neg)
+	{
+		a_hi = ~a_hi;
+		a_lo = ~a_lo + 1;
+		if (a_lo == 0)
+			a_hi++;
+	}
+	b_lo = b.lo;
+	b_hi = (uint64) b.hi;
+	if (b_neg)
+	{
+		b_hi = ~b_hi;
+		b_lo = ~b_lo + 1;
+		if (b_lo == 0)
+			b_hi++;
+	}
+
+	/* If both high parts are nonzero, product exceeds 128 bits */
+	if (a_hi != 0 && b_hi != 0)
+		return true;
+
+	/* Compute a_lo * b_lo as 128-bit product (using 32-bit limbs) */
+	{
+		uint64		a0 = (uint32) a_lo;
+		uint64		a1 = a_lo >> 32;
+		uint64		b0 = (uint32) b_lo;
+		uint64		b1 = b_lo >> 32;
+		uint64		p0 = a0 * b0;
+		uint64		p1 = a0 * b1;
+		uint64		p2 = a1 * b0;
+		uint64		p3 = a1 * b1;
+		uint64		carry = (p0 >> 32) + (uint32) p1 + (uint32) p2;
+
+		prod_lo = (p0 & 0xffffffff) | (carry << 32);
+		prod_hi = p3 + (carry >> 32) + (p1 >> 32) + (p2 >> 32);
+	}
+
+	/* Add cross term a_hi * b_lo (shifted left 64, so adds to prod_hi) */
+	if (a_hi != 0)
+	{
+		/* a_hi * b_lo must fit in 64 bits (it's added to prod_hi) */
+		if (b_lo != 0 && a_hi > UINT64_MAX / b_lo)
+			return true;
+		cross = a_hi * b_lo;
+		old = prod_hi;
+		prod_hi += cross;
+		if (prod_hi < old)
+			return true;
+	}
+
+	/* Add cross term a_lo * b_hi (shifted left 64, so adds to prod_hi) */
+	if (b_hi != 0)
+	{
+		if (a_lo != 0 && b_hi > UINT64_MAX / a_lo)
+			return true;
+		cross = b_hi * a_lo;
+		old = prod_hi;
+		prod_hi += cross;
+		if (prod_hi < old)
+			return true;
+	}
+
+	/*
+	 * Check that the unsigned 128-bit product fits in a signed 128-bit value.
+	 * For a positive result, prod_hi must not have bit 63 set (max is 2^127 -
+	 * 1).  For a negative result, the absolute value must be at most 2^127
+	 * (INT128_MIN), so prod_hi must be <= 0x8000000000000000, and if it
+	 * equals that, prod_lo must be 0.
+	 */
+	if (result_neg)
+	{
+		if (prod_hi > UINT64CONST(0x8000000000000000) ||
+			(prod_hi == UINT64CONST(0x8000000000000000) && prod_lo != 0))
+			return true;
+	}
+	else
+	{
+		if (prod_hi > UINT64CONST(0x7fffffffffffffff))
+			return true;
+	}
+
+	/* Assemble result */
+	{
+		INT128		rval = make_int128((int64) prod_hi, prod_lo);
+
+		/* Negate if needed */
+		if (result_neg)
+		{
+			if (int128_negate_overflow(rval, &rval))
+				return true;
+		}
+		*result = rval;
+		return false;
+	}
+#endif
+}
+
+/*
+ * Divide INT128 a by INT128 b, producing quotient and remainder.
+ * Returns true on overflow (division by zero, or INT128_MIN / -1).
+ */
+bool
+int128_div_mod_int128(INT128 a, INT128 b, INT128 *quotient, INT128 *remainder)
+{
+#if USE_NATIVE_INT128
+	if (b == 0)
+		return true;
+	/* INT128_MIN / -1 overflows */
+	if (a == INT16_MIN_VAL && b == -1)
+		return true;
+	*quotient = a / b;
+	*remainder = a % b;
+	return false;
+#else
+	/*
+	 * Manual shift-and-subtract division for the non-native case. We work
+	 * with absolute values and fix the sign at the end.
+	 */
+	bool		a_neg = (a.hi < 0);
+	bool		b_neg = (b.hi < 0);
+	bool		result_neg = (a_neg != b_neg);
+	uint64		ua_hi,
+				ua_lo,
+				ub_hi,
+				ub_lo;
+	uint64		q_hi = 0,
+				q_lo = 0;
+	uint64		r_hi = 0,
+				r_lo = 0;
+	int			bit;
+
+	if (b.hi == 0 && b.lo == 0)
+		return true;
+
+	/* Check for INT128_MIN / -1: a == INT128_MIN, b == -1 */
+	if (a.hi == INT64CONST(0x8000000000000000) && a.lo == 0 &&
+		b.hi == -1 && b.lo == UINT64CONST(0xffffffffffffffff))
+		return true;
+
+	/* Compute absolute values of a */
+	ua_hi = (uint64) a.hi;
+	ua_lo = a.lo;
+	if (a_neg)
+	{
+		ua_hi = ~ua_hi;
+		ua_lo = ~ua_lo + 1;
+		if (ua_lo == 0)
+			ua_hi++;
+	}
+
+	/* Compute absolute values of b */
+	ub_hi = (uint64) b.hi;
+	ub_lo = b.lo;
+	if (b_neg)
+	{
+		ub_hi = ~ub_hi;
+		ub_lo = ~ub_lo + 1;
+		if (ub_lo == 0)
+			ub_hi++;
+	}
+
+	/* Shift-and-subtract: process 128 bits from most significant to least */
+	for (bit = 127; bit >= 0; bit--)
+	{
+		uint64		new_r_hi = (r_hi << 1) | (r_lo >> 63);
+		uint64		new_r_lo = (r_lo << 1);
+
+		/* Extract bit 'bit' from the dividend */
+		if (bit >= 64)
+		{
+			if (ua_hi & (UINT64CONST(1) << (bit - 64)))
+				new_r_lo |= 1;
+		}
+		else
+		{
+			if (ua_lo & (UINT64CONST(1) << bit))
+				new_r_lo |= 1;
+		}
+
+		r_hi = new_r_hi;
+		r_lo = new_r_lo;
+
+		/* If r >= ub, subtract ub and set quotient bit */
+		if (r_hi > ub_hi ||
+			(r_hi == ub_hi && r_lo >= ub_lo))
+		{
+			uint64		old_r_lo = r_lo;
+
+			r_lo -= ub_lo;
+			r_hi -= ub_hi;
+			if (r_lo > old_r_lo)
+				r_hi--;
+
+			if (bit >= 64)
+				q_hi |= (UINT64CONST(1) << (bit - 64));
+			else
+				q_lo |= (UINT64CONST(1) << bit);
+		}
+	}
+
+	/* Apply sign to quotient */
+	{
+		INT128		q_val = make_int128((int64) q_hi, q_lo);
+		INT128		r_val = make_int128((int64) r_hi, r_lo);
+
+		if (result_neg)
+			int128_negate_overflow(q_val, &q_val);
+
+		/* Remainder has the same sign as the dividend */
+		if (a_neg)
+			int128_negate_overflow(r_val, &r_val);
+
+		*quotient = q_val;
+		*remainder = r_val;
+	}
+	return false;
+#endif
+}
+
+/* Widen int32 to INT128 */
+static inline INT128
+int32_to_int128(int32 v)
+{
+	return int64_to_int128((int64) v);
+}
+
+/* Widen int16 to INT128 */
+static inline INT128
+int16_to_int128(int16 v)
+{
+	return int64_to_int128((int64) v);
+}
+
+/* Convert INT128 to int64 with overflow check */
+static bool
+int128_to_int64_overflow(INT128 val, int64 *result)
+{
+#if USE_NATIVE_INT128
+	if (val < INT64_MIN || val > INT64_MAX)
+		return true;
+	*result = (int64) val;
+	return false;
+#else
+	if (val.hi < 0)
+	{
+		/* Negative: check that high bits are all 1s, or hi == -1 and lo >= 0 */
+		if (val.hi < -1)
+			return true;
+	}
+	else
+	{
+		/* Non-negative: hi must be 0 */
+		if (val.hi > 0)
+			return true;
+	}
+	*result = (int64) val.lo;
+	return false;
+#endif
+}
+
+/* Convert INT128 to int32 with overflow check */
+static bool
+int128_to_int32_overflow(INT128 val, int32 *result)
+{
+	int64		v64;
+
+	if (int128_to_int64_overflow(val, &v64))
+		return true;
+	if (v64 < INT32_MIN || v64 > INT32_MAX)
+		return true;
+	*result = (int32) v64;
+	return false;
+}
+
+/* Convert INT128 to int16 with overflow check */
+static bool
+int128_to_int16_overflow(INT128 val, int16 *result)
+{
+	int64		v64;
+
+	if (int128_to_int64_overflow(val, &v64))
+		return true;
+	if (v64 < INT16_MIN || v64 > INT16_MAX)
+		return true;
+	*result = (int16) v64;
+	return false;
+}
+
+
+/*----------------------------------------------------------
+ * I/O routines
+ *---------------------------------------------------------
+ */
+
+/* int16in()
+ * Parse a string to INT128.
+ */
+Datum
+int16in(PG_FUNCTION_ARGS)
+{
+	char	   *num = PG_GETARG_CSTRING(0);
+	INT128		result = make_int128(0, 0);
+	bool		negative = false;
+	const char *str;
+
+	/* Skip leading whitespace */
+	str = num;
+	while (*str && isspace((unsigned char) *str))
+		str++;
+
+	if (*str == '-')
+	{
+		negative = true;
+		str++;
+	}
+	else if (*str == '+')
+		str++;
+
+	if (*str == '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						"int16", num)));
+
+	/* Parse digits */
+	while (*str)
+	{
+		int			digit;
+
+		if (!isdigit((unsigned char) *str))
+			break;
+
+		digit = (*str - '0');
+
+		/* result = result * 10 + digit, with overflow check */
+		{
+			INT128		tmp = result;
+
+			/* tmp = result * 10 = result * 8 + result * 2 */
+			int128_add_int128(&tmp, result);	/* tmp = result * 2 */
+			{
+				INT128		eight = tmp;
+
+				int128_add_int128(&eight, eight);	/* eight = result * 4 */
+				int128_add_int128(&eight, eight);	/* eight = result * 8 */
+				int128_add_int128(&tmp, eight); /* tmp = result * 10 */
+			}
+			int128_add_int64(&tmp, digit);
+
+			/*
+			 * Check for overflow: if tmp became negative, the value exceeded
+			 * 2^127-1 (for positive) or 2^127 (for negative). For negative,
+			 * 2^127 is the valid |INT128_MIN|, but anything larger overflows.
+			 */
+			if (int128_sign(tmp) < 0)
+			{
+				bool		is_min_abs;
+
+#if USE_NATIVE_INT128
+				is_min_abs = (tmp == ((INT128) 1 << 127));
+#else
+				is_min_abs = (PG_INT128_HI_INT64(tmp) == INT64CONST(0x8000000000000000) &&
+							  PG_INT128_LO_UINT64(tmp) == 0);
+#endif
+				if (!negative || !is_min_abs)
+					ereport(ERROR,
+							(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+							 errmsg("value \"%s\" is out of range for type %s",
+									num, "int16")));
+			}
+			else if (!negative && int128_compare(tmp, INT16_MAX_VAL) > 0)
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+						 errmsg("value \"%s\" is out of range for type %s",
+								num, "int16")));
+			}
+
+			result = tmp;
+		}
+
+		str++;
+	}
+
+	/* Skip trailing whitespace */
+	while (*str && isspace((unsigned char) *str))
+		str++;
+
+	if (*str != '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("invalid input syntax for type %s: \"%s\"",
+						"int16", num)));
+
+	if (negative)
+	{
+		INT128		zero = make_int128(0, 0);
+
+		int128_sub_int128(&zero, result);
+		result = zero;
+	}
+
+	{
+		int16StructP ret = palloc(INT16_LEN);
+
+		memcpy(ret, &result, INT16_LEN);
+		PG_RETURN_INT16_P(ret);
+	}
+}
+
+/* int16out()
+ * Convert INT128 to string.
+ */
+Datum
+int16out(PG_FUNCTION_ARGS)
+{
+	int16StructP val = PG_GETARG_INT16_P(0);
+	INT128		v;
+	char		buf[42];		/* 39 digits + sign + null + safety */
+	char	   *p;
+	char	   *result;
+	bool		negative = false;
+
+	memcpy(&v, val, INT16_LEN);
+
+	/* Handle zero specially */
+	if (int128_is_zero(v))
+	{
+		result = palloc(2);
+		result[0] = '0';
+		result[1] = '\0';
+		PG_RETURN_CSTRING(result);
+	}
+
+	/* Handle negative */
+	if (int128_sign(v) < 0)
+	{
+		negative = true;
+		if (int128_negate_overflow(v, &v))
+		{
+			/* INT128_MIN: use a constant string */
+			result = pstrdup("-170141183460469231731687303715884105728");
+			PG_RETURN_CSTRING(result);
+		}
+	}
+
+	/* Convert to decimal digits (reverse order) */
+	p = buf + sizeof(buf) - 1;
+	*p = '\0';
+	do
+	{
+		int32		rem;
+
+		int128_div_mod_int32(&v, 10, &rem);
+		*--p = '0' + rem;
+	} while (!int128_is_zero(v));
+
+	if (negative)
+		*--p = '-';
+
+	result = pstrdup(p);
+	PG_RETURN_CSTRING(result);
+}
+
+/* int16recv() — binary input */
+Datum
+int16recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buffer = (StringInfo) PG_GETARG_POINTER(0);
+	int16StructP result;
+
+	result = (int16StructP) palloc(INT16_LEN);
+	memcpy(result, pq_getmsgbytes(buffer, INT16_LEN), INT16_LEN);
+	PG_RETURN_INT16_P(result);
+}
+
+/* int16send() — binary output */
+Datum
+int16send(PG_FUNCTION_ARGS)
+{
+	int16StructP val = PG_GETARG_INT16_P(0);
+	StringInfoData buffer;
+
+	pq_begintypsend(&buffer);
+	pq_sendbytes(&buffer, (char *) val, INT16_LEN);
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buffer));
+}
+
+
+/*----------------------------------------------------------
+ * Arithmetic operators (int16 vs int16)
+ *---------------------------------------------------------
+ */
+
+Datum
+int16pl(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_add_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16mi(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_sub_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16mul(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_mul_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16div(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				q,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_div_mod_int128(a, b, &q, &r))
+	{
+		if (int128_is_zero(b))
+			ereport(ERROR,
+					(errcode(ERRCODE_DIVISION_BY_ZERO),
+					 errmsg("division by zero")));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &q, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16mod(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				q,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_div_mod_int128(a, b, &q, &r))
+	{
+		if (int128_is_zero(b))
+			ereport(ERROR,
+					(errcode(ERRCODE_DIVISION_BY_ZERO),
+					 errmsg("division by zero")));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16um(PG_FUNCTION_ARGS)
+{
+	int16StructP arg = PG_GETARG_INT16_P(0);
+	INT128		a,
+				r;
+
+	memcpy(&a, arg, INT16_LEN);
+
+	if (int128_negate_overflow(a, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16up(PG_FUNCTION_ARGS)
+{
+	int16StructP arg = PG_GETARG_INT16_P(0);
+
+	/* Just return a copy */
+	int16StructP result = palloc(INT16_LEN);
+
+	memcpy(result, arg, INT16_LEN);
+	PG_RETURN_INT16_P(result);
+}
+
+Datum
+int16abs(PG_FUNCTION_ARGS)
+{
+	int16StructP arg = PG_GETARG_INT16_P(0);
+	INT128		a,
+				r;
+
+	memcpy(&a, arg, INT16_LEN);
+
+	if (int128_sign(a) < 0)
+	{
+		if (int128_negate_overflow(a, &r))
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+	else
+		r = a;
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+
+/*----------------------------------------------------------
+ * Comparison operators (int16 vs int16)
+ *---------------------------------------------------------
+ */
+
+Datum
+int16eq(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) == 0);
+}
+
+Datum
+int16ne(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) != 0);
+}
+
+Datum
+int16lt(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) < 0);
+}
+
+Datum
+int16gt(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) > 0);
+}
+
+Datum
+int16le(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) <= 0);
+}
+
+Datum
+int16ge(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) >= 0);
+}
+
+Datum
+int16_cmp(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_INT32(int128_compare(a, b));
+}
+
+
+/*----------------------------------------------------------
+ * Bitwise operators
+ *---------------------------------------------------------
+ */
+
+Datum
+int16and(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+#if USE_NATIVE_INT128
+	r = a & b;
+#else
+	r.hi = a.hi & b.hi;
+	r.lo = a.lo & b.lo;
+#endif
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16or(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+#if USE_NATIVE_INT128
+	r = a | b;
+#else
+	r.hi = a.hi | b.hi;
+	r.lo = a.lo | b.lo;
+#endif
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16xor(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+#if USE_NATIVE_INT128
+	r = a ^ b;
+#else
+	r.hi = a.hi ^ b.hi;
+	r.lo = a.lo ^ b.lo;
+#endif
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16not(PG_FUNCTION_ARGS)
+{
+	int16StructP arg = PG_GETARG_INT16_P(0);
+	INT128		a,
+				r;
+
+	memcpy(&a, arg, INT16_LEN);
+
+#if USE_NATIVE_INT128
+	r = ~a;
+#else
+	r.hi = ~a.hi;
+	r.lo = ~a.lo;
+#endif
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16shl(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		shift = PG_GETARG_INT32(1);
+	INT128		a,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+
+#if USE_NATIVE_INT128
+	/* avoid undefined behavior if shift is more than 127 */
+	shift &= 127;
+	r = a << shift;
+#else
+
+	/*
+	 * Replicate C shift behavior: mask shift to 7 bits (0-127). This matches
+	 * what most compilers do for native int128 shifts.
+	 */
+	shift &= 127;
+	if (shift == 0)
+		r = a;
+	else if (shift < 64)
+	{
+		r.hi = (a.hi << shift) | (a.lo >> (64 - shift));
+		r.lo = a.lo << shift;
+	}
+	else
+	{
+		r.hi = (int64) (a.lo << (shift - 64));
+		r.lo = 0;
+	}
+#endif
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16shr(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		shift = PG_GETARG_INT32(1);
+	INT128		a,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+
+#if USE_NATIVE_INT128
+	/* avoid undefined behavior if shift is more than 127 */
+	shift &= 127;
+	r = a >> shift;				/* arithmetic shift for signed type */
+#else
+
+	/*
+	 * Replicate C shift behavior: mask shift to 7 bits (0-127). This matches
+	 * what most compilers do for native int128 shifts.
+	 */
+	shift &= 127;
+	if (shift == 0)
+		r = a;
+	else if (shift < 64)
+	{
+		r.lo = (a.lo >> shift) | ((uint64) a.hi << (64 - shift));
+		r.hi = a.hi >> shift;	/* arithmetic shift */
+	}
+	else
+	{
+		r.lo = (uint64) a.hi >> (shift - 64);
+		r.hi = (a.hi < 0) ? -1 : 0;
+	}
+#endif
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+
+/*----------------------------------------------------------
+ * Conversion functions (for casts)
+ *---------------------------------------------------------
+ */
+
+Datum
+int16_from_int8(PG_FUNCTION_ARGS)
+{
+	int64		val = PG_GETARG_INT64(0);
+	INT128		result = int64_to_int128(val);
+	int16StructP ret = palloc(INT16_LEN);
+
+	memcpy(ret, &result, INT16_LEN);
+	PG_RETURN_INT16_P(ret);
+}
+
+Datum
+int16_to_int8(PG_FUNCTION_ARGS)
+{
+	int16StructP arg = PG_GETARG_INT16_P(0);
+	INT128		val;
+	int64		result;
+
+	memcpy(&val, arg, INT16_LEN);
+
+	if (int128_to_int64_overflow(val, &result))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	PG_RETURN_INT64(result);
+}
+
+Datum
+int16_from_int4(PG_FUNCTION_ARGS)
+{
+	int32		val = PG_GETARG_INT32(0);
+	INT128		result = int32_to_int128(val);
+	int16StructP ret = palloc(INT16_LEN);
+
+	memcpy(ret, &result, INT16_LEN);
+	PG_RETURN_INT16_P(ret);
+}
+
+Datum
+int16_to_int4(PG_FUNCTION_ARGS)
+{
+	int16StructP arg = PG_GETARG_INT16_P(0);
+	INT128		val;
+	int32		result;
+
+	memcpy(&val, arg, INT16_LEN);
+
+	if (int128_to_int32_overflow(val, &result))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	PG_RETURN_INT32(result);
+}
+
+Datum
+int16_from_int2(PG_FUNCTION_ARGS)
+{
+	int16		val = PG_GETARG_INT16(0);
+	INT128		result = int16_to_int128(val);
+	int16StructP ret = palloc(INT16_LEN);
+
+	memcpy(ret, &result, INT16_LEN);
+	PG_RETURN_INT16_P(ret);
+}
+
+Datum
+int16_to_int2(PG_FUNCTION_ARGS)
+{
+	int16StructP arg = PG_GETARG_INT16_P(0);
+	INT128		val;
+	int16		result;
+
+	memcpy(&val, arg, INT16_LEN);
+
+	if (int128_to_int16_overflow(val, &result))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	PG_RETURN_INT16(result);
+}
+
+
+/*----------------------------------------------------------
+ * Cross-type arithmetic: int16 op int8
+ *---------------------------------------------------------
+ */
+
+Datum
+int168pl(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	if (int128_add_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int168mi(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	if (int128_sub_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int168mul(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	if (int128_mul_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int168div(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b,
+				q,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	if (int128_div_mod_int128(a, b, &q, &r))
+	{
+		if (int128_is_zero(b))
+			ereport(ERROR,
+					(errcode(ERRCODE_DIVISION_BY_ZERO),
+					 errmsg("division by zero")));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &q, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+/* int8 op int16 */
+
+Datum
+int816pl(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_add_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int816mi(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_sub_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int816mul(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_mul_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int816div(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				q,
+				r;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_div_mod_int128(a, b, &q, &r))
+	{
+		if (int128_is_zero(b))
+			ereport(ERROR,
+					(errcode(ERRCODE_DIVISION_BY_ZERO),
+					 errmsg("division by zero")));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &q, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+
+/*----------------------------------------------------------
+ * Cross-type arithmetic: int16 op int4
+ *---------------------------------------------------------
+ */
+
+Datum
+int164pl(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	if (int128_add_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int164mi(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	if (int128_sub_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int164mul(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	if (int128_mul_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int164div(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b,
+				q,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	if (int128_div_mod_int128(a, b, &q, &r))
+	{
+		if (int128_is_zero(b))
+			ereport(ERROR,
+					(errcode(ERRCODE_DIVISION_BY_ZERO),
+					 errmsg("division by zero")));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &q, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+/* int4 op int16 */
+
+Datum
+int416pl(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_add_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int416mi(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_sub_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int416mul(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_mul_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int416div(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				q,
+				r;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_div_mod_int128(a, b, &q, &r))
+	{
+		if (int128_is_zero(b))
+			ereport(ERROR,
+					(errcode(ERRCODE_DIVISION_BY_ZERO),
+					 errmsg("division by zero")));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &q, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+
+/*----------------------------------------------------------
+ * Cross-type arithmetic: int16 op int2
+ *---------------------------------------------------------
+ */
+
+Datum
+int162pl(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	if (int128_add_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int162mi(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	if (int128_sub_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int162mul(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	if (int128_mul_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int162div(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b,
+				q,
+				r;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	if (int128_div_mod_int128(a, b, &q, &r))
+	{
+		if (int128_is_zero(b))
+			ereport(ERROR,
+					(errcode(ERRCODE_DIVISION_BY_ZERO),
+					 errmsg("division by zero")));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &q, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+/* int2 op int16 */
+
+Datum
+int216pl(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_add_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int216mi(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_sub_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int216mul(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				r;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_mul_overflow(a, b, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &r, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int216div(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				q,
+				r;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_div_mod_int128(a, b, &q, &r))
+	{
+		if (int128_is_zero(b))
+			ereport(ERROR,
+					(errcode(ERRCODE_DIVISION_BY_ZERO),
+					 errmsg("division by zero")));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, &q, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+
+/*----------------------------------------------------------
+ * Cross-type comparison operators
+ *---------------------------------------------------------
+ *
+ * Each pair (int16 vs int8/int4/int2) gets 6 operators + a cmp function.
+ * We use macros to reduce boilerplate.
+ *---------------------------------------------------------
+ */
+
+/* int16 vs int8 */
+
+Datum
+int168eq(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) == 0);
+}
+
+Datum
+int168ne(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) != 0);
+}
+
+Datum
+int168lt(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) < 0);
+}
+
+Datum
+int168gt(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) > 0);
+}
+
+Datum
+int168le(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) <= 0);
+}
+
+Datum
+int168ge(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) >= 0);
+}
+
+Datum
+int168cmp(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int64		arg2 = PG_GETARG_INT64(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int64_to_int128(arg2);
+
+	PG_RETURN_INT32(int128_compare(a, b));
+}
+
+/* int8 vs int16 */
+
+Datum
+int816eq(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) == 0);
+}
+
+Datum
+int816ne(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) != 0);
+}
+
+Datum
+int816lt(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) < 0);
+}
+
+Datum
+int816gt(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) > 0);
+}
+
+Datum
+int816le(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) <= 0);
+}
+
+Datum
+int816ge(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) >= 0);
+}
+
+Datum
+int816cmp(PG_FUNCTION_ARGS)
+{
+	int64		arg1 = PG_GETARG_INT64(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int64_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_INT32(int128_compare(a, b));
+}
+
+/* int16 vs int4 */
+
+Datum
+int164eq(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) == 0);
+}
+
+Datum
+int164ne(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) != 0);
+}
+
+Datum
+int164lt(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) < 0);
+}
+
+Datum
+int164gt(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) > 0);
+}
+
+Datum
+int164le(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) <= 0);
+}
+
+Datum
+int164ge(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) >= 0);
+}
+
+Datum
+int164cmp(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int32		arg2 = PG_GETARG_INT32(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int32_to_int128(arg2);
+
+	PG_RETURN_INT32(int128_compare(a, b));
+}
+
+/* int4 vs int16 */
+
+Datum
+int416eq(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) == 0);
+}
+
+Datum
+int416ne(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) != 0);
+}
+
+Datum
+int416lt(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) < 0);
+}
+
+Datum
+int416gt(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) > 0);
+}
+
+Datum
+int416le(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) <= 0);
+}
+
+Datum
+int416ge(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) >= 0);
+}
+
+Datum
+int416cmp(PG_FUNCTION_ARGS)
+{
+	int32		arg1 = PG_GETARG_INT32(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int32_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_INT32(int128_compare(a, b));
+}
+
+/* int16 vs int2 */
+
+Datum
+int162eq(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) == 0);
+}
+
+Datum
+int162ne(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) != 0);
+}
+
+Datum
+int162lt(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) < 0);
+}
+
+Datum
+int162gt(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) > 0);
+}
+
+Datum
+int162le(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) <= 0);
+}
+
+Datum
+int162ge(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	PG_RETURN_BOOL(int128_compare(a, b) >= 0);
+}
+
+Datum
+int162cmp(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16		arg2 = PG_GETARG_INT16(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	b = int16_to_int128(arg2);
+
+	PG_RETURN_INT32(int128_compare(a, b));
+}
+
+/* int2 vs int16 */
+
+Datum
+int216eq(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) == 0);
+}
+
+Datum
+int216ne(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) != 0);
+}
+
+Datum
+int216lt(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) < 0);
+}
+
+Datum
+int216gt(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) > 0);
+}
+
+Datum
+int216le(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) <= 0);
+}
+
+Datum
+int216ge(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_BOOL(int128_compare(a, b) >= 0);
+}
+
+Datum
+int216cmp(PG_FUNCTION_ARGS)
+{
+	int16		arg1 = PG_GETARG_INT16(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	a = int16_to_int128(arg1);
+	memcpy(&b, arg2, INT16_LEN);
+
+	PG_RETURN_INT32(int128_compare(a, b));
+}
+
+
+/*----------------------------------------------------------
+ * Hash function
+ *---------------------------------------------------------
+ */
+
+Datum
+int16_hash(PG_FUNCTION_ARGS)
+{
+	int16StructP arg = PG_GETARG_INT16_P(0);
+
+	return hash_any((unsigned char *) arg, INT16_LEN);
+}
+
+
+/*----------------------------------------------------------
+ * Aggregate support functions
+ *---------------------------------------------------------
+ */
+
+Datum
+int16larger(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_compare(a, b) >= 0)
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, arg1, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+	else
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, arg2, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+Datum
+int16smaller(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	if (int128_compare(a, b) <= 0)
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, arg1, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+	else
+	{
+		int16StructP result = palloc(INT16_LEN);
+
+		memcpy(result, arg2, INT16_LEN);
+		PG_RETURN_INT16_P(result);
+	}
+}
+
+/*
+ * State type for int16 avg aggregate: INT128 sum + int64 count.
+ * This is exposed as the SQL type "int16_avg_state" (24 bytes).
+ */
+typedef struct Int16AvgState
+{
+	INT128		sum;
+	int64		count;
+}			Int16AvgState;
+
+/* Macros for the avg state type (pass-by-reference, like int16) */
+#define PG_GETARG_AVGSTATE_P(n)	((Int16AvgState *) PG_GETARG_POINTER(n))
+#define PG_RETURN_AVGSTATE_P(x)	PG_RETURN_POINTER(x)
+
+Datum
+int16_avg_state_in(PG_FUNCTION_ARGS)
+{
+	/* This type is only used internally; reject direct input */
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("cannot accept a value of type int16_avg_state")));
+
+	PG_RETURN_NULL();			/* keep compiler quiet */
+}
+
+Datum
+int16_avg_state_out(PG_FUNCTION_ARGS)
+{
+	/* This type is only used internally; output a placeholder */
+	PG_RETURN_CSTRING(pstrdup("(internal)"));
+}
+
+Datum
+int16_avg_accum(PG_FUNCTION_ARGS)
+{
+	Int16AvgState *state;
+
+	if (PG_ARGISNULL(0))
+		state = palloc0_object(Int16AvgState);
+	else
+		state = PG_GETARG_AVGSTATE_P(0);
+
+	if (!PG_ARGISNULL(1))
+	{
+		int16StructP newval = PG_GETARG_INT16_P(1);
+		INT128		v;
+
+		memcpy(&v, newval, INT16_LEN);
+
+		int128_add_int128(&state->sum, v);
+		state->count++;
+	}
+
+	PG_RETURN_AVGSTATE_P(state);
+}
+
+Datum
+int16_avg_accum_combine(PG_FUNCTION_ARGS)
+{
+	Int16AvgState *state1;
+	Int16AvgState *state2;
+
+	state1 = PG_ARGISNULL(0) ? NULL : PG_GETARG_AVGSTATE_P(0);
+	state2 = PG_ARGISNULL(1) ? NULL : PG_GETARG_AVGSTATE_P(1);
+
+	if (state2 == NULL)
+	{
+		if (state1 == NULL)
+			state1 = palloc0_object(Int16AvgState);
+		PG_RETURN_AVGSTATE_P(state1);
+	}
+	if (state1 == NULL)
+	{
+		/* Return a copy of state2 */
+		Int16AvgState *result = palloc_object(Int16AvgState);
+
+		memcpy(result, state2, sizeof(Int16AvgState));
+		PG_RETURN_AVGSTATE_P(result);
+	}
+
+	/* Merge state2 into state1 */
+	int128_add_int128(&state1->sum, state2->sum);
+	state1->count += state2->count;
+
+	PG_RETURN_AVGSTATE_P(state1);
+}
+
+/*
+ * Convert INT128 to numeric Datum by going through string representation.
+ */
+Datum
+int128_to_numeric(INT128 val)
+{
+	char		buf[42];
+	char	   *p;
+	bool		negative = false;
+
+	if (int128_is_zero(val))
+		return DirectFunctionCall3(numeric_in, CStringGetDatum("0"),
+								   ObjectIdGetDatum(InvalidOid),
+								   Int32GetDatum(-1));
+
+	if (int128_sign(val) < 0)
+	{
+		negative = true;
+		if (int128_negate_overflow(val, &val))
+		{
+			/* INT128_MIN */
+			return DirectFunctionCall3(numeric_in,
+									   CStringGetDatum("-170141183460469231731687303715884105728"),
+									   ObjectIdGetDatum(InvalidOid),
+									   Int32GetDatum(-1));
+		}
+	}
+
+	p = buf + sizeof(buf) - 1;
+	*p = '\0';
+	do
+	{
+		int32		rem;
+
+		int128_div_mod_int32(&val, 10, &rem);
+		*--p = '0' + rem;
+	} while (!int128_is_zero(val));
+
+	if (negative)
+		*--p = '-';
+
+	return DirectFunctionCall3(numeric_in, CStringGetDatum(p),
+							   ObjectIdGetDatum(InvalidOid),
+							   Int32GetDatum(-1));
+}
+
+Datum
+int16_avg(PG_FUNCTION_ARGS)
+{
+	Int16AvgState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : PG_GETARG_AVGSTATE_P(0);
+
+	/* If there were no non-null inputs, return NULL */
+	if (state == NULL || state->count == 0)
+		PG_RETURN_NULL();
+
+	{
+		Datum		sumd = int128_to_numeric(state->sum);
+		Datum		countd = DirectFunctionCall1(int8_numeric,
+												 Int64GetDatum(state->count));
+
+		return DirectFunctionCall2(numeric_div, sumd, countd);
+	}
+}
diff --git a/contrib/int16/int16.control b/contrib/int16/int16.control
new file mode 100644
index 00000000000..72658844fab
--- /dev/null
+++ b/contrib/int16/int16.control
@@ -0,0 +1,6 @@
+# int16 extension
+comment = '128-bit signed integer data type'
+default_version = '1.0'
+module_pathname = '$libdir/int16'
+relocatable = true
+trusted = true
diff --git a/contrib/int16/int16.h b/contrib/int16/int16.h
new file mode 100644
index 00000000000..15d9db13b0a
--- /dev/null
+++ b/contrib/int16/int16.h
@@ -0,0 +1,65 @@
+/*-------------------------------------------------------------------------
+ *
+ * int16.h
+ *	  Header file for the "int16" 128-bit signed integer ADT.
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * contrib/int16/int16.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef INT16_EXT_H
+#define INT16_EXT_H
+
+#include "common/int128.h"
+
+/* int16 size in bytes */
+#define INT16_LEN	16
+
+typedef INT128 int16Struct;
+typedef int16Struct * int16StructP;
+
+/* fmgr interface macros */
+static inline Datum
+Int16PGetDatum(const int16StructP X)
+{
+	return PointerGetDatum(X);
+}
+
+#define PG_RETURN_INT16_P(X)	return Int16PGetDatum(X)
+
+static inline int16StructP
+DatumGetInt16P(Datum X)
+{
+	return (int16StructP) DatumGetPointer(X);
+}
+
+#define PG_GETARG_INT16_P(n)	DatumGetInt16P(PG_GETARG_DATUM(n))
+
+/*
+ * INT128 limits: 2^127 - 1 and -2^127.
+ * 170141183460469231731687303715884105727
+ * -170141183460469231731687303715884105728
+ */
+#define INT16_MAX_VAL	make_int128(INT64CONST(0x7fffffffffffffff), UINT64CONST(0xffffffffffffffff))
+#define INT16_MIN_VAL	make_int128(INT64CONST(0x8000000000000000), UINT64CONST(0))
+
+/* Convert INT128 to numeric Datum (shared by int16_avg and int16_numeric). */
+extern Datum int128_to_numeric(INT128 val);
+
+/* int16 input function (shared by int16in and numeric_int16). */
+extern Datum int16in(PG_FUNCTION_ARGS);
+
+/*
+ * Internal helpers shared across int16 source files.
+ * Defined in int16.c, used by int16_funcs.c.
+ */
+extern bool int128_add_overflow(INT128 a, INT128 b, INT128 *result);
+extern bool int128_sub_overflow(INT128 a, INT128 b, INT128 *result);
+extern bool int128_negate_overflow(INT128 a, INT128 *result);
+extern bool int128_mul_overflow(INT128 a, INT128 b, INT128 *result);
+extern bool int128_div_mod_int128(INT128 a, INT128 b,
+								  INT128 *quotient, INT128 *remainder);
+
+#endif							/* INT16_EXT_H */
diff --git a/contrib/int16/int16_funcs.c b/contrib/int16/int16_funcs.c
new file mode 100644
index 00000000000..cc19203cd9e
--- /dev/null
+++ b/contrib/int16/int16_funcs.c
@@ -0,0 +1,607 @@
+/*-------------------------------------------------------------------------
+ *
+ * int16_funcs.c
+ *	  Additional functions for the "int16" 128-bit signed integer data type.
+ *
+ * This file contains:
+ *   - gcd, lcm (Euclidean algorithm, mirroring int8)
+ *   - factorial (returns numeric, mirroring numeric_fac)
+ *   - generate_series (set-returning function, mirroring int8)
+ *   - in_range (window frame support, mirroring int8)
+ *   - btree sort support and skip support functions
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * contrib/int16/int16_funcs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <ctype.h>
+#include <limits.h>
+
+#include "fmgr.h"
+#include "funcapi.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/numeric.h"
+#include "utils/sortsupport.h"
+#include "utils/skipsupport.h"
+#include "int16.h"
+
+PG_FUNCTION_INFO_V1(int16gcd);
+PG_FUNCTION_INFO_V1(int16lcm);
+PG_FUNCTION_INFO_V1(int16factorial);
+PG_FUNCTION_INFO_V1(generate_series_int16);
+PG_FUNCTION_INFO_V1(generate_series_step_int16);
+PG_FUNCTION_INFO_V1(in_range_int16_int16);
+PG_FUNCTION_INFO_V1(int16sortsupport);
+PG_FUNCTION_INFO_V1(int16skipsupport);
+
+/*----------------------------------------------------------
+ * Local helpers
+ *---------------------------------------------------------
+ */
+
+/*
+ * Greatest Common Divisor (internal).
+ *
+ * Mirrors int8gcd_internal from int8.c, working in negative space to
+ * handle INT128_MIN (whose absolute value cannot be represented).
+ *
+ * Special cases:
+ *   gcd(x, 0) = gcd(0, x) = abs(x)
+ *   gcd(0, 0) = 0
+ *   gcd(INT128_MIN, 0), gcd(0, INT128_MIN), gcd(INT128_MIN, INT128_MIN)
+ *       all error (abs(INT128_MIN) overflows).
+ *   gcd(INT128_MIN, -1) = 1 (special-cased to avoid modulo exception).
+ */
+static INT128
+int16gcd_internal(INT128 arg1, INT128 arg2)
+{
+	INT128		swap;
+	INT128		a1,
+				a2;
+	INT128		zero = make_int128(0, 0);
+	INT128		minus_one;
+	INT128		q,
+				r;
+
+	/* Build -1 as INT128 */
+	if (int128_negate_overflow(make_int128(0, 1), &minus_one))
+	{
+		/* Should not happen */
+		ereport(ERROR,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("cannot negate 1")));
+	}
+
+	/*
+	 * Put the greater absolute value in arg1.
+	 *
+	 * We do this in negative space in order to handle INT128_MIN. A value
+	 * that is negative has a larger absolute value, so we compare: if a1 > a2
+	 * (in negative space, "less negative" means "smaller absolute value"),
+	 * then swap.
+	 */
+	if (int128_sign(arg1) < 0)
+		a1 = arg1;
+	else
+	{
+		if (int128_negate_overflow(arg1, &a1))
+		{
+			/* arg1 == INT128_MIN, abs is max, no swap needed */
+			a1 = arg1;
+		}
+	}
+	if (int128_sign(arg2) < 0)
+		a2 = arg2;
+	else
+	{
+		if (int128_negate_overflow(arg2, &a2))
+		{
+			/* arg2 == INT128_MIN */
+			a2 = arg2;
+		}
+	}
+
+	/*
+	 * In negative space, a "greater" value means a smaller absolute value. So
+	 * if a1 > a2, arg1 has a smaller absolute value than arg2, and we swap to
+	 * put the larger absolute value in arg1.
+	 */
+	if (int128_compare(a1, a2) > 0)
+	{
+		swap = arg1;
+		arg1 = arg2;
+		arg2 = swap;
+	}
+
+	/* Special care for INT128_MIN */
+	if (int128_compare(arg1, INT16_MIN_VAL) == 0)
+	{
+		if (int128_compare(arg2, zero) == 0 ||
+			int128_compare(arg2, INT16_MIN_VAL) == 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+
+		/*
+		 * Some machines throw a floating-point exception for INT128_MIN % -1,
+		 * which is a bit silly since the correct answer is perfectly
+		 * well-defined, namely zero.  Guard against this and just return the
+		 * result, gcd(INT128_MIN, -1) = 1.
+		 */
+		if (int128_compare(arg2, minus_one) == 0)
+			return make_int128(0, 1);
+	}
+
+	/* Use the Euclidean algorithm to find the GCD */
+	while (!int128_is_zero(arg2))
+	{
+		swap = arg2;
+		/* arg2 = arg1 % arg2 */
+		if (int128_div_mod_int128(arg1, arg2, &q, &r))
+		{
+			/* Only possible for INT128_MIN / -1, handled above */
+			ereport(ERROR,
+					(errcode(ERRCODE_INTERNAL_ERROR),
+					 errmsg("unexpected overflow in gcd")));
+		}
+		arg2 = r;
+		arg1 = swap;
+	}
+
+	/* Make sure the result is positive (we know it's not INT128_MIN) */
+	if (int128_sign(arg1) < 0)
+	{
+		if (int128_negate_overflow(arg1, &arg1))
+		{
+			/* Should not happen given the checks above */
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+		}
+	}
+
+	return arg1;
+}
+
+/*----------------------------------------------------------
+ * gcd
+ *---------------------------------------------------------
+ */
+Datum
+int16gcd(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				result;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	result = int16gcd_internal(a, b);
+
+	{
+		int16StructP r = palloc(INT16_LEN);
+
+		memcpy(r, &result, INT16_LEN);
+		PG_RETURN_INT16_P(r);
+	}
+}
+
+/*----------------------------------------------------------
+ * lcm
+ *---------------------------------------------------------
+ */
+Datum
+int16lcm(PG_FUNCTION_ARGS)
+{
+	int16StructP arg1 = PG_GETARG_INT16_P(0);
+	int16StructP arg2 = PG_GETARG_INT16_P(1);
+	INT128		a,
+				b,
+				gcd,
+				q,
+				r,
+				result;
+
+	memcpy(&a, arg1, INT16_LEN);
+	memcpy(&b, arg2, INT16_LEN);
+
+	/*
+	 * Handle lcm(x, 0) = lcm(0, x) = 0 as a special case.  This prevents a
+	 * division-by-zero error below when x is zero, and an overflow error from
+	 * the GCD computation when x = INT128_MIN.
+	 */
+	if (int128_is_zero(a) || int128_is_zero(b))
+	{
+		int16StructP res = palloc(INT16_LEN);
+		INT128		zero = make_int128(0, 0);
+
+		memcpy(res, &zero, INT16_LEN);
+		PG_RETURN_INT16_P(res);
+	}
+
+	/* lcm(x, y) = abs(x / gcd(x, y) * y) */
+	gcd = int16gcd_internal(a, b);
+
+	/* arg1 = arg1 / gcd */
+	if (int128_div_mod_int128(a, gcd, &q, &r))
+		ereport(ERROR,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("unexpected overflow in lcm")));
+
+	/* result = q * b */
+	if (int128_mul_overflow(q, b, &result))
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	/* If the result is INT128_MIN, it cannot be represented as positive */
+	if (int128_compare(result, INT16_MIN_VAL) == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("integer out of range")));
+
+	if (int128_sign(result) < 0)
+	{
+		if (int128_negate_overflow(result, &result))
+			ereport(ERROR,
+					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+					 errmsg("integer out of range")));
+	}
+
+	{
+		int16StructP res = palloc(INT16_LEN);
+
+		memcpy(res, &result, INT16_LEN);
+		PG_RETURN_INT16_P(res);
+	}
+}
+
+/*----------------------------------------------------------
+ * factorial
+ *---------------------------------------------------------
+ *
+ * Mirrors numeric_fac() from numeric.c.  Takes int16, returns numeric.
+ * The input is int16 but the practical limit is the same as for int8
+ * (~32177) because numeric format itself overflows.
+ */
+Datum
+int16factorial(PG_FUNCTION_ARGS)
+{
+	int16StructP arg = PG_GETARG_INT16_P(0);
+	INT128		num128;
+	int64		num;
+	Datum		result;
+	Datum		fact;
+
+	memcpy(&num128, arg, INT16_LEN);
+
+	/* Check if value is negative */
+	if (int128_sign(num128) < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("factorial of a negative number is undefined")));
+
+	/* Check if value exceeds INT64_MAX (would definitely overflow numeric) */
+	if (int128_compare(num128, int64_to_int128(INT64_MAX)) > 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("value overflows numeric format")));
+
+	/* Extract int64 value */
+	num = int128_to_int64(num128);
+
+	if (num <= 1)
+		PG_RETURN_DATUM(DirectFunctionCall1(int8_numeric,
+											Int64GetDatum(1)));
+
+	/* Fail immediately if the result would overflow */
+	if (num > 32177)
+		ereport(ERROR,
+				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+				 errmsg("value overflows numeric format")));
+
+	/*
+	 * Compute factorial iteratively using numeric multiplication. Start with
+	 * result = num, then multiply by num-1, num-2, ..., 2.
+	 */
+	result = DirectFunctionCall1(int8_numeric, Int64GetDatum(num));
+
+	for (num = num - 1; num > 1; num--)
+	{
+		CHECK_FOR_INTERRUPTS();
+
+		fact = DirectFunctionCall1(int8_numeric, Int64GetDatum(num));
+		result = DirectFunctionCall2(numeric_mul, result, fact);
+	}
+
+	PG_RETURN_DATUM(result);
+}
+
+/*----------------------------------------------------------
+ * generate_series
+ *---------------------------------------------------------
+ */
+
+typedef struct
+{
+	INT128		current;
+	INT128		finish;
+	INT128		step;
+}			int16_series_fctx;
+
+Datum
+generate_series_step_int16(PG_FUNCTION_ARGS)
+{
+	FuncCallContext *funcctx;
+	int16_series_fctx *fctx;
+	MemoryContext oldcontext;
+	INT128		result;
+
+	/* stuff done only on the first call of the function */
+	if (SRF_IS_FIRSTCALL())
+	{
+		int16StructP start_val = PG_GETARG_INT16_P(0);
+		int16StructP finish_val = PG_GETARG_INT16_P(1);
+		INT128		start;
+		INT128		finish;
+		INT128		step = int64_to_int128(1);
+
+		memcpy(&start, start_val, INT16_LEN);
+		memcpy(&finish, finish_val, INT16_LEN);
+
+		/* see if we were given an explicit step size */
+		if (PG_NARGS() == 3)
+		{
+			int16StructP step_val = PG_GETARG_INT16_P(2);
+
+			memcpy(&step, step_val, INT16_LEN);
+		}
+
+		if (int128_is_zero(step))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("step size cannot equal zero")));
+
+		/* create a function context for cross-call persistence */
+		funcctx = SRF_FIRSTCALL_INIT();
+
+		/*
+		 * switch to memory context appropriate for multiple function calls
+		 */
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		/* allocate memory for user context */
+		fctx = palloc(sizeof(int16_series_fctx));
+
+		/*
+		 * Use fctx to keep state from call to call. Seed current with the
+		 * original start value
+		 */
+		fctx->current = start;
+		fctx->finish = finish;
+		fctx->step = step;
+
+		funcctx->user_fctx = fctx;
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+
+	/*
+	 * get the saved state and use current as the result for this iteration
+	 */
+	fctx = funcctx->user_fctx;
+	result = fctx->current;
+
+	if ((int128_sign(fctx->step) > 0 && int128_compare(fctx->current, fctx->finish) <= 0) ||
+		(int128_sign(fctx->step) < 0 && int128_compare(fctx->current, fctx->finish) >= 0))
+	{
+		/*
+		 * Increment current in preparation for next iteration. If next-value
+		 * computation overflows, this is the final result.
+		 */
+		if (int128_add_overflow(fctx->current, fctx->step, &fctx->current))
+			fctx->step = make_int128(0, 0);
+
+		/* do when there is more left to send */
+		{
+			int16StructP r = palloc(INT16_LEN);
+
+			memcpy(r, &result, INT16_LEN);
+			SRF_RETURN_NEXT(funcctx, PointerGetDatum(r));
+		}
+	}
+	else
+		/* do when there is no more left */
+		SRF_RETURN_DONE(funcctx);
+}
+
+Datum
+generate_series_int16(PG_FUNCTION_ARGS)
+{
+	return generate_series_step_int16(fcinfo);
+}
+
+/*----------------------------------------------------------
+ * in_range support for window functions
+ *---------------------------------------------------------
+ *
+ * Mirrors in_range_int8_int8 from int8.c.
+ * Signature: in_range(val int16, base int16, offset int16, sub bool, less bool)
+ * Returns true if val is within [base, base+offset] (or [base-offset, base]).
+ */
+Datum
+in_range_int16_int16(PG_FUNCTION_ARGS)
+{
+	int16StructP val_p = PG_GETARG_INT16_P(0);
+	int16StructP base_p = PG_GETARG_INT16_P(1);
+	int16StructP offset_p = PG_GETARG_INT16_P(2);
+	bool		sub = PG_GETARG_BOOL(3);
+	bool		less = PG_GETARG_BOOL(4);
+	INT128		val,
+				base,
+				offset,
+				sum;
+
+	memcpy(&val, val_p, INT16_LEN);
+	memcpy(&base, base_p, INT16_LEN);
+	memcpy(&offset, offset_p, INT16_LEN);
+
+	if (int128_sign(offset) < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
+				 errmsg("invalid preceding or following size in window function")));
+
+	if (sub)
+	{
+		/* Negate offset; cannot overflow since offset >= 0 */
+		if (int128_negate_overflow(offset, &offset))
+		{
+			/*
+			 * offset was 0... but negating 0 can't overflow. This shouldn't
+			 * happen. But if offset == INT128_MIN somehow (it can't since we
+			 * checked offset < 0 above and INT128_MIN < 0), handle it.
+			 */
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
+					 errmsg("invalid preceding or following size in window function")));
+		}
+	}
+
+	if (int128_add_overflow(base, offset, &sum))
+	{
+		/*
+		 * If sub is false, the true sum is surely more than val, so correct
+		 * answer is the same as "less".  If sub is true, the true sum is
+		 * surely less than val, so the answer is "!less".
+		 */
+		PG_RETURN_BOOL(sub ? !less : less);
+	}
+
+	if (less)
+		PG_RETURN_BOOL(int128_compare(val, sum) <= 0);
+	else
+		PG_RETURN_BOOL(int128_compare(val, sum) >= 0);
+}
+
+/*----------------------------------------------------------
+ * B-tree sort support
+ *---------------------------------------------------------
+ */
+
+static int
+int16_sortsupport_cmp(Datum x, Datum y, SortSupport ssup)
+{
+	INT128		a,
+				b;
+
+	memcpy(&a, DatumGetPointer(x), INT16_LEN);
+	memcpy(&b, DatumGetPointer(y), INT16_LEN);
+
+	return int128_compare(a, b);
+}
+
+Datum
+int16sortsupport(PG_FUNCTION_ARGS)
+{
+	SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
+
+	ssup->comparator = int16_sortsupport_cmp;
+	PG_RETURN_VOID();
+}
+
+/*----------------------------------------------------------
+ * B-tree skip support
+ *---------------------------------------------------------
+ */
+
+static Datum
+int16_decrement(Relation rel, Datum existing, bool *underflow)
+{
+	INT128		val;
+
+	memcpy(&val, DatumGetPointer(existing), INT16_LEN);
+
+	if (int128_compare(val, INT16_MIN_VAL) == 0)
+	{
+		/* return value is undefined */
+		*underflow = true;
+		return (Datum) 0;
+	}
+
+	*underflow = false;
+	{
+		INT128		result;
+		int16StructP r;
+
+		/* Decrement: subtract 1 */
+		int128_sub_uint64(&val, 1);
+		result = val;
+
+		r = palloc(INT16_LEN);
+		memcpy(r, &result, INT16_LEN);
+		return PointerGetDatum(r);
+	}
+}
+
+static Datum
+int16_increment(Relation rel, Datum existing, bool *overflow)
+{
+	INT128		val;
+
+	memcpy(&val, DatumGetPointer(existing), INT16_LEN);
+
+	if (int128_compare(val, INT16_MAX_VAL) == 0)
+	{
+		/* return value is undefined */
+		*overflow = true;
+		return (Datum) 0;
+	}
+
+	*overflow = false;
+	{
+		INT128		result;
+		int16StructP r;
+
+		/* Increment: add 1 */
+		int128_add_uint64(&val, 1);
+		result = val;
+
+		r = palloc(INT16_LEN);
+		memcpy(r, &result, INT16_LEN);
+		return PointerGetDatum(r);
+	}
+}
+
+Datum
+int16skipsupport(PG_FUNCTION_ARGS)
+{
+	SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
+	INT128		low_val = INT16_MIN_VAL;
+	INT128		high_val = INT16_MAX_VAL;
+	int16StructP low_p;
+	int16StructP high_p;
+
+	sksup->decrement = int16_decrement;
+	sksup->increment = int16_increment;
+
+	low_p = palloc(INT16_LEN);
+	memcpy(low_p, &low_val, INT16_LEN);
+	sksup->low_elem = PointerGetDatum(low_p);
+
+	high_p = palloc(INT16_LEN);
+	memcpy(high_p, &high_val, INT16_LEN);
+	sksup->high_elem = PointerGetDatum(high_p);
+
+	PG_RETURN_VOID();
+}
diff --git a/contrib/int16/int16_numeric.c b/contrib/int16/int16_numeric.c
new file mode 100644
index 00000000000..06154d59766
--- /dev/null
+++ b/contrib/int16/int16_numeric.c
@@ -0,0 +1,100 @@
+/*-------------------------------------------------------------------------
+ *
+ * int16_numeric.c
+ *	  Cast functions between int16 (128-bit signed integer) and numeric.
+ *
+ * The conversion approach mirrors core PostgreSQL's int8↔numeric casts:
+ *
+ *  - int16 → numeric: uses the shared int128_to_numeric() helper (string-based).
+ *  - numeric → int16: rounds to 0 decimal places (round-half-away-from-zero,
+ *    matching numeric_int8's round_var behavior), then converts via string
+ *    representation using numeric_out / int16in.
+ *
+ * NaN and Infinity numerics are rejected with ERRCODE_FEATURE_NOT_SUPPORTED,
+ * matching the core numeric_int8 behavior.
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * contrib/int16/int16_numeric.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "utils/builtins.h"
+#include "utils/numeric.h"
+#include "int16.h"
+
+PG_FUNCTION_INFO_V1(int16_numeric);
+PG_FUNCTION_INFO_V1(numeric_int16);
+
+/*
+ * int16_numeric(int16) → numeric
+ *
+ * Convert a 128-bit signed integer to numeric.
+ * This is the widening direction → IMPLICIT cast (matches int8→numeric).
+ */
+Datum
+int16_numeric(PG_FUNCTION_ARGS)
+{
+	int16StructP val = PG_GETARG_INT16_P(0);
+	INT128		v;
+
+	memcpy(&v, val, INT16_LEN);
+
+	return int128_to_numeric(v);
+}
+
+/*
+ * numeric_int16(numeric) → int16
+ *
+ * Convert a numeric to a 128-bit signed integer.
+ * This is the narrowing direction → ASSIGNMENT cast (matches numeric→int8).
+ *
+ * Behavior matches core numeric_int8():
+ *  - NaN and Infinity raise ERRCODE_FEATURE_NOT_SUPPORTED
+ *  - Fractional parts are rounded to nearest integer (round-half-away-from-zero)
+ *  - Values outside the int16 range raise ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE
+ */
+Datum
+numeric_int16(PG_FUNCTION_ARGS)
+{
+	Numeric		num = PG_GETARG_NUMERIC(0);
+	Datum		rounded;
+	char	   *str;
+	int16StructP result;
+
+	/* Reject NaN and Infinity, matching numeric_int8 behavior */
+	if (numeric_is_nan(num))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot convert NaN to %s", "int16")));
+	else if (numeric_is_inf(num))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot convert infinity to %s", "int16")));
+
+	/*
+	 * Round to 0 decimal places (round-half-away-from-zero), matching
+	 * numeric_int8's use of round_var(&rounded, 0).
+	 */
+	rounded = DirectFunctionCall2(numeric_round,
+								  NumericGetDatum(num),
+								  Int32GetDatum(0));
+
+	/* Convert the rounded numeric to a string (no exponent, plain decimal) */
+	str = DatumGetCString(DirectFunctionCall1(numeric_out, rounded));
+
+	/*
+	 * Parse the string as int16.  int16in will raise
+	 * ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE for values outside the int16 range,
+	 * and ERRCODE_INVALID_TEXT_REPRESENTATION for malformed input.
+	 */
+	result = DatumGetInt16P(DirectFunctionCall3(int16in,
+												CStringGetDatum(str),
+												ObjectIdGetDatum(InvalidOid),
+												Int32GetDatum(-1)));
+
+	PG_RETURN_INT16_P(result);
+}
diff --git a/contrib/int16/meson.build b/contrib/int16/meson.build
new file mode 100644
index 00000000000..6e877f5d47c
--- /dev/null
+++ b/contrib/int16/meson.build
@@ -0,0 +1,54 @@
+# Copyright (c) 2022-2026, PostgreSQL Global Development Group
+
+int16_sources = files(
+    'int16.c',
+    'int16_numeric.c',
+    'int16_funcs.c',
+)
+
+int16 = shared_module('int16',
+    int16_sources,
+    kwargs: pg_mod_args,
+)
+
+install_data(
+    'int16--1.0.sql',
+    kwargs: install_data_args,
+)
+
+install_data(
+    'int16.control',
+    kwargs: install_data_args,
+)
+
+# Regression tests
+int16_tests = [
+    'init',
+    'int16',
+    'int16_overflow',
+    'int16_numeric',
+    'int16_funcs',
+]
+
+pg_test_config = {
+    'testname': 'int16',
+    'tests': int16_tests,
+}
+
+configure_file(
+    input: 'meson.build.in',
+    output: 'meson.build',
+    copy: true,
+    configuration: pg_test_config,
+)
+
+tests += {
+    'name': 'int16',
+    'sd': meson.current_source_dir(),
+    'bd': meson.current_build_dir(),
+    'regress': {
+        'sql': files('sql', 'int16.sql'),
+        'expected': files('expected', 'int16.out'),
+        'schedule': files('parallel_schedule'),
+    },
+}
diff --git a/contrib/int16/sql/init.sql b/contrib/int16/sql/init.sql
new file mode 100644
index 00000000000..5a7a6cc6411
--- /dev/null
+++ b/contrib/int16/sql/init.sql
@@ -0,0 +1 @@
+CREATE EXTENSION int16;
diff --git a/contrib/int16/sql/int16.sql b/contrib/int16/sql/int16.sql
new file mode 100644
index 00000000000..ed44960608a
--- /dev/null
+++ b/contrib/int16/sql/int16.sql
@@ -0,0 +1,187 @@
+-- Test the int16 (128-bit signed integer) extension
+
+--
+-- I/O tests
+--
+SELECT '0'::int16;
+SELECT '1'::int16;
+SELECT '-1'::int16;
+SELECT '42'::int16;
+SELECT '-42'::int16;
+SELECT '9223372036854775807'::int16;  -- INT8_MAX
+SELECT '-9223372036854775808'::int16; -- INT8_MIN
+SELECT '170141183460469231731687303715884105727'::int16;  -- INT16_MAX
+SELECT '-170141183460469231731687303715884105728'::int16; -- INT16_MIN
+SELECT '  123  '::int16;  -- whitespace
+SELECT '+42'::int16;
+
+-- Invalid input
+SELECT ''::int16;
+SELECT 'abc'::int16;
+SELECT '1.5'::int16;
+SELECT '1 2'::int16;
+SELECT '170141183460469231731687303715884105728'::int16;  -- overflow (MAX+1)
+SELECT '-170141183460469231731687303715884105729'::int16; -- overflow (MIN-1)
+
+--
+-- Arithmetic: int16 vs int16
+--
+SELECT '100'::int16 + '50'::int16;
+SELECT '100'::int16 - '50'::int16;
+SELECT '100'::int16 * '50'::int16;
+SELECT '100'::int16 / '50'::int16;
+SELECT '100'::int16 % '30'::int16;
+SELECT -'42'::int16;
+SELECT +'42'::int16;
+SELECT @('-42'::int16);
+
+-- Overflow on arithmetic
+SELECT '170141183460469231731687303715884105727'::int16 + '1'::int16;
+SELECT '-170141183460469231731687303715884105728'::int16 - '1'::int16;
+SELECT '85070591730234615865843651857942052864'::int16 * '3'::int16;
+SELECT '-170141183460469231731687303715884105728'::int16 / '-1'::int16;
+
+-- Division by zero
+SELECT '1'::int16 / '0'::int16;
+SELECT '1'::int16 % '0'::int16;
+
+--
+-- Comparison: int16 vs int16
+--
+SELECT '1'::int16 = '1'::int16;
+SELECT '1'::int16 = '2'::int16;
+SELECT '1'::int16 <> '2'::int16;
+SELECT '1'::int16 < '2'::int16;
+SELECT '2'::int16 > '1'::int16;
+SELECT '1'::int16 <= '1'::int16;
+SELECT '1'::int16 >= '1'::int16;
+SELECT int16_cmp('5'::int16, '3'::int16);
+SELECT int16_cmp('3'::int16, '5'::int16);
+SELECT int16_cmp('5'::int16, '5'::int16);
+
+--
+-- Bitwise operators
+--
+SELECT '255'::int16 & '15'::int16;
+SELECT '240'::int16 | '15'::int16;
+SELECT '255'::int16 # '15'::int16;
+SELECT ~'0'::int16;
+SELECT '1'::int16 << 4;
+SELECT '256'::int16 >> 4;
+SELECT '-1'::int16 >> 1;  -- arithmetic shift
+
+--
+-- Cross-type arithmetic: int16 op int8
+--
+SELECT '100'::int16 + '50'::int8;
+SELECT '50'::int8 + '100'::int16;
+SELECT '100'::int16 - '50'::int8;
+SELECT '50'::int8 - '100'::int16;
+SELECT '100'::int16 * '50'::int8;
+SELECT '50'::int8 * '100'::int16;
+SELECT '100'::int16 / '50'::int8;
+SELECT '50'::int8 / '100'::int16;
+
+--
+-- Cross-type arithmetic: int16 op int4
+--
+SELECT '100'::int16 + 50;
+SELECT 50 + '100'::int16;
+SELECT '100'::int16 - 50;
+SELECT 50 - '100'::int16;
+SELECT '100'::int16 * 50;
+SELECT 50 * '100'::int16;
+SELECT '100'::int16 / 50;
+SELECT 50 / '100'::int16;
+
+--
+-- Cross-type arithmetic: int16 op int2
+--
+SELECT '100'::int16 + 50::int2;
+SELECT 50::int2 + '100'::int16;
+SELECT '100'::int16 - 50::int2;
+SELECT 50::int2 - '100'::int16;
+SELECT '100'::int16 * 50::int2;
+SELECT 50::int2 * '100'::int16;
+SELECT '100'::int16 / 50::int2;
+SELECT 50::int2 / '100'::int16;
+
+--
+-- Cross-type comparison
+--
+SELECT '100'::int16 = '100'::int8;
+SELECT '100'::int8 = '100'::int16;
+SELECT '100'::int16 < '101'::int8;
+SELECT '101'::int8 > '100'::int16;
+SELECT '100'::int16 = 100;
+SELECT 100 = '100'::int16;
+SELECT '100'::int16 < 101;
+SELECT 101 > '100'::int16;
+SELECT '100'::int16 = 100::int2;
+SELECT 100::int2 = '100'::int16;
+
+--
+-- Casts
+--
+SELECT '9223372036854775807'::int8::int16;
+SELECT '-9223372036854775808'::int8::int16;
+SELECT '100'::int16::int8;
+SELECT '100'::int4::int16;
+SELECT '100'::int16::int4;
+SELECT '100'::int2::int16;
+SELECT '100'::int16::int2;
+
+-- Cast overflow
+SELECT '170141183460469231731687303715884105727'::int16::int8;
+SELECT '2147483648'::int16::int4;
+SELECT '32768'::int16::int2;
+
+--
+-- Aggregates
+--
+CREATE TABLE int16_test (x int16);
+INSERT INTO int16_test VALUES ('1'), ('2'), ('3'), ('4'), ('5'), (NULL);
+SELECT sum(x) FROM int16_test;
+SELECT avg(x) FROM int16_test;
+SELECT min(x) FROM int16_test;
+SELECT max(x) FROM int16_test;
+SELECT sum(x) FROM int16_test WHERE x IS NOT NULL;
+
+-- Sum overflow
+INSERT INTO int16_test VALUES ('170141183460469231731687303715884105727');
+SELECT sum(x) FROM int16_test;
+-- Clean up the overflow row
+DELETE FROM int16_test WHERE x = '170141183460469231731687303715884105727'::int16;
+
+-- Empty table
+DELETE FROM int16_test;
+SELECT sum(x) FROM int16_test;
+SELECT avg(x) FROM int16_test;
+SELECT min(x) FROM int16_test;
+SELECT max(x) FROM int16_test;
+
+DROP TABLE int16_test;
+
+--
+-- B-tree index test
+--
+CREATE TABLE int16_bt (x int16);
+INSERT INTO int16_bt VALUES ('5'), ('3'), ('8'), ('1'), ('9'), ('2'), ('7'), ('4'), ('6');
+CREATE INDEX idx_int16_bt ON int16_bt USING btree (x);
+SET enable_seqscan = off;
+SELECT * FROM int16_bt WHERE x > '4'::int16 ORDER BY x;
+SELECT * FROM int16_bt WHERE x <= '3'::int16 ORDER BY x;
+SELECT * FROM int16_bt WHERE x = '7'::int16;
+RESET enable_seqscan;
+DROP TABLE int16_bt;
+
+--
+-- Hash index test
+--
+CREATE TABLE int16_hash (x int16);
+INSERT INTO int16_hash VALUES ('5'), ('3'), ('8'), ('1'), ('9');
+CREATE INDEX idx_int16_hash ON int16_hash USING hash (x);
+SET enable_seqscan = off;
+SELECT * FROM int16_hash WHERE x = '8'::int16;
+RESET enable_seqscan;
+DROP TABLE int16_hash;
diff --git a/contrib/int16/sql/int16_funcs.sql b/contrib/int16/sql/int16_funcs.sql
new file mode 100644
index 00000000000..229a536670e
--- /dev/null
+++ b/contrib/int16/sql/int16_funcs.sql
@@ -0,0 +1,216 @@
+--
+-- int16_funcs.sql
+--
+-- Regression tests for additional int16 functions: gcd, lcm, factorial,
+-- generate_series, in_range (window support), and btree support functions.
+--
+
+--
+-- gcd
+--
+
+-- Basic cases
+SELECT gcd('12'::int16, '8'::int16);   -- 4
+SELECT gcd('17'::int16, '5'::int16);   -- 1 (coprime)
+SELECT gcd('100'::int16, '10'::int16); -- 10
+SELECT gcd('48'::int16, '36'::int16);  -- 12
+
+-- gcd with zero
+SELECT gcd('0'::int16, '5'::int16);    -- 5
+SELECT gcd('5'::int16, '0'::int16);    -- 5
+SELECT gcd('0'::int16, '0'::int16);    -- 0
+
+-- gcd with negatives
+SELECT gcd('-12'::int16, '8'::int16);  -- 4
+SELECT gcd('12'::int16, '-8'::int16);  -- 4
+SELECT gcd('-12'::int16, '-8'::int16); -- 4
+
+-- gcd with 1
+SELECT gcd('1'::int16, '1'::int16);    -- 1
+SELECT gcd('100'::int16, '1'::int16);  -- 1
+
+-- gcd with same value
+SELECT gcd('42'::int16, '42'::int16);  -- 42
+
+-- gcd(INT128_MIN, -1) = 1 (special case)
+SELECT gcd('-170141183460469231731687303715884105728'::int16, '-1'::int16); -- 1
+
+-- gcd(INT128_MIN, 0) should error (abs overflow)
+SELECT gcd('-170141183460469231731687303715884105728'::int16, '0'::int16);
+
+-- gcd(INT128_MIN, INT128_MIN) should error (abs overflow)
+SELECT gcd('-170141183460469231731687303715884105728'::int16,
+           '-170141183460469231731687303715884105728'::int16);
+
+--
+-- lcm
+--
+
+-- Basic cases
+SELECT lcm('4'::int16, '6'::int16);    -- 12
+SELECT lcm('3'::int16, '5'::int16);    -- 15 (coprime)
+SELECT lcm('12'::int16, '8'::int16);   -- 24
+SELECT lcm('10'::int16, '10'::int16);  -- 10
+
+-- lcm with zero
+SELECT lcm('0'::int16, '5'::int16);    -- 0
+SELECT lcm('5'::int16, '0'::int16);    -- 0
+SELECT lcm('0'::int16, '0'::int16);    -- 0
+
+-- lcm with negatives
+SELECT lcm('-4'::int16, '6'::int16);   -- 12
+SELECT lcm('4'::int16, '-6'::int16);   -- 12
+SELECT lcm('-4'::int16, '-6'::int16);  -- 12
+
+-- lcm with 1
+SELECT lcm('1'::int16, '7'::int16);    -- 7
+
+-- lcm overflow
+SELECT lcm('170141183460469231731687303715884105727'::int16, '2'::int16);
+
+--
+-- factorial
+--
+
+SELECT factorial('0'::int16);  -- 1
+SELECT factorial('1'::int16);  -- 1
+SELECT factorial('2'::int16);  -- 2
+SELECT factorial('5'::int16);  -- 120
+SELECT factorial('10'::int16); -- 3628800
+SELECT factorial('20'::int16); -- 2432902008176640000
+
+-- factorial of negative number (error)
+SELECT factorial('-1'::int16);
+
+--
+-- generate_series
+--
+
+-- Default step (1)
+SELECT * FROM generate_series('1'::int16, '5'::int16);
+
+-- Positive step
+SELECT * FROM generate_series('1'::int16, '10'::int16, '2'::int16);
+
+-- Negative step (counting down)
+SELECT * FROM generate_series('5'::int16, '1'::int16, '-1'::int16);
+
+-- Step of 0 (error)
+SELECT * FROM generate_series('1'::int16, '5'::int16, '0'::int16);
+
+-- Empty range (start > stop with positive step)
+SELECT * FROM generate_series('5'::int16, '1'::int16);
+
+-- Single element
+SELECT * FROM generate_series('3'::int16, '3'::int16);
+
+-- Large step
+SELECT * FROM generate_series('0'::int16, '100000000000000000000'::int16,
+                              '100000000000000000000'::int16);
+
+-- generate_series in a subquery
+SELECT count(*) FROM generate_series('1'::int16, '100'::int16);
+
+-- generate_series with large values near INT128_MAX
+SELECT * FROM generate_series(
+    '170141183460469231731687303715884105725'::int16,
+    '170141183460469231731687303715884105727'::int16
+);
+
+--
+-- in_range (window function support)
+--
+
+-- Basic in_range tests (direct function call)
+SELECT in_range('5'::int16, '0'::int16, '10'::int16, false, true);  -- true (5 <= 0+10)
+SELECT in_range('15'::int16, '0'::int16, '10'::int16, false, true); -- false (15 > 10)
+SELECT in_range('5'::int16, '0'::int16, '10'::int16, false, false); -- true (5 >= 0+10? no) false
+
+-- in_range with sub=true (preceding)
+SELECT in_range('5'::int16, '10'::int16, '3'::int16, true, true);   -- true (5 <= 10-3=7)
+
+-- Negative offset (error)
+SELECT in_range('5'::int16, '0'::int16, '-1'::int16, false, true);
+
+-- Window function using RANGE with int16
+CREATE TABLE int16_window_test (id int4, val int16);
+INSERT INTO int16_window_test VALUES
+    (1, '10'::int16),
+    (2, '20'::int16),
+    (3, '30'::int16),
+    (4, '40'::int16),
+    (5, '50'::int16);
+
+-- RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING
+SELECT id, val, count(*) OVER (
+    ORDER BY val
+    RANGE BETWEEN '10'::int16 PRECEDING AND '10'::int16 FOLLOWING
+) AS cnt
+FROM int16_window_test
+ORDER BY id;
+
+-- RANGE BETWEEN 15 PRECEDING AND 5 FOLLOWING
+SELECT id, val, sum(val) OVER (
+    ORDER BY val
+    RANGE BETWEEN '15'::int16 PRECEDING AND '5'::int16 FOLLOWING
+) AS range_sum
+FROM int16_window_test
+ORDER BY id;
+
+DROP TABLE int16_window_test;
+
+--
+-- B-tree operator class support functions (sort support, equalimage, skip support)
+--
+
+-- Verify btree index creation and queries work
+CREATE TABLE int16_btree_test (val int16);
+INSERT INTO int16_btree_test VALUES
+    ('5'::int16), ('3'::int16), ('8'::int16), ('1'::int16), ('9'::int16);
+
+CREATE INDEX idx_int16_btree ON int16_btree_test USING btree (val);
+
+-- Query using the index
+SET enable_seqscan = off;
+EXPLAIN (COSTS OFF) SELECT * FROM int16_btree_test WHERE val = '5'::int16;
+SELECT * FROM int16_btree_test WHERE val = '5'::int16;
+
+-- Ordered query
+SELECT * FROM int16_btree_test ORDER BY val;
+
+-- Deduplication (btequalimage) - create table with duplicates and verify
+CREATE TABLE int16_dedup_test (val int16);
+INSERT INTO int16_dedup_test VALUES
+    ('5'::int16), ('5'::int16), ('5'::int16), ('3'::int16), ('3'::int16);
+
+CREATE INDEX idx_int16_dedup ON int16_dedup_test USING btree (val);
+
+-- Verify deduplication is working (index should have fewer entries)
+SELECT count(*) FROM int16_dedup_test;
+
+SET enable_seqscan = off;
+SELECT * FROM int16_dedup_test WHERE val = '5'::int16;
+SET enable_seqscan = on;
+
+DROP TABLE int16_dedup_test;
+
+-- Skip scan test: query on a column with a leading skipped column
+CREATE TABLE int16_skip_test (grp int16, val int4);
+INSERT INTO int16_skip_test VALUES
+    ('1'::int16, 10),
+    ('1'::int16, 20),
+    ('2'::int16, 30),
+    ('2'::int16, 40),
+    ('3'::int16, 50);
+
+CREATE INDEX idx_int16_skip ON int16_skip_test (grp, val);
+
+-- DISTINCT on grp should be able to use skip scan
+SET enable_seqscan = off;
+EXPLAIN (COSTS OFF) SELECT DISTINCT grp FROM int16_skip_test ORDER BY grp;
+SELECT DISTINCT grp FROM int16_skip_test ORDER BY grp;
+SET enable_seqscan = on;
+
+DROP TABLE int16_skip_test;
+
+DROP TABLE int16_btree_test;
diff --git a/contrib/int16/sql/int16_numeric.sql b/contrib/int16/sql/int16_numeric.sql
new file mode 100644
index 00000000000..f3118b9de30
--- /dev/null
+++ b/contrib/int16/sql/int16_numeric.sql
@@ -0,0 +1,179 @@
+--
+-- int16_numeric.sql
+--
+-- Regression tests for casts between int16 (128-bit signed integer) and numeric.
+-- Tests cover: round-trip conversions, edge cases (0, ±1, MIN, MAX, powers of
+-- 10), fractional rounding behavior, NaN/Infinity rejection, overflow detection,
+-- NULL handling, and cast usage in INSERT/SELECT contexts.
+--
+
+--
+-- int16 → numeric cast (implicit)
+--
+
+-- Basic values
+SELECT '0'::int16::numeric;
+SELECT '1'::int16::numeric;
+SELECT '-1'::int16::numeric;
+SELECT '42'::int16::numeric;
+SELECT '-42'::int16::numeric;
+
+-- INT128_MAX and INT128_MIN
+SELECT '170141183460469231731687303715884105727'::int16::numeric;  -- INT16_MAX
+SELECT '-170141183460469231731687303715884105728'::int16::numeric; -- INT16_MIN
+
+-- Powers of 10 (testing digit boundaries)
+SELECT '1000000000000000000'::int16::numeric;      -- 10^18
+SELECT '10000000000000000000'::int16::numeric;     -- 10^19
+SELECT '100000000000000000000000000000000000000'::int16::numeric; -- 10^38
+
+-- Values around int64 boundaries
+SELECT '9223372036854775807'::int16::numeric;  -- INT64_MAX
+SELECT '-9223372036854775808'::int16::numeric; -- INT64_MIN
+SELECT '9223372036854775808'::int16::numeric;  -- INT64_MAX + 1
+SELECT '-9223372036854775809'::int16::numeric; -- INT64_MIN - 1
+
+-- 2^64
+SELECT '18446744073709551616'::int16::numeric;
+SELECT '-18446744073709551616'::int16::numeric;
+
+-- Use int16_numeric() function directly
+SELECT int16_numeric('123'::int16);
+SELECT int16_numeric('-456'::int16);
+
+--
+-- numeric → int16 cast (assignment)
+--
+
+-- Basic values
+SELECT '0'::numeric::int16;
+SELECT '1'::numeric::int16;
+SELECT '-1'::numeric::int16;
+SELECT '42'::numeric::int16;
+SELECT '-42'::numeric::int16;
+
+-- INT128_MAX and INT128_MIN
+SELECT '170141183460469231731687303715884105727'::numeric::int16;  -- INT16_MAX
+SELECT '-170141183460469231731687303715884105728'::numeric::int16; -- INT16_MIN
+
+-- Powers of 10
+SELECT '1000000000000000000'::numeric::int16;      -- 10^18
+SELECT '10000000000000000000'::numeric::int16;     -- 10^19
+SELECT '100000000000000000000000000000000000000'::numeric::int16; -- 10^38
+
+-- Use numeric_int16() function directly
+SELECT numeric_int16('789'::numeric);
+SELECT numeric_int16('-321'::numeric);
+
+--
+-- Fractional rounding (matches int8 behavior: round-half-away-from-zero)
+--
+
+SELECT '1.4'::numeric::int16;   -- rounds to 1
+SELECT '1.5'::numeric::int16;   -- rounds to 2
+SELECT '1.6'::numeric::int16;   -- rounds to 2
+SELECT '-1.4'::numeric::int16;  -- rounds to -1
+SELECT '-1.5'::numeric::int16;  -- rounds to -2
+SELECT '-1.6'::numeric::int16;  -- rounds to -2
+SELECT '2.5'::numeric::int16;   -- rounds to 3 (round half away from zero)
+SELECT '-2.5'::numeric::int16;  -- rounds to -3
+SELECT '0.4'::numeric::int16;   -- rounds to 0
+SELECT '0.5'::numeric::int16;   -- rounds to 1
+SELECT '-0.5'::numeric::int16;  -- rounds to -1
+SELECT '99999999999999999999.5'::numeric::int16;  -- rounds to 10^20
+
+--
+-- Round-trip: int16 → numeric → int16
+--
+
+SELECT '170141183460469231731687303715884105727'::int16::numeric::int16;  -- MAX
+SELECT '-170141183460469231731687303715884105728'::int16::numeric::int16; -- MIN
+SELECT '9223372036854775807'::int16::numeric::int16;  -- INT64_MAX
+SELECT '18446744073709551616'::int16::numeric::int16; -- 2^64
+SELECT '123456789012345678901234567890'::int16::numeric::int16;
+
+--
+-- Round-trip: numeric → int16 → numeric
+--
+
+SELECT '170141183460469231731687303715884105727'::numeric::int16::numeric;
+SELECT '-170141183460469231731687303715884105728'::numeric::int16::numeric;
+SELECT '99999999999999999999999999999999'::numeric::int16::numeric;
+
+--
+-- NaN and Infinity (should error, matching int8 behavior)
+--
+
+SELECT 'NaN'::numeric::int16;
+SELECT 'Infinity'::numeric::int16;
+SELECT '-Infinity'::numeric::int16;
+
+--
+-- Overflow: values outside the int16 range (should error)
+--
+
+SELECT '170141183460469231731687303715884105728'::numeric::int16;  -- MAX + 1
+SELECT '-170141183460469231731687303715884105729'::numeric::int16; -- MIN - 1
+SELECT '999999999999999999999999999999999999999999999999'::numeric::int16; -- way too big
+
+--
+-- NULL handling
+--
+
+SELECT NULL::int16::numeric;
+SELECT NULL::numeric::int16;
+
+--
+-- Cast usage in INSERT/SELECT
+--
+
+CREATE TABLE int16_numeric_test (
+    id serial PRIMARY KEY,
+    big_val int16,
+    num_val numeric
+);
+
+-- Insert int16 values, cast to numeric on retrieval
+INSERT INTO int16_numeric_test (big_val) VALUES
+    ('1'),
+    ('-1'),
+    ('170141183460469231731687303715884105727'),
+    ('-170141183460469231731687303715884105728'),
+    ('9223372036854775807');
+
+SELECT id, big_val::numeric FROM int16_numeric_test ORDER BY id;
+
+-- Insert numeric values, cast to int16
+INSERT INTO int16_numeric_test (num_val) VALUES
+    ('100'),
+    ('-200'),
+    ('99999999999999999999999999999999');
+
+SELECT id, num_val::int16 FROM int16_numeric_test WHERE num_val IS NOT NULL ORDER BY id;
+
+-- Use cast in WHERE clause comparison
+SELECT id, big_val FROM int16_numeric_test
+WHERE big_val::numeric > '0'::numeric
+ORDER BY id;
+
+-- Use cast in arithmetic
+SELECT '100'::int16::numeric + '50'::numeric AS sum_result;
+SELECT '100'::numeric::int16 + '50'::int16 AS sum_int16;
+
+-- Drop the test table
+DROP TABLE int16_numeric_test;
+
+--
+-- Comparison of cast results with expected values
+--
+
+-- int16 values cast to numeric should compare equal
+SELECT '170141183460469231731687303715884105727'::int16::numeric =
+       '170141183460469231731687303715884105727'::numeric AS max_equal;
+
+SELECT '-170141183460469231731687303715884105728'::int16::numeric =
+       '-170141183460469231731687303715884105728'::numeric AS min_equal;
+
+-- Numeric cast to int16 should compare equal
+SELECT '123456789012345678901234567890'::numeric::int16 =
+       '123456789012345678901234567890'::int16 AS roundtrip_equal;
diff --git a/contrib/int16/sql/int16_overflow.sql b/contrib/int16/sql/int16_overflow.sql
new file mode 100644
index 00000000000..c0b50f65518
--- /dev/null
+++ b/contrib/int16/sql/int16_overflow.sql
@@ -0,0 +1,257 @@
+--
+-- int16_overflow.sql
+--
+-- Regression tests for the int16 (128-bit signed integer) extension focusing
+-- on values that exceed the int64 range, overflow detection, boundary
+-- conditions, and sign combinations.  These tests are designed to verify
+-- correctness of both the native __int128 and the struct-based INT128
+-- fallback code paths.
+--
+-- This file assumes the extension is already loaded (by the preceding
+-- int16.sql test).
+--
+
+--
+-- Constants for readability
+--
+-- INT64_MAX  = 9223372036854775807
+-- INT64_MIN  = -9223372036854775808
+-- 2^63       = 9223372036854775808
+-- 2^63+1     = 9223372036854775809
+-- 2^64       = 18446744073709551616
+-- 2^126      = 85070591730234615865843651857942052864
+-- INT16_MAX  = 170141183460469231731687303715884105727
+-- INT16_MIN  = -170141183460469231731687303715884105728
+
+-- =========================================================================
+-- Addition
+-- =========================================================================
+SELECT '9223372036854775808'::int16 + '9223372036854775808'::int16;  -- 2^63 + 2^63 = 2^64
+SELECT '-9223372036854775809'::int16 + '-9223372036854775809'::int16; -- -(2^63+1) + -(2^63+1)
+SELECT '170141183460469231731687303715884105727'::int16 + '-170141183460469231731687303715884105728'::int16; -- MAX + MIN
+SELECT '170141183460469231731687303715884105727'::int16 + '1'::int16; -- overflow
+SELECT '-170141183460469231731687303715884105728'::int16 + '-1'::int16; -- overflow
+SELECT '18446744073709551616'::int16 + '0'::int16; -- 2^64 + 0
+SELECT '-18446744073709551616'::int16 + '18446744073709551616'::int16; -- -2^64 + 2^64
+SELECT '9223372036854775807'::int16 + '9223372036854775808'::int16; -- INT64_MAX + 2^63
+SELECT '-9223372036854775808'::int16 + '-9223372036854775808'::int16; -- INT64_MIN + INT64_MIN
+SELECT '85070591730234615865843651857942052864'::int16 + '85070591730234615865843651857942052864'::int16; -- 2^126 + 2^126 (overflow)
+
+-- =========================================================================
+-- Subtraction
+-- =========================================================================
+SELECT '-9223372036854775809'::int16 - '9223372036854775808'::int16; -- -(2^63+1) - 2^63
+SELECT '9223372036854775808'::int16 - '-9223372036854775809'::int16; -- 2^63 - -(2^63+1)
+SELECT '170141183460469231731687303715884105727'::int16 - '-1'::int16; -- MAX - (-1) = overflow
+SELECT '-170141183460469231731687303715884105728'::int16 - '1'::int16; -- MIN - 1 = overflow
+SELECT '18446744073709551616'::int16 - '1'::int16; -- 2^64 - 1
+SELECT '0'::int16 - '-18446744073709551616'::int16; -- 0 - (-2^64)
+SELECT '170141183460469231731687303715884105727'::int16 - '170141183460469231731687303715884105726'::int16; -- MAX - (MAX-1)
+SELECT '-170141183460469231731687303715884105727'::int16 - '-170141183460469231731687303715884105728'::int16; -- -(MIN+1) - MIN
+SELECT '9223372036854775808'::int16 - '9223372036854775807'::int16; -- 2^63 - INT64_MAX
+SELECT '-9223372036854775809'::int16 - '-9223372036854775808'::int16; -- -(2^63+1) - INT64_MIN
+
+-- =========================================================================
+-- Multiplication
+-- =========================================================================
+SELECT '3000000000'::int16 * '3000000000'::int16;  -- 9e18 > INT64_MAX
+SELECT '-3000000000'::int16 * '3000000000'::int16; -- -9e18
+SELECT '3000000000'::int16 * '-3000000000'::int16; -- -9e18
+SELECT '-3000000000'::int16 * '-3000000000'::int16; -- 9e18
+SELECT '170141183460469231731687303715884105727'::int16 * '2'::int16; -- overflow
+SELECT '18446744073709551616'::int16 * '18446744073709551616'::int16; -- 2^64 * 2^64 overflow
+SELECT '4294967296'::int16 * '4294967296'::int16;  -- 2^32 * 2^32 = 2^64
+SELECT '-4294967296'::int16 * '4294967296'::int16; -- -2^64
+SELECT '9223372036854775807'::int16 * '9223372036854775807'::int16; -- INT64_MAX^2
+SELECT '0'::int16 * '18446744073709551616'::int16; -- 0 * 2^64
+SELECT '18446744073709551616'::int16 * '1'::int16; -- 2^64 * 1
+SELECT '18446744073709551615'::int16 * '2'::int16; -- (2^64-1) * 2
+
+-- =========================================================================
+-- Division
+-- =========================================================================
+SELECT '170141183460469231731687303715884105727'::int16 / '170141183460469231731687303715884105727'::int16; -- MAX / MAX = 1
+SELECT '170141183460469231731687303715884105727'::int16 / '-1'::int16; -- MAX / -1 = -MAX
+SELECT '-170141183460469231731687303715884105728'::int16 / '-1'::int16; -- MIN / -1 = overflow
+SELECT '170141183460469231731687303715884105727'::int16 / '2'::int16; -- MAX / 2
+SELECT '-170141183460469231731687303715884105728'::int16 / '2'::int16; -- MIN / 2
+SELECT '18446744073709551616'::int16 / '9223372036854775808'::int16; -- 2^64 / 2^63 = 2
+SELECT '-18446744073709551616'::int16 / '9223372036854775808'::int16; -- -2^64 / 2^63 = -2
+SELECT '18446744073709551616'::int16 / '-9223372036854775808'::int16; -- 2^64 / -2^63 = -2
+SELECT '-18446744073709551616'::int16 / '-9223372036854775808'::int16; -- -2^64 / -2^63 = 2
+SELECT '170141183460469231731687303715884105727'::int16 / '9223372036854775807'::int16; -- MAX / INT64_MAX
+SELECT '1'::int16 / '0'::int16;  -- division by zero
+
+-- =========================================================================
+-- Remainder (modulo)
+-- =========================================================================
+SELECT '170141183460469231731687303715884105727'::int16 % '10'::int16;  -- MAX % 10
+SELECT '-170141183460469231731687303715884105728'::int16 % '10'::int16; -- MIN % 10
+SELECT '170141183460469231731687303715884105727'::int16 % '-10'::int16;  -- MAX % -10
+SELECT '-170141183460469231731687303715884105728'::int16 % '-10'::int16; -- MIN % -10
+SELECT '18446744073709551616'::int16 % '9223372036854775808'::int16; -- 2^64 % 2^63 = 0
+SELECT '18446744073709551617'::int16 % '9223372036854775808'::int16; -- (2^64+1) % 2^63 = 1
+SELECT '-18446744073709551617'::int16 % '9223372036854775808'::int16; -- -(2^64+1) % 2^63 = -1
+SELECT '18446744073709551617'::int16 % '-9223372036854775808'::int16; -- (2^64+1) % -2^63 = 1
+SELECT '-18446744073709551617'::int16 % '-9223372036854775808'::int16; -- -(2^64+1) % -2^63 = -1
+SELECT '1'::int16 % '0'::int16;  -- division by zero
+
+-- =========================================================================
+-- Unary minus
+-- =========================================================================
+SELECT -'170141183460469231731687303715884105727'::int16;  -- -MAX
+SELECT -'-170141183460469231731687303715884105728'::int16; -- -MIN = overflow
+SELECT -'9223372036854775808'::int16;  -- -2^63
+SELECT -'-9223372036854775808'::int16; -- 2^63
+SELECT -'18446744073709551616'::int16; -- -2^64
+
+-- =========================================================================
+-- Bitwise AND
+-- =========================================================================
+SELECT '18446744073709551616'::int16 & '18446744073709551616'::int16; -- 2^64 & 2^64
+SELECT '18446744073709551616'::int16 & '1'::int16; -- 2^64 & 1 = 0
+SELECT '18446744073709551617'::int16 & '1'::int16; -- 2^64+1 & 1 = 1
+SELECT '-1'::int16 & '18446744073709551616'::int16; -- -1 & 2^64 = 2^64
+SELECT '-1'::int16 & '0'::int16; -- -1 & 0 = 0
+SELECT '170141183460469231731687303715884105727'::int16 & '0'::int16; -- MAX & 0
+SELECT '-170141183460469231731687303715884105728'::int16 & '0'::int16; -- MIN & 0
+
+-- =========================================================================
+-- Bitwise OR
+-- =========================================================================
+SELECT '18446744073709551616'::int16 | '1'::int16; -- 2^64 | 1 = 2^64+1
+SELECT '18446744073709551616'::int16 | '0'::int16; -- 2^64 | 0 = 2^64
+SELECT '0'::int16 | '18446744073709551616'::int16; -- 0 | 2^64 = 2^64
+SELECT '-1'::int16 | '0'::int16; -- -1 | 0 = -1
+SELECT '18446744073709551615'::int16 | '1'::int16; -- (2^64-1) | 1 = 2^64-1
+
+-- =========================================================================
+-- Bitwise XOR
+-- =========================================================================
+SELECT '18446744073709551616'::int16 # '1'::int16; -- 2^64 # 1 = 2^64+1
+SELECT '18446744073709551616'::int16 # '18446744073709551616'::int16; -- 2^64 # 2^64 = 0
+SELECT '0'::int16 # '18446744073709551616'::int16; -- 0 # 2^64 = 2^64
+SELECT '-1'::int16 # '0'::int16; -- -1 # 0 = -1
+SELECT '-1'::int16 # '18446744073709551616'::int16; -- -1 # 2^64 = -(2^64+1)
+SELECT '-1'::int16 # '-1'::int16; -- -1 # -1 = 0
+
+-- =========================================================================
+-- Bitwise NOT
+-- =========================================================================
+SELECT ~'0'::int16; -- ~0 = -1
+SELECT ~'-1'::int16; -- ~(-1) = 0
+SELECT ~'18446744073709551616'::int16; -- ~(2^64)
+SELECT ~'170141183460469231731687303715884105727'::int16; -- ~MAX = MIN
+SELECT ~'-170141183460469231731687303715884105728'::int16; -- ~MIN = MAX
+
+-- =========================================================================
+-- Left shift (shifts 0-127 only; behavior matches int8: raw C shift)
+-- =========================================================================
+SELECT '1'::int16 << 0;   -- 1
+SELECT '1'::int16 << 1;   -- 2
+SELECT '1'::int16 << 64;  -- 2^64
+SELECT '1'::int16 << 127; -- 2^127 = INT16_MIN
+SELECT '18446744073709551616'::int16 << 1;  -- 2^64 << 1 = 2^65
+SELECT '18446744073709551616'::int16 << 63; -- 2^64 << 63 = 2^127 = INT16_MIN
+SELECT '-1'::int16 << 1;  -- -1 << 1 = -2
+SELECT '-1'::int16 << 0;  -- -1 << 0 = -1
+
+-- =========================================================================
+-- Right shift (shifts 0-127 only; arithmetic shift for negative values)
+-- =========================================================================
+SELECT '-1'::int16 >> 1;  -- -1 >> 1 = -1 (arithmetic)
+SELECT '-1'::int16 >> 64; -- -1 >> 64 = -1 (arithmetic)
+SELECT '18446744073709551616'::int16 >> 1;  -- 2^64 >> 1 = 2^63
+SELECT '18446744073709551616'::int16 >> 64; -- 2^64 >> 64 = 1
+SELECT '18446744073709551616'::int16 >> 0;  -- 2^64 >> 0 = 2^64
+SELECT '170141183460469231731687303715884105727'::int16 >> 1; -- MAX >> 1
+SELECT '-170141183460469231731687303715884105728'::int16 >> 1; -- MIN >> 1
+SELECT '-170141183460469231731687303715884105728'::int16 >> 64; -- MIN >> 64
+SELECT '0'::int16 >> 10; -- 0 >> 10 = 0
+
+-- =========================================================================
+-- Cross-type arithmetic (minimal: one test per operator per type)
+-- =========================================================================
+
+-- int16 op int8
+SELECT '18446744073709551616'::int16 + '1'::int8;
+SELECT '1'::int8 + '18446744073709551616'::int16;
+SELECT '18446744073709551616'::int16 - '1'::int8;
+SELECT '1'::int8 - '18446744073709551616'::int16;
+SELECT '18446744073709551616'::int16 * '2'::int8;
+SELECT '2'::int8 * '18446744073709551616'::int16;
+SELECT '18446744073709551616'::int16 / '2'::int8;
+SELECT '2'::int8 / '18446744073709551616'::int16;
+
+-- int16 op int4
+SELECT '18446744073709551616'::int16 + 1;
+SELECT 1 + '18446744073709551616'::int16;
+SELECT '18446744073709551616'::int16 - 1;
+SELECT 1 - '18446744073709551616'::int16;
+SELECT '18446744073709551616'::int16 * 2;
+SELECT 2 * '18446744073709551616'::int16;
+SELECT '18446744073709551616'::int16 / 2;
+
+-- int16 op int2
+SELECT '18446744073709551616'::int16 + 1::int2;
+SELECT 1::int2 + '18446744073709551616'::int16;
+SELECT '18446744073709551616'::int16 - 1::int2;
+SELECT '18446744073709551616'::int16 * 2::int2;
+SELECT '18446744073709551616'::int16 / 2::int2;
+
+-- =========================================================================
+-- Aggregates with values exceeding int64 range
+-- =========================================================================
+CREATE TABLE int16_overflow_test (x int16);
+INSERT INTO int16_overflow_test VALUES ('9223372036854775808'), ('9223372036854775808'); -- two 2^63 values
+
+-- sum = 2^64 (exceeds int64, fits in int16)
+SELECT sum(x) FROM int16_overflow_test;
+
+-- avg = 2^63 (returns numeric)
+SELECT avg(x) FROM int16_overflow_test;
+
+-- min/max
+SELECT min(x), max(x) FROM int16_overflow_test;
+
+-- Sum overflow: add MAX then add 2^63
+INSERT INTO int16_overflow_test VALUES ('170141183460469231731687303715884105727');
+SELECT sum(x) FROM int16_overflow_test; -- overflow
+
+-- Clean up the overflow row
+DELETE FROM int16_overflow_test WHERE x = '170141183460469231731687303715884105727'::int16;
+
+-- avg with 3 values all = 2^64
+DELETE FROM int16_overflow_test;
+INSERT INTO int16_overflow_test VALUES ('18446744073709551616'), ('18446744073709551616'), ('18446744073709551616');
+SELECT sum(x) FROM int16_overflow_test; -- 3 * 2^64
+SELECT avg(x) FROM int16_overflow_test; -- 2^64 as numeric
+
+DROP TABLE int16_overflow_test;
+
+-- =========================================================================
+-- int16_avg_state type (internal aggregate state for avg)
+-- =========================================================================
+-- The state type is internal-only (cannot accept direct input), so we
+-- verify it works correctly through the avg() aggregate with large values
+-- that exercise the INT128 sum accumulator.
+CREATE TABLE int16_avg_state_test (x int16);
+INSERT INTO int16_avg_state_test VALUES
+    ('9223372036854775808'),   -- 2^63
+    ('9223372036854775808'),   -- 2^63
+    ('9223372036854775808'),   -- 2^63
+    ('9223372036854775808'),   -- 2^63
+    (NULL);                     -- should be skipped
+-- avg = 2^63 (sum = 4 * 2^63 = 2^65, count = 4, avg = 2^63)
+SELECT avg(x) FROM int16_avg_state_test;
+
+-- Larger values: sum exceeds int64 range significantly
+DELETE FROM int16_avg_state_test;
+INSERT INTO int16_avg_state_test VALUES
+    ('18446744073709551616'),   -- 2^64
+    ('18446744073709551616'),   -- 2^64
+    ('18446744073709551616');   -- 2^64
+-- avg = 2^64
+SELECT avg(x) FROM int16_avg_state_test;
+
+DROP TABLE int16_avg_state_test;
diff --git a/contrib/meson.build b/contrib/meson.build
index ebb7f83d8c5..6e9f178fc86 100644
--- a/contrib/meson.build
+++ b/contrib/meson.build
@@ -32,6 +32,7 @@ subdir('fuzzystrmatch')
 subdir('hstore')
 subdir('hstore_plperl')
 subdir('hstore_plpython')
+subdir('int16')
 subdir('intagg')
 subdir('intarray')
 subdir('isn')
-- 
2.51.0

