use std::io::*;
use std::str::FromStr;
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::HashSet;
use std::cmp;
use std::f64::consts;
use std::cmp::Ordering;
use std::collections::VecDeque; //push_back, pop_front

const MOD: i64 = 1000000007;

fn main() {
    let a: i32 = read();
    let b: i32 = read();
    let k: i32 = read();

    let big = cmp::max(a, b);
    let mut vec: Vec<i32> = Vec::new();

    for i in 1..big+1 {
        if a % i == 0 && b % i == 0 {
            vec.push(i);
        }
    }

    if vec.len() == 0 {
        println!("1");
    } else {
        println!("{}", vec[vec.len()-k as usize]);
    }
}

//(フェルマーの定理)
// (ab / a / b) % MOD を計算したいので、
// (ab * calc(a, MOD-2, MOD) % MOD * calc(b, MOD-2, MOD) % MOD を計算する。
fn calc(a: i64, b: i64, p: i64) -> i64 {
    if b == 0 {
        return 1;
    }
    if b % 2 == 0 {
        let d = calc(a, b/2, p);
        return (d * d) % p;
    } else {
        return (a * calc(a, b-1, p)) % p;
    }
}

// 素因数分解
fn prime_factorization(number: u64) -> Vec<u64> {
    let mut result = number;
    for i in 2..((number as f64).sqrt() as u64)+1 {
        if number % i == 0 {
            result = i;
            break;
        }
    }

    if result == number {
        return vec![number];
    } else {
        let mut v1 = vec![result];
        let mut v2 = prime_factorization(number / result);

        v1.append(&mut v2);

        return v1;
    }
}

fn calc_distance(xa: f64, ya: f64, xb: f64, yb: f64)->f64 {
    (f64::powf(xb-xa, 2.0) + f64::powf(yb-ya, 2.0)).sqrt()
}

fn calc_triangle_area(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> f64 {
    1.0/2.0 * ((x1-x3)*(y2-y3)-(x2-x3)*(y1-y3)).abs()
}

fn read<T: FromStr>() -> T {
    let stdin = stdin();
    let stdin = stdin.lock();
    let token: String = stdin
        .bytes()
        .map(|c| c.expect("failed to read char") as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect();
    token.parse().ok().expect("failed to parse token")
}

// 最大公約数
fn gcd(a: i64, b: i64) -> i64 {
    match b {
        0 => a,
        _ => gcd(b, a % b)
    }
}

// 最小公倍数
fn lcm(a: i64, b: i64) -> i64 {
    let g = gcd(a ,b);
    a / g * b
}

// 階乗-dp (overflow回避で1000000007の余りを使って計算)
// 普通の階乗はmに1を指定
fn kaijou_dp(n: i64) -> i64 {
    let mut dp: Vec<i64> = vec![0; n as usize+1];
    dp[0] = 1;
    dp[1] = 1;

    for i in 1..n {
        dp[i as usize+1] = dp[i as usize] % MOD * (i+1)  % MOD;
    }
    return dp[n as usize];
}

// 階乗
fn kaijou(n: i64)->i64 {
    if n == 1 {
        return n;
    }
    return n * kaijou(n-1);
}

// 順列全列挙
pub trait LexicalPermutation {
    /// Return `true` if the slice was permuted, `false` if it is already
    /// at the last ordered permutation.
    fn next_permutation(&mut self) -> bool;
}

impl<T> LexicalPermutation for [T] where T: Ord {
    /// Original author in Rust: Thomas Backman <serenity@exscape.org>
    /// let mut data = [1, 2, 3];
    //    let mut permutations = Vec::new();
    //
    //    loop {
    //        permutations.push(data.to_vec());
    //        if !data.next_permutation() {
    //            break;
    //        }
    //    }
    //
    //    println!("{:?}", permutations);
    //    // [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
    fn next_permutation(&mut self) -> bool {
        // These cases only have 1 permutation each, so we can't do anything.
        if self.len() < 2 { return false; }

        // Step 1: Identify the longest, rightmost weakly decreasing part of the vector
        let mut i = self.len() - 1;
        while i > 0 && self[i-1] >= self[i] {
            i -= 1;
        }

        // If that is the entire vector, this is the last-ordered permutation.
        if i == 0 {
            return false;
        }

        // Step 2: Find the rightmost element larger than the pivot (i-1)
        let mut j = self.len() - 1;
        while j >= i && self[j] <= self[i-1]  {
            j -= 1;
        }

        // Step 3: Swap that element with the pivot
        self.swap(j, i-1);

        // Step 4: Reverse the (previously) weakly decreasing part
        self[i..].reverse();

        true
    }
}

//iより小さい数字の数 = lower_bound(&a, i) as i64;
//iより大きい数字の数 = n - upper_bound(&c, i) as i64;
fn lower_bound<T: Ord>(vec: &Vec<T>, x: T) -> usize {
    let (mut left, mut right): (i64, i64) = (-1, vec.len() as i64);

    while (right - left) > 1 {
        let mid = (right + left) / 2;

        if x <= vec[mid as usize] {
            right = mid;
        } else {
            left = mid;
        }
    }

    return right as usize;
}

fn upper_bound<T: Ord>(vec: &Vec<T>, x: T) -> usize {
    let (mut left, mut right): (i64, i64) = (-1, vec.len() as i64);

    while (right - left) > 1 {
        let mid = (right + left) / 2;

        if x < vec[mid as usize] {
            right = mid;
        } else {
            left = mid;
        }
    }

    return right as usize;
}



Measured memory_bytes: 4352