use std::collections::BTreeSet;

const S4_3: [[char; 4]; 4] = [
    ['a', 'a', 'b', 'c'],
    ['d', 'd', 'b', 'c'],
    ['e', 'f', 'g', 'g'],
    ['e', 'f', 'h', 'h'],
];

const S5_3: [[char; 5]; 5] = [
    ['a', 'a', 'b', 'b', 'c'],
    ['d', 'd', 'e', '.', 'c'],
    ['f', 'f', 'e', 'g', 'g'],
    ['.', '.', 'h', 'i', 'j'],
    ['.', '.', 'h', 'i', 'j'],
];

const S6_3: [[char; 6]; 6] = [
    ['a', 'a', 'b', 'b', 'c', 'c'],
    ['d', 'd', 'e', 'e', 'f', 'f'],
    ['g', 'h', 'i', '.', '.', '.'],
    ['g', 'h', 'i', '.', '.', '.'],
    ['.', '.', '.', 'j', 'k', 'l'],
    ['.', '.', '.', 'j', 'k', 'l'],
];

fn main() {
    //    for n in 7.. {
    //        println!("n={}", n);
    //        let mut map = vec![vec!['.'; n]; n];
    //        naive(&mut map, 0, 0);
    //    }

    let (r, w) = (std::io::stdin(), std::io::stdout());
    let mut sc = IO::new(r.lock(), w.lock());

    let n: usize = sc.read();
    if n == 2 {
        println!("-1");
        return;
    }

    let ans = solve(n);

    for i in 0..n {
        for j in 0..n {
            sc.write(format!("{}", ans[i][j]));
        }
        sc.write("\n");
    }
}

fn naive7() {
    let ans = [['.'; 7]; 7];
}

fn solve(n: usize) -> Vec<Vec<char>> {
    let mut ans = vec![vec!['.'; n]; n];
    match n % 4 {
        0 => {
            let s = n / 4;
            fill_4(&mut ans, 0, s);
        }
        1 => {
            assert!(n >= 5);
            let s = (n - 5) / 4;
            fill_4(&mut ans, 5, s);
            for i in 0..5 {
                for j in 0..5 {
                    ans[i][j] = S5_3[i][j];
                }
            }
        }
        2 => {
            assert!(n >= 6);
            let s = (n - 6) / 4;
            fill_4(&mut ans, 6, s);
            for i in 0..6 {
                for j in 0..6 {
                    ans[i][j] = S6_3[i][j];
                }
            }
        }
        3 => {
            if n == 3 {
                ans = vec![
                    vec!['a', 'a', '.'],
                    vec!['.', '.', 'b'],
                    vec!['.', '.', 'b'],
                ];
            } else if n == 7 {
                ans = vec![
                    "aa..bc.".chars().collect(),
                    "....bcd".chars().collect(),
                    "kll...d".chars().collect(),
                    "k..mm.e".chars().collect(),
                    "..nn.fe".chars().collect(),
                    "jii..f.".chars().collect(),
                    "j..hhgg".chars().collect(),
                ];
            } else {
                assert!(n >= 11);
                let s = (n - 11) / 4;
                fill_4(&mut ans, 11, s);
                for i in 0..6 {
                    for j in 0..6 {
                        ans[i][j] = S6_3[i][j];
                    }
                }
                for i in 0..5 {
                    for j in 0..5 {
                        ans[i + 6][j + 6] = S5_3[i][j];
                    }
                }
            }
        }
        _ => unreachable!(),
    }
    ans
}

fn validate(ans: &Vec<Vec<char>>) {
    let n = ans.len();
    for row in 0..n {
        let mut row_set = BTreeSet::new();
        for col in 0..n {
            if ans[row][col] == '.' {
                continue;
            }
            row_set.insert(ans[row][col]);
        }

        assert_eq!(row_set.len(), 3);
    }

    for col in 0..n {
        let mut col_set = BTreeSet::new();
        for row in 0..n {
            if ans[row][col] == '.' {
                continue;
            }
            col_set.insert(ans[row][col]);
        }

        assert_eq!(col_set.len(), 3);
    }
}

fn fill_4(ans: &mut Vec<Vec<char>>, offset: usize, s: usize) {
    for t in 0..s {
        for i in 0..4 {
            for j in 0..4 {
                ans[i + t * 4 + offset][j + t * 4 + offset] = S4_3[i][j];
            }
        }
    }
}

fn naive(map: &mut Vec<Vec<char>>, depth: u8, pos: usize) {
    let n = map.len();
    for pos in pos..(n * n) {
        let i = pos / n;
        let j = pos % n;
        if map[i][j] == '.' {
            let c = (depth + 'a' as u8) as char;
            if j + 1 < n && map[i][j + 1] == '.' {
                map[i][j] = c;
                map[i][j + 1] = c;
                naive(map, depth + 1, pos + 1);
                map[i][j] = '.';
                map[i][j + 1] = '.';
            }
            if i + 1 < n && map[i + 1][j] == '.' {
                map[i][j] = c;
                map[i + 1][j] = c;
                naive(map, depth + 1, pos + 1);
                map[i][j] = '.';
                map[i + 1][j] = '.';
            }
        }
    }
    print_result(&map);
}

fn print_result(map: &Vec<Vec<char>>) {
    let mut set = BTreeSet::new();
    let n = map.len();
    for i in 0..n {
        let mut c_set = BTreeSet::new();
        for j in 0..n {
            if map[i][j] != '.' {
                c_set.insert(map[i][j]);
            }
        }
        set.insert(c_set.len());
        if set.len() > 1 {
            return;
        }
    }
    for j in 0..n {
        let mut c_set = BTreeSet::new();
        for i in 0..n {
            if map[i][j] != '.' {
                c_set.insert(map[i][j]);
            }
        }
        set.insert(c_set.len());
        if set.len() > 1 {
            return;
        }
    }

    if set.len() == 1 {
        let k = set.into_iter().next().unwrap();
        if k > 0 {
            println!("q={}", k);
            for i in 0..n {
                println!("{:?}", map[i]);
            }
            println!();
        }
    }
}

pub struct IO<R, W: std::io::Write>(R, std::io::BufWriter<W>);

impl<R: std::io::Read, W: std::io::Write> IO<R, W> {
    pub fn new(r: R, w: W) -> IO<R, W> {
        IO(r, std::io::BufWriter::new(w))
    }
    pub fn write<S: std::ops::Deref<Target = str>>(&mut self, s: S) {
        use std::io::Write;
        self.1.write(s.as_bytes()).unwrap();
    }
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .0
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
            .take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}


Measured memory_bytes: 9340