diff --git a/DESCRIPTION b/DESCRIPTION
index 6bb50e288a1796e6ad5dfac3275feb5ca81a37d6..0e61513f81d46ef48d8b45876e8e103566d3241f 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -43,9 +43,10 @@ Collate:
     'lifeTable.R'
     'makeQxDataFrame.R'
     'mortalityComparisonTable.R'
+    'periodDeathProbabilities.R'
+    'mortalityTable.jointLives.R'
     'mortalityTables.list.R'
     'mortalityTables.load.R'
-    'periodDeathProbabilities.R'
     'plot.mortalityTable.R'
     'plotMortalityTableComparisons.R'
     'plotMortalityTables.R'
diff --git a/NAMESPACE b/NAMESPACE
index 47e542a6220db5d1b66c4a3f6dbeb8bde48666c9..821a3ff52efe1af200a9df302a75425b397b3f5c 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,12 +1,14 @@
 # Generated by roxygen2: do not edit by hand
 
 S3method(plot,mortalityTable)
+export(deathProbabilitiesIndividual)
 export(makeQxDataFrame)
 export(mortalityComparisonTable)
 export(mortalityTable)
 export(mortalityTable.ageShift)
 export(mortalityTable.improvementFactors)
 export(mortalityTable.joined)
+export(mortalityTable.jointLives)
 export(mortalityTable.mixed)
 export(mortalityTable.observed)
 export(mortalityTable.period)
@@ -19,6 +21,7 @@ exportClasses(mortalityTable)
 exportClasses(mortalityTable.ageShift)
 exportClasses(mortalityTable.improvementFactors)
 exportClasses(mortalityTable.joined)
+exportClasses(mortalityTable.jointLives)
 exportClasses(mortalityTable.mixed)
 exportClasses(mortalityTable.observed)
 exportClasses(mortalityTable.period)
diff --git a/R/baseTable.R b/R/baseTable.R
index 8197691264932e6061523bf5c70cc3f0ecdfb906..a40fa380367f500a85596e3cc4de760760bf2e2c 100644
--- a/R/baseTable.R
+++ b/R/baseTable.R
@@ -15,12 +15,12 @@ setGeneric("baseTable", function(object, ...) standardGeneric("baseTable"));
 
 #' @describeIn baseTable Return the base table of the life table
 setMethod("baseTable", "mortalityTable",
-          function (object,  ...) {
+          function(object,  ...) {
               c()
           })
 
 #' @describeIn baseTable Return the base table of the life table
 setMethod("baseTable", "mortalityTable.period",
-          function (object,  ...) {
+          function(object,  ...) {
               object@deathProbs
           })
diff --git a/R/getCohortTable.R b/R/getCohortTable.R
index cb226cbed17521275bc56022c002100814794c11..bea275d3ed11bc9ce0327cb5ab37c48470104b7c 100644
--- a/R/getCohortTable.R
+++ b/R/getCohortTable.R
@@ -13,11 +13,11 @@ setGeneric("getCohortTable", function(object, YOB, ...) standardGeneric("getCoho
 #' @describeIn getCohortTable Return the cohort life table as a
 #'             \code{mortalityTable.period} object
 setMethod("getCohortTable","mortalityTable",
-          function (object, YOB, ...) {
+          function(object, YOB, ...) {
               mortalityTable.period(
                   name = paste(object@name, ", YOB ", YOB),
                   baseYear = YOB,
                   ages = ages(object),
-                  deathProbs = deathProbabilities(object, YOB = YOB)
+                  deathProbs = deathProbabilities(object, YOB = YOB, ...)
               );
           })
diff --git a/R/getPeriodTable.R b/R/getPeriodTable.R
index 184aabd6ec27f6a9fd16114bf3c0e98183783d6a..6a03f73937816ae4d9ce3ac2f95abffba6c67672 100644
--- a/R/getPeriodTable.R
+++ b/R/getPeriodTable.R
@@ -22,6 +22,6 @@ setMethod("getPeriodTable","mortalityTable",
                   name = paste(object@name, ", Period ", Period),
                   baseYear = Period,
                   ages = ages(object),
-                  deathProbs = periodDeathProbabilities(object, Period = Period)
+                  deathProbs = periodDeathProbabilities(object, Period = Period, ...)
               )
           })
diff --git a/R/mortalityTable.R b/R/mortalityTable.R
index f0286eb1388652f1fed6a7df1cfab60dc7083ef0..44838cdb140ff2315b8a83fc8b663b4826f78155 100644
--- a/R/mortalityTable.R
+++ b/R/mortalityTable.R
@@ -32,7 +32,7 @@ mortalityTable = setClass(
     ),
     prototype = list(
         name = "Actuarial Mortality Table",
-        baseYear = 2000,
+        baseYear = 0,
         loading = 0,
         modification = identity
     ),
diff --git a/R/mortalityTable.jointLives.R b/R/mortalityTable.jointLives.R
new file mode 100644
index 0000000000000000000000000000000000000000..4808b1e06ac5b7efc61c1956437afb8a7fc6bc73
--- /dev/null
+++ b/R/mortalityTable.jointLives.R
@@ -0,0 +1,163 @@
+#' @include mortalityTable.R periodDeathProbabilities.R
+NULL
+
+#' Class mortalityTable.jointLives - Life table for multiple joint lives
+#'
+#' A cohort life table obtained by calculating joint death probabilities for
+#' multiple lives, each possibly using a different mortality table.
+#'
+#' @slot table The \code{mortalityTable} object for all lives (vector if different tables should be used for the different persons)
+#'
+#' @export mortalityTable.jointLives
+#' @exportClass mortalityTable.jointLives
+mortalityTable.jointLives = setClass(
+    "mortalityTable.jointLives",
+    slots = list(
+        table = "mortalityTable"
+    ),
+    contains = "mortalityTable"
+)
+
+
+pad0 = function(v, l, value=0) {
+    if (l >= length(v)) {
+        c(v, rep(value, l - length(v)))
+    } else {
+        v[0:l]
+    }
+}
+padLast = function(v, l) {
+    pad0(v, l, tail(v, n = 1))
+}
+
+#' @export
+deathProbabilitiesIndividual = function(tables, YOB, ageDifferences) {
+    n = max(length(YOB), length(ageDifferences) + 1);
+    if (length(YOB) == 1) {
+        YOB = c(YOB, YOB + ageDifferences);
+    }
+    if (length(ageDifferences) < length(YOB) - 1) {
+        ageDifferences = diff(YOB);
+    }
+    # prepend a 0, because the first entry has no offset
+    ageDifferences = c(0, ageDifferences);
+    tables = padLast(tables, n);
+
+    # Find the required length to have all (shifted) death probabilities fit
+    # last value will be repeated for shorter tables
+    qxlen = max(mapply(
+        function(table, yob, difference) {
+            getOmega(table) - difference
+        },
+        tables, YOB, ageDifferences)) + 1;
+    qxMatrix = mapply(
+        function(table, yob, difference) {
+            qx = deathProbabilities(table, yob);
+            if (difference <= 0) {
+                # Person is younger, so we need to pad with qx=0 for x<=difference, i.e. pad with difference zeroes
+                # This code also works with difference==0!
+                qxtmp = c(
+                    rep(0, -difference),
+                    qx);
+            } else {
+                qxtmp = tail(qx, -difference);
+            }
+            qxnew = padLast(qxtmp, qxlen)
+            str(qxnew);
+            qxnew
+        },
+        tables, YOB, ageDifferences);
+    qxMatrix
+}
+
+periodDeathProbabilitiesIndividual = function(tables, period, ageDifferences) {
+    # prepend a 0, because the first entry has no offset
+    ageDifferences = c(0, ageDifferences);
+    tables = padLast(tables, length(ageDifferences));
+
+    # Find the required length to have all (shifted) death probabilities fit
+    # last value will be repeated for shorter tables
+    qxlen = max(mapply(
+        function(table, difference) {
+            getOmega(table) - difference
+        },
+        tables, ageDifferences)) + 1;
+    qxMatrix = mapply(
+        function(table, difference) {
+            qx = periodDeathProbabilities(table, Period = period);
+            if (difference <= 0) {
+                # Person is younger, so we need to pad with qx=0 for x<=difference, i.e. pad with difference zeroes
+                # This code also works with difference==0!
+                qxtmp = c(
+                    rep(0, -difference),
+                    qx);
+            } else {
+                qxtmp = tail(qx, -difference);
+            }
+            qxnew = padLast(qxtmp, qxlen)
+            qxnew
+        },
+        tables, ageDifferences);
+    qxMatrix
+}
+
+#' @describeIn ages Return the defined ages of the joint lives mortality table (returns the ages of the first table used for joint lives)
+setMethod("ages", "mortalityTable.jointLives",
+          function(object, ...) {
+              ages(c(object@table)[1], ...);
+          })
+
+#' @describeIn baseTable Return the base table of the joint lives mortality table (returns the base table of the first table used for joint lives)
+setMethod("baseTable", "mortalityTable.jointLives",
+          function(object,  ...) {
+              baseTable(c(object@table)[1], ...)
+          })
+
+#' @describeIn baseYear Return the base year of the life table
+setMethod("baseYear", "mortalityTable.jointLives",
+          function(object,  ...) {
+              baseYear(c(object@table)[1], ...)
+          })
+
+#' @describeIn deathProbabilities Return the (cohort) death probabilities of the
+#'                                life table given the birth year (if needed)
+setMethod("deathProbabilities", "mortalityTable.jointLives",
+          function(object,  ..., ageDifferences = c(), YOB = 1975) {
+              qxMatrix = deathProbabilitiesIndividual(c(object@table), YOB = YOB, ageDifferences = ageDifferences);
+              # First death probabilities are characterized as p_x1x2x3.. = \prod p_xi, i.e.
+              # q_x1x2x3... = 1 - \prod (1 - p_xi)
+              qx = 1 - apply(1 - qxMatrix, 1, prod)
+              object@modification(qx * (1 + object@loading));
+          })
+
+#' @describeIn getOmega Return the maximum age of the joint lives mortality table (returns the maximum age of the first table used for joint lives, as the ages of the joint lives are now known to the function)
+setMethod("getOmega", "mortalityTable.observed",
+          function(object) {
+              max(object@ages, na.rm = TRUE);
+          })
+
+
+#' @describeIn periodDeathProbabilities Return the (period) death probabilities
+#'             of the joint lives mortality table for a given observation year
+setMethod("periodDeathProbabilities", "mortalityTable.jointLives",
+          function(object,  ..., ageDifferences = c(), Period = 1975) {
+              qxMatrix = periodDeathProbabilitiesIndividual(c(object@table), period = Period, ageDifferences = ageDifferences);
+              # First death probabilities are characterized as p_x1x2x3.. = \prod p_xi, i.e.
+              # q_x1x2x3... = 1 - \prod (1 - p_xi)
+              qx = 1 - apply(1 - qxMatrix, 1, prod)
+              object@modification(qx * (1 + object@loading));
+          })
+
+
+# Examples
+if (FALSE) {
+    mortalityTables.load("Germany_Census")
+    table.JL = mortalityTable.jointLives(
+        name = "ADSt 24/26 auf verbundene Leben",
+        table = mort.DE.census.1924.26.male
+    )
+    deathProbabilities(table.JL, YOB = 1977, ageDifferences = c(1, 5, -5, 16))
+    deathProbabilities(table.JL, ageDifferences = c(0))
+    deathProbabilities(table.JL, ageDifferences = c(1, 5, 16))
+
+}
diff --git a/data-raw/Austria_Endowments_ADSt2426_2Lives.xlsx b/data-raw/Austria_Endowments_ADSt2426_2Lives.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..9a9782401816364c7c2054bb30fb6acb9de06dd5
Binary files /dev/null and b/data-raw/Austria_Endowments_ADSt2426_2Lives.xlsx differ
diff --git a/man/ages.Rd b/man/ages.Rd
index 9c147c61f5050bb1fa1d53f7444e963c2209a7ff..6e16e6918a08d05fab4b5207329ca713e33217b5 100644
--- a/man/ages.Rd
+++ b/man/ages.Rd
@@ -1,9 +1,10 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/ages.R
+% Please edit documentation in R/ages.R, R/mortalityTable.jointLives.R
 \docType{methods}
 \name{ages}
 \alias{ages}
 \alias{ages,mortalityTable.joined-method}
+\alias{ages,mortalityTable.jointLives-method}
 \alias{ages,mortalityTable.mixed-method}
 \alias{ages,mortalityTable.observed-method}
 \alias{ages,mortalityTable.period-method}
@@ -18,6 +19,8 @@ ages(object, ...)
 \S4method{ages}{mortalityTable.joined}(object, ...)
 
 \S4method{ages}{mortalityTable.observed}(object, ...)
+
+\S4method{ages}{mortalityTable.jointLives}(object, ...)
 }
 \arguments{
 \item{object}{A life table object (instance of a \code{\linkS4class{mortalityTable}} class)}
@@ -36,6 +39,8 @@ Return the defined ages of the life table
 \item \code{mortalityTable.joined}: Return the defined ages of the joined life table
 
 \item \code{mortalityTable.observed}: Return the defined ages of the observed life table
+
+\item \code{mortalityTable.jointLives}: Return the defined ages of the joint lives mortality table (returns the ages of the first table used for joint lives)
 }}
 \examples{
 mortalityTables.load("Austria_*", wildcard=TRUE)
diff --git a/man/baseTable.Rd b/man/baseTable.Rd
index 4b7dd345dbfd7a741ceb094f69f7e2dda724cb99..85afef62a01da9941476c1f9ce6a436480848341 100644
--- a/man/baseTable.Rd
+++ b/man/baseTable.Rd
@@ -1,9 +1,10 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/baseTable.R
+% Please edit documentation in R/baseTable.R, R/mortalityTable.jointLives.R
 \docType{methods}
 \name{baseTable}
 \alias{baseTable}
 \alias{baseTable,mortalityTable-method}
+\alias{baseTable,mortalityTable.jointLives-method}
 \alias{baseTable,mortalityTable.period-method}
 \title{Return the base table of the life table}
 \usage{
@@ -12,6 +13,8 @@ baseTable(object, ...)
 \S4method{baseTable}{mortalityTable}(object, ...)
 
 \S4method{baseTable}{mortalityTable.period}(object, ...)
+
+\S4method{baseTable}{mortalityTable.jointLives}(object, ...)
 }
 \arguments{
 \item{object}{The life table object (class inherited from mortalityTable)}
@@ -26,6 +29,8 @@ Return the base table of the life table
 \item \code{mortalityTable}: Return the base table of the life table
 
 \item \code{mortalityTable.period}: Return the base table of the life table
+
+\item \code{mortalityTable.jointLives}: Return the base table of the joint lives mortality table (returns the base table of the first table used for joint lives)
 }}
 \examples{
 mortalityTables.load("Austria_Annuities")
diff --git a/man/baseYear.Rd b/man/baseYear.Rd
index 281ff4b47970da5952c17a4cc783cd602606bb46..3d4f848a263bec90765a8290a132c1ad7d4337ed 100644
--- a/man/baseYear.Rd
+++ b/man/baseYear.Rd
@@ -1,9 +1,10 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/baseYear.R
+% Please edit documentation in R/baseYear.R, R/mortalityTable.jointLives.R
 \docType{methods}
 \name{baseYear}
 \alias{baseYear}
 \alias{baseYear,mortalityTable-method}
+\alias{baseYear,mortalityTable.jointLives-method}
 \alias{baseYear,mortalityTable.mixed-method}
 \title{Return the base year of the life table}
 \usage{
@@ -12,6 +13,8 @@ baseYear(object, ...)
 \S4method{baseYear}{mortalityTable}(object, ...)
 
 \S4method{baseYear}{mortalityTable.mixed}(object, ...)
+
+\S4method{baseYear}{mortalityTable.jointLives}(object, ...)
 }
 \arguments{
 \item{object}{The life table object (class inherited from mortalityTable)}
@@ -26,6 +29,8 @@ Return the base year of the life table
 \item \code{mortalityTable}: Return the base year of the life table
 
 \item \code{mortalityTable.mixed}: Return the base year of the life table
+
+\item \code{mortalityTable.jointLives}: Return the base year of the life table
 }}
 \examples{
 mortalityTables.load("Austria_Annuities")
diff --git a/man/deathProbabilities.Rd b/man/deathProbabilities.Rd
index 3bbcb561b5305840d7f7c0a7f4e9f88c07939b7d..2763443e70d6db1c24ae4b4c7c3c089dac6f7ef6 100644
--- a/man/deathProbabilities.Rd
+++ b/man/deathProbabilities.Rd
@@ -1,10 +1,11 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/deathProbabilities.R
+% Please edit documentation in R/deathProbabilities.R, R/mortalityTable.jointLives.R
 \docType{methods}
 \name{deathProbabilities}
 \alias{deathProbabilities}
 \alias{deathProbabilities,mortalityTable.ageShift-method}
 \alias{deathProbabilities,mortalityTable.improvementFactors-method}
+\alias{deathProbabilities,mortalityTable.jointLives-method}
 \alias{deathProbabilities,mortalityTable.mixed-method}
 \alias{deathProbabilities,mortalityTable.period-method}
 \alias{deathProbabilities,mortalityTable.trendProjection-method}
@@ -24,6 +25,9 @@ deathProbabilities(object, ..., YOB = 1975)
   YOB = 1975)
 
 \S4method{deathProbabilities}{mortalityTable.mixed}(object, ..., YOB = 1975)
+
+\S4method{deathProbabilities}{mortalityTable.jointLives}(object, ...,
+  ageDifferences = c(), YOB = 1975)
 }
 \arguments{
 \item{object}{The life table object (class inherited from mortalityTable)}
@@ -51,5 +55,8 @@ life table given the birth year (if needed)
 
 \item \code{mortalityTable.mixed}: Return the (cohort) death probabilities of the
 life table given the birth year (if needed)
+
+\item \code{mortalityTable.jointLives}: Return the (cohort) death probabilities of the
+life table given the birth year (if needed)
 }}
 
diff --git a/man/getOmega.Rd b/man/getOmega.Rd
index 7ce28d966ef1266dac0c814acd9df32e89239216..feed400213f527636e55ef6e92cc427869a1ae34 100644
--- a/man/getOmega.Rd
+++ b/man/getOmega.Rd
@@ -1,5 +1,5 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/getOmega.R
+% Please edit documentation in R/getOmega.R, R/mortalityTable.jointLives.R
 \docType{methods}
 \name{getOmega}
 \alias{getOmega}
@@ -17,6 +17,8 @@ getOmega(object)
 
 \S4method{getOmega}{mortalityTable.joined}(object)
 
+\S4method{getOmega}{mortalityTable.observed}(object)
+
 \S4method{getOmega}{mortalityTable.observed}(object)
 }
 \arguments{
@@ -34,5 +36,7 @@ Return the maximum age of the life table
 \item \code{mortalityTable.joined}: Return the maximum age of the joined life table
 
 \item \code{mortalityTable.observed}: Return the maximum age of the joined life table
+
+\item \code{mortalityTable.observed}: Return the maximum age of the joint lives mortality table (returns the maximum age of the first table used for joint lives, as the ages of the joint lives are now known to the function)
 }}
 
diff --git a/man/mortalityTable.jointLives-class.Rd b/man/mortalityTable.jointLives-class.Rd
new file mode 100644
index 0000000000000000000000000000000000000000..13c361049ba7a654e8624cc71b2899ca2f6e7bdd
--- /dev/null
+++ b/man/mortalityTable.jointLives-class.Rd
@@ -0,0 +1,17 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/mortalityTable.jointLives.R
+\docType{class}
+\name{mortalityTable.jointLives-class}
+\alias{mortalityTable.jointLives}
+\alias{mortalityTable.jointLives-class}
+\title{Class mortalityTable.jointLives - Life table for multiple joint lives}
+\description{
+A cohort life table obtained by calculating joint death probabilities for
+multiple lives, each possibly using a different mortality table.
+}
+\section{Slots}{
+
+\describe{
+\item{\code{table}}{The \code{mortalityTable} object for all lives (vector if different tables should be used for the different persons)}
+}}
+
diff --git a/man/periodDeathProbabilities.Rd b/man/periodDeathProbabilities.Rd
index 7d85c3eb7686d5a8fb3b239a9ff23b256c3cc563..056d3355f3651d707df4940f31032b35ad306bab 100644
--- a/man/periodDeathProbabilities.Rd
+++ b/man/periodDeathProbabilities.Rd
@@ -1,10 +1,11 @@
 % Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/periodDeathProbabilities.R
+% Please edit documentation in R/periodDeathProbabilities.R, R/mortalityTable.jointLives.R
 \docType{methods}
 \name{periodDeathProbabilities}
 \alias{periodDeathProbabilities}
 \alias{periodDeathProbabilities,mortalityTable.ageShift-method}
 \alias{periodDeathProbabilities,mortalityTable.improvementFactors-method}
+\alias{periodDeathProbabilities,mortalityTable.jointLives-method}
 \alias{periodDeathProbabilities,mortalityTable.mixed-method}
 \alias{periodDeathProbabilities,mortalityTable.period-method}
 \alias{periodDeathProbabilities,mortalityTable.trendProjection-method}
@@ -27,6 +28,9 @@ periodDeathProbabilities(object, ..., Period = 1975)
 
 \S4method{periodDeathProbabilities}{mortalityTable.mixed}(object, ...,
   Period = 1975)
+
+\S4method{periodDeathProbabilities}{mortalityTable.jointLives}(object, ...,
+  ageDifferences = c(), Period = 1975)
 }
 \arguments{
 \item{object}{The life table object (class inherited from mortalityTable)}
@@ -55,5 +59,8 @@ of the life table for a given observation year
 
 \item \code{mortalityTable.mixed}: Return the (period) death probabilities
 of the life table for a given observation year
+
+\item \code{mortalityTable.jointLives}: Return the (period) death probabilities
+of the joint lives mortality table for a given observation year
 }}