//! This module defines traits describing general properties of abstract domains
//! as well as several abstract domain types implementing these traits.

use crate::bil::*;

mod bitvector;
pub use bitvector::*;

mod identifier;
pub use identifier::*;

mod pointer;
pub use pointer::*;

mod data;
pub use data::*;

mod mem_region;
pub use mem_region::*;

/// The main trait describing an abstract domain.
///
/// Each abstract domain is partially ordered and has a maximal element (which can be generated by `top()`).
/// Abstract domains of the same type can be merged.
pub trait AbstractDomain: Sized + Eq + Clone {
    fn merge(&self, other: &Self) -> Self;

    /// Returns whether the element represents the top element or not.
    fn is_top(&self) -> bool;
}

/// A trait for types representing values with a fixed size (in bits).
///
/// For abstract domains, the bitsize is a parameter of the domain itself,
/// i.e. you cannot merge values of different bitsizes,
/// since they lie in different posets (one for each bitsize).
pub trait HasBitSize {
    /// Return the size of the represented value in bits.
    fn bitsize(&self) -> BitSize;
}

/// An abstract domain implementing this trait has a global maximum, i.e. a *Top* element.
pub trait HasTop {
    /// Return an instance of the *Top* element.
    ///
    /// Since an abstract domain type may represent a whole family of abstract domains,
    /// this function takes an instance of the domain as a parameter,
    /// so it can return the *Top* element of the same family member that the provided instance belongs to.
    fn top(&self) -> Self;
}

/// A trait for abstract domains that can represent values loaded into CPU register.
///
/// The domain implements all general operations used to manipulate register values.
/// The domain is parametrized by its bitsize (which represents the size of the register).
/// It has a *Top* element, which is only characterized by its bitsize.
pub trait RegisterDomain: AbstractDomain + HasBitSize + HasTop {
    /// Return a new top element with the given bitsize
    fn new_top(bitsize: BitSize) -> Self;

    /// Compute the (abstract) result of a binary operation
    fn bin_op(&self, op: BinOpType, rhs: &Self) -> Self;

    /// Compute the (abstract) result of a unary operation
    fn un_op(&self, op: UnOpType) -> Self;

    /// extract a sub-bitvector
    fn extract(&self, low_bit: BitSize, high_bit: BitSize) -> Self;

    /// Extend a bitvector using the given cast type
    fn cast(&self, kind: CastType, width: BitSize) -> Self;

    /// Concatenate two bitvectors
    fn concat(&self, other: &Self) -> Self;
}
