use std::io::*;
use std::str::FromStr;
use std::collections::HashMap;

#[allow(dead_code)]
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")
}

type Weight = i32;
#[allow(dead_code)]
#[derive(Clone, Debug, Eq, PartialEq)]
struct Edge {
    index: usize,
    src: usize,
    dest: usize,
    weight: Weight,
}
#[allow(dead_code)]
impl Edge {
    fn new(index: usize, src: usize, dest: usize, weight: Weight) -> Edge {
        Edge {
            index: index,
            src: src,
            dest: dest,
            weight: weight,
        }
    }
}
impl std::cmp::Ord for Edge {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        (self.weight, self.src, self.dest).cmp(&(other.weight, other.src, other.dest))
    }
}
impl std::cmp::PartialOrd for Edge {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}
type Edges = Vec<Edge>;
type Graph = Vec<Edges>;
#[allow(dead_code)]
fn graph_new(n: usize) -> Graph {
    vec![vec![]; n]
}
#[allow(dead_code)]
fn add_bi_edge(graph: &mut Graph, index: &mut usize, src: usize, dest: usize, weight: Weight) {
    graph[src].push(Edge::new(*index, src, dest, weight));
    graph[dest].push(Edge::new(*index, dest, src, weight));
    *index += 1;
}
#[allow(dead_code)]
fn add_uni_edge(graph: &mut Graph, index: &mut usize, src: usize, dest: usize, weight: Weight) {
    graph[src].push(Edge::new(*index, src, dest, weight));
    *index += 1;
}

fn calc_min_c(y_cnt: usize, x_cnt: usize, ds: &Vec<Vec<i32>>) -> i32 {
    let mut ret = 0;
    for i in 0..ds.len() {
        for j in 0..ds[0].len() {
            let v = ds[i][j] - (i as i32 + 1) * y_cnt as i32 - (j as i32 + 1) * x_cnt as i32;
            ret = std::cmp::max(ret, v);
        }
    }
    // println!("{} {} {}", y_cnt, x_cnt, ret);
    ret
}

fn calc_use_node(y_cnt: usize, x_cnt: usize, c: i32, ds: &Vec<Vec<i32>>, visit: &mut Vec<Vec<bool>>, use_node: &mut Vec<Vec<bool>>) -> usize {
    let mut ret = 0;
    for i in 0..ds.len() {
        for j in 0..ds[0].len() {
            if visit[i][j] { continue; }
            let v = (i as i32 + 1) * y_cnt as i32 + (j as i32 + 1) * x_cnt as i32 + c;
            if v == ds[i][j] {
                ret += 1;
                visit[i][j] = true;
                use_node[y_cnt][x_cnt] = true;
                // println!("{} {} {} {} {} {}", y_cnt, x_cnt, i, j, v, c);
            }
        }
    }
    ret
}

fn add_xy_edge(g: &mut Graph, e: &mut usize, from: usize, y_cnt: usize, x_cnt: usize, node_map: &mut HashMap<(usize, usize), usize>) {
    if y_cnt == 0 && x_cnt == 0 { return; }
    if g[from].len() == 1 { return; }
    let mut ny_cnt = y_cnt;
    let mut nx_cnt = x_cnt;
    let cost;
    if y_cnt != 0 {
        ny_cnt -= 1;
        cost = -1;
    } else {
        nx_cnt -= 1;
        cost = -2;
    }
    let node = (ny_cnt, nx_cnt);
    if !node_map.contains_key(&node) {
        let to = g.len();
        node_map.insert(node, to);
        g.push(vec![]);
    }
    let to = *node_map.get(&node).unwrap();
    add_uni_edge(g, e, from, to, cost);
    add_xy_edge(g, e, to, ny_cnt, nx_cnt, node_map);
}

fn main() {
    let a: usize = read();
    let b: usize = read();
    let mut ds = vec![vec![0 as i32; a]; b];
    for x in 0..a {
        for y in 0..b {
            ds[y][x] = read();
        }
    }
    let mut visit = vec![vec![false; a]; b];
    let mut min_c = vec![vec![0 as i32; 101]; 101];
    let mut use_node = vec![vec![false; 101]; 101];
    let mut total = 0;
    for i in 0..101 {
    // for i in 0..3 {
        for x_cnt in 0..i {
            let y_cnt = i;
            min_c[y_cnt][x_cnt] = calc_min_c(y_cnt, x_cnt, &ds);
            total += calc_use_node(y_cnt, x_cnt, min_c[y_cnt][x_cnt], &ds, &mut visit, &mut use_node);
        }
        for y_cnt in 0..i+1 {
            let x_cnt = i;
            min_c[y_cnt][x_cnt] = calc_min_c(y_cnt, x_cnt, &ds);
            total += calc_use_node(y_cnt, x_cnt, min_c[y_cnt][x_cnt], &ds, &mut visit, &mut use_node);
        }
    }
    // println!("{}", total);
    if total != a * b {
        println!("Impossible");
        return;
    }
    let mut g = graph_new(1);
    let mut node_map = HashMap::<(usize, usize), usize>::new();
    let mut e = 0;
    for y_cnt in 0..101 {
        for x_cnt in 0..101 {
            if !use_node[y_cnt][x_cnt] { continue; }
            let to = g.len();
            g.push(vec![]);
            node_map.insert((y_cnt, x_cnt), to);
            add_uni_edge(&mut g, &mut e, 0, to, min_c[y_cnt][x_cnt]);
            add_xy_edge(&mut g, &mut e, to, y_cnt, x_cnt, &mut node_map);
        }
    }
    assert!(g.len() <= 300);
    println!("Possible");
    println!("{} {}", g.len(), e);
    for edges in g.iter() {
        for edge in edges.iter() {
            print!("{} {} ", edge.src + 1, edge.dest + 1);
            match edge.weight {
                -1 => print!("Y"),
                -2 => print!("X"),
                _ => print!("{}", edge.weight),
            }
            println!("");
        }
    }
    println!("1 2")
}

Measured memory_bytes: 4352