1#![doc = concat!("```ignore\n", include_str!("./Ints.smt2"), "```")]
2
3use smtlib_lowlevel::{
4 ast::{self, Identifier, QualIdentifier, SpecConstant, Term},
5 lexicon::{Numeral, Symbol},
6 Storage,
7};
8
9use crate::{
10 impl_op,
11 sorts::Sort,
12 terms::{app, Const, Dynamic, IntoWithStorage, STerm, Sorted, StaticSorted},
13 Bool,
14};
15
16#[derive(Debug, Clone, Copy)]
20pub struct Int<'st>(STerm<'st>);
21impl<'st> From<Const<'st, Int<'st>>> for Int<'st> {
22 fn from(c: Const<'st, Int<'st>>) -> Self {
23 c.1
24 }
25}
26impl<'st> IntoWithStorage<'st, Int<'st>> for Const<'st, Int<'st>> {
27 fn into_with_storage(self, _st: &'st Storage) -> Int<'st> {
28 self.1
29 }
30}
31impl std::fmt::Display for Int<'_> {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 STerm::from(*self).term().fmt(f)
34 }
35}
36
37impl<'st> From<Int<'st>> for Dynamic<'st> {
38 fn from(i: Int<'st>) -> Self {
39 i.into_dynamic()
40 }
41}
42
43impl<'st> From<Int<'st>> for STerm<'st> {
44 fn from(i: Int<'st>) -> Self {
45 i.0
46 }
47}
48impl<'st> From<STerm<'st>> for Int<'st> {
49 fn from(t: STerm<'st>) -> Self {
50 Int(t)
51 }
52}
53impl<'st> From<(STerm<'st>, Sort<'st>)> for Int<'st> {
54 fn from((t, _): (STerm<'st>, Sort<'st>)) -> Self {
55 t.into()
56 }
57}
58impl<'st> StaticSorted<'st> for Int<'st> {
59 type Inner = Self;
60 const AST_SORT: ast::Sort<'static> = ast::Sort::new_simple("Int");
61
62 fn static_st(&self) -> &'st Storage {
63 self.0.st()
64 }
65}
66impl<'st> IntoWithStorage<'st, Int<'st>> for i128 {
67 fn into_with_storage(self, st: &'st Storage) -> Int<'st> {
68 let term = if self < 0 {
69 app(
70 st,
71 "-",
72 Term::SpecConstant(SpecConstant::Numeral(Numeral::from_u128(
73 self.unsigned_abs() as _,
74 ))),
75 )
76 } else {
77 STerm::new(
78 st,
79 Term::SpecConstant(SpecConstant::Numeral(Numeral::from_u128(self as _))),
80 )
81 };
82 term.into()
83 }
84}
85#[derive(Debug, thiserror::Error, PartialEq, Eq, PartialOrd, Ord)]
87pub enum TryFromError {
88 #[error("numeral was a string. this happens when the numeral is too large to fit into u128")]
89 NumeralWasString,
91 #[error("out of range {}", if *neg { format!("-{abs}") } else { abs.to_string() })]
92 OutOfRange {
94 neg: bool,
96 abs: u128,
98 },
99 #[error("numeral was not a spec constant or a negation of a numeral")]
100 NotANumeral,
102}
103impl<'st> TryFrom<Int<'st>> for i128 {
104 type Error = TryFromError;
105 fn try_from(i: Int<'st>) -> Result<Self, Self::Error> {
106 match i.term() {
107 Term::SpecConstant(SpecConstant::Numeral(c)) => {
108 let n = c.into_u128().map_err(|_| TryFromError::NumeralWasString)?;
109 n.try_into()
110 .map_err(|_| TryFromError::OutOfRange { abs: n, neg: false })
111 }
112 Term::Application(
113 QualIdentifier::Identifier(Identifier::Simple(Symbol("-"))),
114 [Term::SpecConstant(SpecConstant::Numeral(c))],
115 ) => {
116 let n = c.into_u128().map_err(|_| TryFromError::NumeralWasString)?;
117 if n == i128::MIN as u128 {
118 return Ok(i128::MIN);
121 }
122 n.try_into()
123 .map_err(|_| TryFromError::OutOfRange { abs: n, neg: true })
124 .map(|n: i128| -n)
125 }
126 _ => Err(TryFromError::NotANumeral),
127 }
128 }
129}
130impl<'st> IntoWithStorage<'st, Int<'st>> for u128 {
131 fn into_with_storage(self, st: &'st Storage) -> Int<'st> {
132 STerm::new(
133 st,
134 Term::SpecConstant(SpecConstant::Numeral(Numeral::from_u128(self as _))),
135 )
136 .into()
137 }
138}
139impl<'st> TryFrom<Int<'st>> for u128 {
140 type Error = TryFromError;
141 fn try_from(i: Int<'st>) -> Result<Self, Self::Error> {
142 match i.term() {
143 Term::SpecConstant(SpecConstant::Numeral(c)) => {
144 c.into_u128().map_err(|_| TryFromError::NumeralWasString)
145 }
146 _ => Err(TryFromError::NotANumeral),
147 }
148 }
149}
150macro_rules! impl_int_conv {
151 (from $s:ty, into $t:ty) => {
152 impl<'st> IntoWithStorage<'st, Int<'st>> for $t {
153 fn into_with_storage(self, st: &'st Storage) -> Int<'st> {
154 IntoWithStorage::into_with_storage(self as $s, st)
155 }
156 }
157 impl<'st> TryFrom<Int<'st>> for $t {
158 type Error = TryFromError;
159 fn try_from(i: Int<'st>) -> Result<Self, Self::Error> {
160 let n = <$s>::try_from(i)?;
161 n.try_into().map_err(|_| TryFromError::OutOfRange {
162 abs: n as u128,
163 #[allow(unused_comparisons)]
164 neg: n < 0,
165 })
166 }
167 }
168 };
169}
170impl_int_conv!(from i128, into i8);
171impl_int_conv!(from i128, into i16);
172impl_int_conv!(from i128, into i32);
173impl_int_conv!(from i128, into i64);
174impl_int_conv!(from i128, into isize);
175impl_int_conv!(from u128, into u8);
176impl_int_conv!(from u128, into u16);
177impl_int_conv!(from u128, into u32);
178impl_int_conv!(from u128, into u64);
179impl_int_conv!(from u128, into usize);
180
181impl<'st> Int<'st> {
182 pub fn sort() -> Sort<'st> {
184 Self::AST_SORT.into()
185 }
186 pub fn new(st: &'st Storage, value: impl IntoWithStorage<'st, Int<'st>>) -> Int<'st> {
188 value.into_with_storage(st)
189 }
190 fn binop<T: From<STerm<'st>>>(self, op: &'st str, other: Int<'st>) -> T {
191 app(self.static_st(), op, (self.term(), other.term())).into()
192 }
193 pub fn gt(self, other: impl IntoWithStorage<'st, Self>) -> Bool<'st> {
195 self.binop(">", other.into_with_storage(self.st()))
196 }
197 pub fn ge(self, other: impl IntoWithStorage<'st, Self>) -> Bool<'st> {
199 self.binop(">=", other.into_with_storage(self.st()))
200 }
201 pub fn lt(self, other: impl IntoWithStorage<'st, Self>) -> Bool<'st> {
203 self.binop("<", other.into_with_storage(self.st()))
204 }
205 pub fn le(self, other: impl IntoWithStorage<'st, Self>) -> Bool<'st> {
207 self.binop("<=", other.into_with_storage(self.st()))
208 }
209 pub fn abs(self) -> Int<'st> {
212 app(self.st(), "abs", self.term()).into()
213 }
214}
215
216impl std::ops::Neg for Int<'_> {
217 type Output = Self;
218 fn neg(self) -> Self::Output {
219 app(self.st(), "-", self.term()).into()
220 }
221}
222
223impl_op!(Int<'st>, i64, Add, add, "+", AddAssign, add_assign, +);
224impl_op!(Int<'st>, i64, Sub, sub, "-", SubAssign, sub_assign, -);
225impl_op!(Int<'st>, i64, Mul, mul, "*", MulAssign, mul_assign, *);
226impl_op!(Int<'st>, i64, Div, div, "div", DivAssign, div_assign, /);
227impl_op!(Int<'st>, i64, Rem, rem, "rem", RemAssign, rem_assign, %);
228impl_op!(Int<'st>, i64, Shl, shl, "hsl", ShlAssign, shl_assign, <<);
229impl_op!(Int<'st>, i64, Shr, shr, "hsr", ShrAssign, shr_assign, >>);